├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── src └── main │ ├── resources │ ├── data │ │ ├── bingbingwahoo │ │ │ ├── tags │ │ │ │ └── blocks │ │ │ │ │ ├── backwards_slopes.json │ │ │ │ │ ├── ground_pound_blacklist.json │ │ │ │ │ ├── ground_pound_whitelist.json │ │ │ │ │ ├── slides.json │ │ │ │ │ └── standard_slopes.json │ │ │ └── recipes │ │ │ │ └── mysterious_cap.json │ │ ├── minecraft │ │ │ └── tags │ │ │ │ └── items │ │ │ │ └── music_discs.json │ │ └── trinkets │ │ │ ├── tags │ │ │ └── items │ │ │ │ └── head │ │ │ │ └── hat.json │ │ │ └── entities │ │ │ └── bingbingwahoo.json │ ├── assets │ │ └── bingbingwahoo │ │ │ ├── icon.png │ │ │ ├── icon_compressed.png │ │ │ ├── models │ │ │ └── item │ │ │ │ ├── music_disc_slider.json │ │ │ │ └── mysterious_cap.json │ │ │ ├── sounds │ │ │ └── music_disc_slider.ogg │ │ │ ├── textures │ │ │ ├── item │ │ │ │ ├── mysterious_cap.png │ │ │ │ ├── music_disc_slider.png │ │ │ │ └── mysterious_cap_overlay.png │ │ │ └── armor │ │ │ │ ├── mysterious_cap.png │ │ │ │ └── mysterious_cap_emblem.png │ │ │ ├── sounds.json │ │ │ └── lang │ │ │ └── en_us.json │ ├── bingbingwahoo.mixins.json │ └── fabric.mod.json │ └── java │ └── io │ └── github │ └── ignoramuses │ └── bing_bing_wahoo │ ├── content │ ├── movement │ │ ├── FlipState.java │ │ ├── GroundPoundType.java │ │ ├── JumpType.java │ │ └── Slopes.java │ ├── cap │ │ ├── CapWearer.java │ │ ├── CapFlyingSoundInstance.java │ │ ├── MysteriousCapArmorMaterial.java │ │ ├── FlyingCapRenderer.java │ │ ├── MysteriousCapFeatureRenderer.java │ │ ├── PreferredCapSlot.java │ │ ├── MysteriousCapItem.java │ │ ├── MysteriousCapModel.java │ │ └── FlyingCapEntity.java │ └── SliderRecordItem.java │ ├── extensions │ ├── PlayerExtensions.java │ ├── KeyboardInputExtensions.java │ ├── AbstractClientPlayerExtensions.java │ ├── LocalPlayerExtensions.java │ └── ServerPlayerExtensions.java │ ├── mixin │ ├── EntityAccessor.java │ ├── EntitySelectorOptionsAccessor.java │ ├── RemotePlayerMixin.java │ ├── KeyboardInputMixin.java │ ├── HumanoidArmorLayerMixin.java │ ├── LivingEntityMixin.java │ ├── LivingEntityRendererMixin.java │ ├── PlayerRendererMixin.java │ ├── ServerPlayerMixin.java │ └── LocalPlayerMixin.java │ ├── synced_config │ ├── SyncedDoubleUpdater.java │ ├── SyncedBooleanUpdater.java │ └── SyncedConfig.java │ ├── WahooNetworking.java │ ├── packets │ ├── StartFallFlyPacket.java │ ├── UpdateSlidePacket.java │ ├── UpdateBonkPacket.java │ ├── UpdatePosePacket.java │ ├── UpdatePreviousJumpTypePacket.java │ ├── RequestStopAllActionsPacket.java │ ├── GroundPoundPacket.java │ ├── UpdateDivePacket.java │ ├── CapThrowPacket.java │ ├── CapSpawnPacket.java │ ├── UpdateSyncedBooleanPacket.java │ ├── UpdateFlipStatePacket.java │ └── UpdateSyncedDoublePacket.java │ ├── BingBingWahoo.java │ ├── api │ └── BingBingWahooApi.java │ ├── BingBingWahooConfig.java │ ├── compat │ ├── TrinketsCompat.java │ └── TemplatesCompat.java │ ├── BingBingWahooClient.java │ ├── WahooRegistry.java │ ├── WahooCommands.java │ └── WahooUtils.java ├── CHANGELOG.txt ├── settings.gradle ├── .gitignore ├── gradle.properties ├── LICENSE ├── .github └── workflows │ └── build.yml ├── gradlew.bat ├── README.md └── gradlew /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ignoramuses/bing-bing-wahoo/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /src/main/resources/data/bingbingwahoo/tags/blocks/backwards_slopes.json: -------------------------------------------------------------------------------- 1 | { 2 | "values": [ 3 | "#minecraft:stairs" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /src/main/resources/data/minecraft/tags/items/music_discs.json: -------------------------------------------------------------------------------- 1 | { 2 | "values": [ 3 | "bingbingwahoo:music_disc_slider" 4 | ] 5 | } -------------------------------------------------------------------------------- /src/main/resources/data/trinkets/tags/items/head/hat.json: -------------------------------------------------------------------------------- 1 | { 2 | "replace": false, 3 | "values": [ 4 | "bingbingwahoo:mysterious_cap" 5 | ] 6 | } -------------------------------------------------------------------------------- /src/main/resources/data/trinkets/entities/bingbingwahoo.json: -------------------------------------------------------------------------------- 1 | { 2 | "entities": [ 3 | "player" 4 | ], 5 | "slots": [ 6 | "head/hat" 7 | ] 8 | } -------------------------------------------------------------------------------- /src/main/resources/assets/bingbingwahoo/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ignoramuses/bing-bing-wahoo/HEAD/src/main/resources/assets/bingbingwahoo/icon.png -------------------------------------------------------------------------------- /src/main/resources/data/bingbingwahoo/tags/blocks/ground_pound_blacklist.json: -------------------------------------------------------------------------------- 1 | { 2 | "replace": false, 3 | "values": [ 4 | "minecraft:netherrack" 5 | ] 6 | } -------------------------------------------------------------------------------- /src/main/resources/assets/bingbingwahoo/icon_compressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ignoramuses/bing-bing-wahoo/HEAD/src/main/resources/assets/bingbingwahoo/icon_compressed.png -------------------------------------------------------------------------------- /src/main/resources/data/bingbingwahoo/tags/blocks/ground_pound_whitelist.json: -------------------------------------------------------------------------------- 1 | { 2 | "replace": false, 3 | "values": [ 4 | "minecraft:grass_block", 5 | "minecraft:bricks" 6 | ] 7 | } -------------------------------------------------------------------------------- /src/main/resources/assets/bingbingwahoo/models/item/music_disc_slider.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "item/generated", 3 | "textures": { 4 | "layer0": "bingbingwahoo:item/music_disc_slider" 5 | } 6 | } -------------------------------------------------------------------------------- /src/main/resources/assets/bingbingwahoo/sounds/music_disc_slider.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ignoramuses/bing-bing-wahoo/HEAD/src/main/resources/assets/bingbingwahoo/sounds/music_disc_slider.ogg -------------------------------------------------------------------------------- /src/main/resources/assets/bingbingwahoo/textures/item/mysterious_cap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ignoramuses/bing-bing-wahoo/HEAD/src/main/resources/assets/bingbingwahoo/textures/item/mysterious_cap.png -------------------------------------------------------------------------------- /src/main/resources/assets/bingbingwahoo/textures/armor/mysterious_cap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ignoramuses/bing-bing-wahoo/HEAD/src/main/resources/assets/bingbingwahoo/textures/armor/mysterious_cap.png -------------------------------------------------------------------------------- /src/main/resources/assets/bingbingwahoo/textures/item/music_disc_slider.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ignoramuses/bing-bing-wahoo/HEAD/src/main/resources/assets/bingbingwahoo/textures/item/music_disc_slider.png -------------------------------------------------------------------------------- /src/main/resources/assets/bingbingwahoo/sounds.json: -------------------------------------------------------------------------------- 1 | { 2 | "music_disc_slider": { 3 | "subtitle": "subtitles.bingbingwahoo.slider", 4 | "sounds": [ 5 | "bingbingwahoo:music_disc_slider" 6 | ] 7 | } 8 | } -------------------------------------------------------------------------------- /src/main/resources/assets/bingbingwahoo/textures/armor/mysterious_cap_emblem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ignoramuses/bing-bing-wahoo/HEAD/src/main/resources/assets/bingbingwahoo/textures/armor/mysterious_cap_emblem.png -------------------------------------------------------------------------------- /src/main/resources/assets/bingbingwahoo/textures/item/mysterious_cap_overlay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ignoramuses/bing-bing-wahoo/HEAD/src/main/resources/assets/bingbingwahoo/textures/item/mysterious_cap_overlay.png -------------------------------------------------------------------------------- /src/main/java/io/github/ignoramuses/bing_bing_wahoo/content/movement/FlipState.java: -------------------------------------------------------------------------------- 1 | package io.github.ignoramuses.bing_bing_wahoo.content.movement; 2 | 3 | public enum FlipState { 4 | NONE, 5 | FORWARDS, 6 | BACKWARDS 7 | } 8 | -------------------------------------------------------------------------------- /src/main/resources/assets/bingbingwahoo/models/item/mysterious_cap.json: -------------------------------------------------------------------------------- 1 | { 2 | "parent": "item/generated", 3 | "textures": { 4 | "layer0": "bingbingwahoo:item/mysterious_cap", 5 | "layer1": "bingbingwahoo:item/mysterious_cap_overlay" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /CHANGELOG.txt: -------------------------------------------------------------------------------- 1 | INFO: 2 | All changes should be appended to this file with each commit. 3 | On publish, it should be cleared to prepare for the next release cycle. 4 | 5 | Change logging starts below: 6 | ---------- 7 | - Make boot destruction area work properly -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | maven { url = "https://maven.fabricmc.net/" } 4 | maven { url = "https://maven.quiltmc.org/repository/release" } 5 | mavenCentral() 6 | gradlePluginPortal() 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/io/github/ignoramuses/bing_bing_wahoo/content/cap/CapWearer.java: -------------------------------------------------------------------------------- 1 | package io.github.ignoramuses.bing_bing_wahoo.content.cap; 2 | 3 | import net.minecraft.world.item.ItemStack; 4 | 5 | public interface CapWearer { 6 | boolean isWearingCap(); 7 | ItemStack getCap(); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/io/github/ignoramuses/bing_bing_wahoo/extensions/PlayerExtensions.java: -------------------------------------------------------------------------------- 1 | package io.github.ignoramuses.bing_bing_wahoo.extensions; 2 | 3 | public interface PlayerExtensions { 4 | default boolean getSliding() { 5 | throw new IllegalStateException("Not implemented"); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/io/github/ignoramuses/bing_bing_wahoo/content/movement/GroundPoundType.java: -------------------------------------------------------------------------------- 1 | package io.github.ignoramuses.bing_bing_wahoo.content.movement; 2 | 3 | public enum GroundPoundType { 4 | DISABLED, 5 | ENABLED, 6 | DESTRUCTIVE; 7 | 8 | public boolean enabled() { 9 | return this == ENABLED || this == DESTRUCTIVE; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # gradle 2 | 3 | .gradle/ 4 | build/ 5 | out/ 6 | classes/ 7 | 8 | # eclipse 9 | 10 | *.launch 11 | 12 | # idea 13 | 14 | .idea/ 15 | *.iml 16 | *.ipr 17 | *.iws 18 | 19 | # vscode 20 | 21 | .settings/ 22 | .vscode/ 23 | bin/ 24 | .classpath 25 | .project 26 | 27 | # macos 28 | 29 | *.DS_Store 30 | 31 | # fabric 32 | 33 | run/ 34 | -------------------------------------------------------------------------------- /src/main/resources/data/bingbingwahoo/recipes/mysterious_cap.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "minecraft:crafting_shaped", 3 | "pattern": [ 4 | "###", 5 | "# #", 6 | " " 7 | ], 8 | "key": { 9 | "#": { 10 | "tag": "minecraft:wool" 11 | } 12 | }, 13 | "result": { 14 | "item": "bingbingwahoo:mysterious_cap", 15 | "count": 1 16 | } 17 | } -------------------------------------------------------------------------------- /src/main/resources/data/bingbingwahoo/tags/blocks/slides.json: -------------------------------------------------------------------------------- 1 | { 2 | "replace": false, 3 | "values": [ 4 | "#minecraft:ice", 5 | "#minecraft:snow", 6 | "#minecraft:stairs", 7 | "#bingbingwahoo:backwards_slopes", 8 | "#bingbingwahoo:standard_slopes", 9 | 10 | { 11 | "id": "templates:slope", 12 | "required": false 13 | } 14 | ] 15 | } -------------------------------------------------------------------------------- /src/main/java/io/github/ignoramuses/bing_bing_wahoo/extensions/KeyboardInputExtensions.java: -------------------------------------------------------------------------------- 1 | package io.github.ignoramuses.bing_bing_wahoo.extensions; 2 | 3 | import net.fabricmc.api.EnvType; 4 | import net.fabricmc.api.Environment; 5 | 6 | @Environment(EnvType.CLIENT) 7 | public interface KeyboardInputExtensions { 8 | default void disableControl() { 9 | throw new IllegalStateException("Not implemented"); 10 | } 11 | 12 | default void enableControl() { 13 | throw new IllegalStateException("Not implemented"); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/io/github/ignoramuses/bing_bing_wahoo/mixin/EntityAccessor.java: -------------------------------------------------------------------------------- 1 | package io.github.ignoramuses.bing_bing_wahoo.mixin; 2 | 3 | import net.minecraft.world.entity.Entity; 4 | import net.minecraft.world.phys.Vec3; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.gen.Accessor; 7 | import org.spongepowered.asm.mixin.gen.Invoker; 8 | 9 | @Mixin(Entity.class) 10 | public interface EntityAccessor { 11 | @Accessor("xRot") 12 | void setXRotRaw(float pitch); 13 | 14 | @Invoker 15 | Vec3 callCalculateViewVector(float pitch, float yaw); 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/io/github/ignoramuses/bing_bing_wahoo/content/movement/JumpType.java: -------------------------------------------------------------------------------- 1 | package io.github.ignoramuses.bing_bing_wahoo.content.movement; 2 | 3 | public enum JumpType { 4 | NORMAL, 5 | LONG, 6 | DOUBLE, 7 | TRIPLE, 8 | DIVE, 9 | WALL, 10 | BACK_FLIP; 11 | 12 | public boolean isRegularJump() { 13 | return this == NORMAL || this == DOUBLE || this == TRIPLE || this == LONG; 14 | } 15 | 16 | public boolean canWallJumpFrom() { 17 | return (isRegularJump() || this == WALL) && this != NORMAL; 18 | } 19 | 20 | public boolean canLongJumpFrom() { 21 | return isRegularJump() && this != TRIPLE && this != DIVE; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/io/github/ignoramuses/bing_bing_wahoo/synced_config/SyncedDoubleUpdater.java: -------------------------------------------------------------------------------- 1 | package io.github.ignoramuses.bing_bing_wahoo.synced_config; 2 | 3 | import io.github.ignoramuses.bing_bing_wahoo.packets.UpdateSyncedDoublePacket; 4 | import net.fabricmc.fabric.api.gamerule.v1.rule.DoubleRule; 5 | import net.minecraft.server.MinecraftServer; 6 | 7 | import java.util.function.BiConsumer; 8 | 9 | public record SyncedDoubleUpdater(String ruleName) implements BiConsumer { 10 | @Override 11 | public void accept(MinecraftServer server, DoubleRule value) { 12 | UpdateSyncedDoublePacket.sendToAll(ruleName, server, value); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/io/github/ignoramuses/bing_bing_wahoo/WahooNetworking.java: -------------------------------------------------------------------------------- 1 | package io.github.ignoramuses.bing_bing_wahoo; 2 | 3 | import io.github.ignoramuses.bing_bing_wahoo.packets.*; 4 | import io.github.ignoramuses.bing_bing_wahoo.synced_config.SyncedConfig; 5 | 6 | public class WahooNetworking { 7 | public static void init() { 8 | CapThrowPacket.init(); 9 | GroundPoundPacket.init(); 10 | UpdatePreviousJumpTypePacket.init(); 11 | StartFallFlyPacket.init(); 12 | UpdateBonkPacket.init(); 13 | UpdateDivePacket.init(); 14 | UpdateFlipStatePacket.init(); 15 | UpdatePosePacket.init(); 16 | UpdateSlidePacket.init(); 17 | 18 | SyncedConfig.init(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/io/github/ignoramuses/bing_bing_wahoo/synced_config/SyncedBooleanUpdater.java: -------------------------------------------------------------------------------- 1 | package io.github.ignoramuses.bing_bing_wahoo.synced_config; 2 | 3 | import io.github.ignoramuses.bing_bing_wahoo.packets.UpdateSyncedBooleanPacket; 4 | import net.minecraft.server.MinecraftServer; 5 | import net.minecraft.world.level.GameRules.BooleanValue; 6 | 7 | import java.util.function.BiConsumer; 8 | 9 | public record SyncedBooleanUpdater(String ruleName) implements BiConsumer { 10 | @Override 11 | public void accept(MinecraftServer server, BooleanValue value) { 12 | UpdateSyncedBooleanPacket.sendToAll(ruleName, server, value); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/resources/bingbingwahoo.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "minVersion": "0.8", 4 | "package": "io.github.ignoramuses.bing_bing_wahoo.mixin", 5 | "refmap": "${archivesBaseName}-refmap.json", 6 | "compatibilityLevel": "JAVA_17", 7 | "client": [ 8 | "EntityAccessor", 9 | "HumanoidArmorLayerMixin", 10 | "KeyboardInputMixin", 11 | "LivingEntityRendererMixin", 12 | "LocalPlayerMixin", 13 | "PlayerRendererMixin" 14 | ], 15 | "injectors": { 16 | "defaultRequire": 1 17 | }, 18 | "mixins": [ 19 | "EntitySelectorOptionsAccessor", 20 | "LivingEntityMixin", 21 | "RemotePlayerMixin", 22 | "ServerPlayerMixin" 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/io/github/ignoramuses/bing_bing_wahoo/extensions/AbstractClientPlayerExtensions.java: -------------------------------------------------------------------------------- 1 | package io.github.ignoramuses.bing_bing_wahoo.extensions; 2 | 3 | import io.github.ignoramuses.bing_bing_wahoo.content.movement.FlipState; 4 | import net.fabricmc.api.EnvType; 5 | import net.fabricmc.api.Environment; 6 | 7 | @Environment(EnvType.CLIENT) 8 | public interface AbstractClientPlayerExtensions { 9 | default int ticksFlipping() { 10 | throw new IllegalStateException("Not implemented"); 11 | } 12 | 13 | default void setFlipState(FlipState state) { 14 | throw new IllegalStateException("Not implemented"); 15 | } 16 | 17 | default boolean flippingForwards() { 18 | throw new IllegalStateException("Not implemented"); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/io/github/ignoramuses/bing_bing_wahoo/mixin/EntitySelectorOptionsAccessor.java: -------------------------------------------------------------------------------- 1 | package io.github.ignoramuses.bing_bing_wahoo.mixin; 2 | 3 | import org.spongepowered.asm.mixin.Mixin; 4 | import org.spongepowered.asm.mixin.gen.Invoker; 5 | 6 | import java.util.function.Predicate; 7 | import net.minecraft.commands.arguments.selector.EntitySelectorParser; 8 | import net.minecraft.commands.arguments.selector.options.EntitySelectorOptions; 9 | import net.minecraft.network.chat.Component; 10 | 11 | @Mixin(EntitySelectorOptions.class) 12 | public interface EntitySelectorOptionsAccessor { 13 | @Invoker 14 | static void callRegister(String id, EntitySelectorOptions.Modifier handler, 15 | Predicate condition, Component description) { 16 | throw new RuntimeException("mixin failed!"); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/io/github/ignoramuses/bing_bing_wahoo/extensions/LocalPlayerExtensions.java: -------------------------------------------------------------------------------- 1 | package io.github.ignoramuses.bing_bing_wahoo.extensions; 2 | 3 | public interface LocalPlayerExtensions { 4 | default boolean groundPounding() { 5 | throw new IllegalStateException("Not implemented"); 6 | } 7 | 8 | default boolean slidingOnSlope() { 9 | throw new IllegalStateException("Not implemented"); 10 | } 11 | 12 | default boolean slidingOnGround() { 13 | throw new IllegalStateException("Not implemented"); 14 | } 15 | 16 | default boolean diving() { 17 | throw new IllegalStateException("Not implemented"); 18 | } 19 | 20 | default void startDiving() { 21 | throw new IllegalStateException("Not implemented"); 22 | } 23 | 24 | default void stopAllActions() { 25 | throw new IllegalStateException("Not implemented"); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/io/github/ignoramuses/bing_bing_wahoo/packets/StartFallFlyPacket.java: -------------------------------------------------------------------------------- 1 | package io.github.ignoramuses.bing_bing_wahoo.packets; 2 | 3 | import io.github.ignoramuses.bing_bing_wahoo.BingBingWahoo; 4 | import net.fabricmc.api.EnvType; 5 | import net.fabricmc.api.Environment; 6 | import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; 7 | import net.fabricmc.fabric.api.networking.v1.PacketByteBufs; 8 | import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; 9 | import net.minecraft.resources.ResourceLocation; 10 | 11 | public class StartFallFlyPacket { 12 | public static final ResourceLocation ID = BingBingWahoo.id("start_fall_fly"); 13 | 14 | public static void init() { 15 | ServerPlayNetworking.registerGlobalReceiver(ID, (server, player, handler, buf, responseSender) -> 16 | server.execute(player::tryToStartFallFlying) 17 | ); 18 | } 19 | 20 | @Environment(EnvType.CLIENT) 21 | public static void send() { 22 | ClientPlayNetworking.send(ID, PacketByteBufs.create()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Done to increase the memory available to gradle. 2 | org.gradle.jvmargs=-Xmx2G 3 | 4 | # Fabric Properties 5 | # check these on https://fabricmc.net/develop 6 | minecraft_version = 1.20.1 7 | compatible_minecraft_versions = 1.20.1 8 | loader_version = 0.14.21 9 | 10 | # Mod Properties 11 | mod_version = 4.2.6 12 | maven_group = io.github.ignoramuses 13 | archives_base_name = BingBingWahoo 14 | 15 | # Mappings 16 | # https://lambdaurora.dev/tools/import_quilt.html 17 | qm_version = 21 18 | 19 | # Dependencies 20 | fabric_version=0.85.0+1.20.1 21 | # https://modrinth.com/mod/midnightlib 22 | midnightlib_version = 1.4.1-fabric 23 | 24 | # Compat 25 | # https://modrinth.com/mod/automobility 26 | automobility_version = 0.4.2+1.20.1-fabric 27 | jsonem_version=0.2.1+1.20 28 | # https://modrinth.com/mod/templates-2 29 | templates_version = 2.0.3-fabric-1.20.1 30 | # https://modrinth.com/mod/conventional-cubes 31 | conventional_cubes_version = 0.8.1+1.20.1 32 | # https://github.com/emilyploszaj/trinkets/releases 33 | trinkets_version = 3.7.0 34 | 35 | # Dev Env 36 | # https://modrinth.com/mod/modmenu/versions 37 | mod_menu_version = 7.0.1 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Tropheus Jay & InsaneASockJr 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /src/main/java/io/github/ignoramuses/bing_bing_wahoo/packets/UpdateSlidePacket.java: -------------------------------------------------------------------------------- 1 | package io.github.ignoramuses.bing_bing_wahoo.packets; 2 | 3 | import io.github.ignoramuses.bing_bing_wahoo.BingBingWahoo; 4 | import net.fabricmc.api.EnvType; 5 | import net.fabricmc.api.Environment; 6 | import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; 7 | import net.fabricmc.fabric.api.networking.v1.PacketByteBufs; 8 | import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; 9 | import net.minecraft.network.FriendlyByteBuf; 10 | import net.minecraft.resources.ResourceLocation; 11 | 12 | public class UpdateSlidePacket { 13 | public static final ResourceLocation ID = BingBingWahoo.id("update_slide"); 14 | 15 | public static void init() { 16 | ServerPlayNetworking.registerGlobalReceiver(ID, (server, player, handler, buf, responseSender) -> { 17 | boolean start = buf.readBoolean(); 18 | server.execute(() -> player.setSliding(start)); 19 | }); 20 | } 21 | 22 | @Environment(EnvType.CLIENT) 23 | public static void send(boolean start) { 24 | FriendlyByteBuf buf = PacketByteBufs.create(); 25 | buf.writeBoolean(start); 26 | ClientPlayNetworking.send(ID, buf); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/io/github/ignoramuses/bing_bing_wahoo/packets/UpdateBonkPacket.java: -------------------------------------------------------------------------------- 1 | package io.github.ignoramuses.bing_bing_wahoo.packets; 2 | 3 | import io.github.ignoramuses.bing_bing_wahoo.BingBingWahoo; 4 | import net.fabricmc.api.EnvType; 5 | import net.fabricmc.api.Environment; 6 | import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; 7 | import net.fabricmc.fabric.api.networking.v1.PacketByteBufs; 8 | import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; 9 | import net.minecraft.network.FriendlyByteBuf; 10 | import net.minecraft.resources.ResourceLocation; 11 | 12 | public class UpdateBonkPacket { 13 | public static final ResourceLocation ID = BingBingWahoo.id("update_bonk"); 14 | 15 | public static void init() { 16 | ServerPlayNetworking.registerGlobalReceiver(ID, (server, player, handler, buf, responseSender) -> { 17 | boolean started = buf.readBoolean(); 18 | server.execute(() -> 19 | player.setBonked(started)); 20 | }); 21 | } 22 | 23 | @Environment(EnvType.CLIENT) 24 | public static void send(boolean started) { 25 | FriendlyByteBuf buf = PacketByteBufs.create(); 26 | buf.writeBoolean(started); 27 | ClientPlayNetworking.send(ID, buf); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | on: 3 | workflow_dispatch: 4 | inputs: 5 | publish: 6 | description: Publish to Modrinth 7 | required: true 8 | default: "false" 9 | pull_request: 10 | push: 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | env: 16 | PUBLISH_SUFFIX: snapshots 17 | MAVEN_USER: ${{ secrets.MAVEN_USER }} 18 | MAVEN_PASS: ${{ secrets.MAVEN_PASS }} 19 | MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }} 20 | steps: 21 | 22 | - name: checkout repository 23 | uses: actions/checkout@v3 24 | 25 | - name: make gradle wrapper executable 26 | run: chmod +x ./gradlew 27 | 28 | - name: setup Java 29 | uses: actions/setup-java@v3 30 | with: 31 | distribution: temurin 32 | java-version: 17 33 | cache: gradle 34 | 35 | - name: build 36 | run: ./gradlew buildOrPublish 37 | 38 | - name: capture build artifacts 39 | uses: actions/upload-artifact@v3 40 | with: 41 | name: Artifacts 42 | path: build/libs/ 43 | 44 | - name: publish to Modrinth 45 | if: ${{ github.event.inputs.publish }} 46 | run: ./gradlew modrinth -------------------------------------------------------------------------------- /src/main/java/io/github/ignoramuses/bing_bing_wahoo/packets/UpdatePosePacket.java: -------------------------------------------------------------------------------- 1 | package io.github.ignoramuses.bing_bing_wahoo.packets; 2 | 3 | import io.github.ignoramuses.bing_bing_wahoo.BingBingWahoo; 4 | import net.fabricmc.api.EnvType; 5 | import net.fabricmc.api.Environment; 6 | import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; 7 | import net.fabricmc.fabric.api.networking.v1.PacketByteBufs; 8 | import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; 9 | import net.minecraft.network.FriendlyByteBuf; 10 | import net.minecraft.resources.ResourceLocation; 11 | import net.minecraft.world.entity.Pose; 12 | 13 | public class UpdatePosePacket { 14 | public static final ResourceLocation ID = BingBingWahoo.id("update_pose"); 15 | 16 | public static void init() { 17 | ServerPlayNetworking.registerGlobalReceiver(ID, ((server, player, handler, buf, responseSender) -> { 18 | Pose newPose = Pose.values()[buf.readVarInt()]; 19 | server.execute(() -> player.setPose(newPose)); 20 | })); 21 | } 22 | 23 | @Environment(EnvType.CLIENT) 24 | public static void send(Pose requested) { 25 | int ordinal = requested.ordinal(); 26 | FriendlyByteBuf buf = PacketByteBufs.create() 27 | .writeVarInt(ordinal); 28 | ClientPlayNetworking.send(ID, buf); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/io/github/ignoramuses/bing_bing_wahoo/packets/UpdatePreviousJumpTypePacket.java: -------------------------------------------------------------------------------- 1 | package io.github.ignoramuses.bing_bing_wahoo.packets; 2 | 3 | import io.github.ignoramuses.bing_bing_wahoo.BingBingWahoo; 4 | import io.github.ignoramuses.bing_bing_wahoo.content.movement.JumpType; 5 | import net.fabricmc.api.EnvType; 6 | import net.fabricmc.api.Environment; 7 | import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; 8 | import net.fabricmc.fabric.api.networking.v1.PacketByteBufs; 9 | import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; 10 | import net.minecraft.network.FriendlyByteBuf; 11 | import net.minecraft.resources.ResourceLocation; 12 | 13 | public class UpdatePreviousJumpTypePacket { 14 | public static final ResourceLocation ID = BingBingWahoo.id("jump_type_packet"); 15 | 16 | public static void init() { 17 | ServerPlayNetworking.registerGlobalReceiver(ID, (server, player, handler, buf, responseSender) -> { 18 | JumpType jumpType = buf.readEnum(JumpType.class); 19 | server.execute(() -> player.setPreviousJumpType(jumpType)); 20 | }); 21 | } 22 | 23 | @Environment(EnvType.CLIENT) 24 | public static void send(JumpType previous) { 25 | FriendlyByteBuf buf = PacketByteBufs.create() 26 | .writeEnum(previous); 27 | ClientPlayNetworking.send(ID, buf); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/io/github/ignoramuses/bing_bing_wahoo/BingBingWahoo.java: -------------------------------------------------------------------------------- 1 | package io.github.ignoramuses.bing_bing_wahoo; 2 | 3 | import eu.midnightdust.lib.config.MidnightConfig; 4 | import net.fabricmc.api.ModInitializer; 5 | import net.minecraft.core.registries.Registries; 6 | import net.minecraft.resources.ResourceLocation; 7 | import net.minecraft.tags.TagKey; 8 | import net.minecraft.world.level.block.Block; 9 | import org.slf4j.Logger; 10 | import org.slf4j.LoggerFactory; 11 | 12 | public class BingBingWahoo implements ModInitializer { 13 | public static final String ID = "bingbingwahoo"; 14 | public static final Logger LOGGER = LoggerFactory.getLogger(ID); 15 | public static final TagKey SLIDES = TagKey.create(Registries.BLOCK, id("slides")); 16 | public static final TagKey GROUND_POUND_BLACKLIST = TagKey.create(Registries.BLOCK, id("ground_pound_blacklist")); 17 | public static final TagKey GROUND_POUND_WHITELIST = TagKey.create(Registries.BLOCK, id("ground_pound_whitelist")); 18 | 19 | @Override 20 | public void onInitialize() { 21 | WahooNetworking.init(); 22 | WahooCommands.init(); 23 | WahooRegistry.init(); 24 | MidnightConfig.init(ID, BingBingWahooConfig.class); 25 | } 26 | 27 | public static ResourceLocation id(String path) { 28 | return new ResourceLocation(ID, path); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/io/github/ignoramuses/bing_bing_wahoo/extensions/ServerPlayerExtensions.java: -------------------------------------------------------------------------------- 1 | package io.github.ignoramuses.bing_bing_wahoo.extensions; 2 | 3 | import io.github.ignoramuses.bing_bing_wahoo.content.movement.JumpType; 4 | import net.minecraft.core.BlockPos; 5 | import org.jetbrains.annotations.Nullable; 6 | 7 | public interface ServerPlayerExtensions { 8 | default void setPreviousJumpType(JumpType type) { 9 | throw new IllegalStateException("Not implemented"); 10 | } 11 | 12 | default void setBonked(boolean value) { 13 | throw new IllegalStateException("Not implemented"); 14 | } 15 | 16 | default void setGroundPounding(boolean value, boolean breakBlocks) { 17 | throw new IllegalStateException("Not implemented"); 18 | } 19 | 20 | default boolean isGroundPounding() { 21 | throw new IllegalStateException("not implemented"); 22 | } 23 | 24 | default void setDiving(boolean value, @Nullable BlockPos startPos) { 25 | throw new IllegalStateException("Not implemented"); 26 | } 27 | 28 | default boolean isDiving() { 29 | throw new IllegalStateException("Not implemented"); 30 | } 31 | 32 | default void setSliding(boolean value) { 33 | throw new IllegalStateException("Not implemented"); 34 | } 35 | 36 | default void setDestructionPermOverride(boolean value) { 37 | throw new IllegalStateException("Not implemented"); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/io/github/ignoramuses/bing_bing_wahoo/mixin/RemotePlayerMixin.java: -------------------------------------------------------------------------------- 1 | package io.github.ignoramuses.bing_bing_wahoo.mixin; 2 | 3 | import io.github.ignoramuses.bing_bing_wahoo.content.movement.FlipState; 4 | import io.github.ignoramuses.bing_bing_wahoo.extensions.AbstractClientPlayerExtensions; 5 | import net.minecraft.client.player.RemotePlayer; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.Unique; 8 | import org.spongepowered.asm.mixin.injection.At; 9 | import org.spongepowered.asm.mixin.injection.Inject; 10 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 11 | 12 | @Mixin(RemotePlayer.class) 13 | public abstract class RemotePlayerMixin implements AbstractClientPlayerExtensions { 14 | @Unique 15 | private int ticksFlipping; 16 | @Unique 17 | private boolean forwardsFlipping; 18 | 19 | @Inject(method = "tick", at = @At("TAIL")) 20 | private void updateFlipCounter(CallbackInfo ci) { 21 | if (ticksFlipping > 0) ticksFlipping++; 22 | } 23 | 24 | @Override 25 | public int ticksFlipping() { 26 | return ticksFlipping; 27 | } 28 | 29 | @Override 30 | public void setFlipState(FlipState state) { 31 | ticksFlipping = state != FlipState.NONE ? 1 : 0; 32 | forwardsFlipping = state == FlipState.FORWARDS; 33 | } 34 | 35 | @Override 36 | public boolean flippingForwards() { 37 | return forwardsFlipping; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/io/github/ignoramuses/bing_bing_wahoo/content/cap/CapFlyingSoundInstance.java: -------------------------------------------------------------------------------- 1 | package io.github.ignoramuses.bing_bing_wahoo.content.cap; 2 | 3 | import net.fabricmc.api.EnvType; 4 | import net.fabricmc.api.Environment; 5 | import net.minecraft.client.Minecraft; 6 | import net.minecraft.client.player.LocalPlayer; 7 | import net.minecraft.client.resources.sounds.AbstractTickableSoundInstance; 8 | import net.minecraft.sounds.SoundEvents; 9 | import net.minecraft.sounds.SoundSource; 10 | 11 | @Environment(EnvType.CLIENT) 12 | public class CapFlyingSoundInstance extends AbstractTickableSoundInstance { 13 | protected FlyingCapEntity capEntity; 14 | 15 | public CapFlyingSoundInstance(FlyingCapEntity capEntity) { 16 | super(SoundEvents.ELYTRA_FLYING, SoundSource.NEUTRAL, capEntity.getRandom()); 17 | this.capEntity = capEntity; 18 | this.looping = true; 19 | this.delay = 0; 20 | this.volume = 0.1F; 21 | this.pitch = 1.75f; 22 | } 23 | 24 | @Override 25 | public void tick() { 26 | if (!capEntity.isRemoved() && !capEntity.isSilent()) { 27 | this.x = this.capEntity.getX(); 28 | this.y = this.capEntity.getY(); 29 | this.z = this.capEntity.getZ(); 30 | LocalPlayer player = Minecraft.getInstance().player; 31 | float distance = (float) player.position().distanceTo(capEntity.position()); 32 | this.volume = 1 / distance; 33 | } else { 34 | stop(); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/io/github/ignoramuses/bing_bing_wahoo/packets/RequestStopAllActionsPacket.java: -------------------------------------------------------------------------------- 1 | package io.github.ignoramuses.bing_bing_wahoo.packets; 2 | 3 | import io.github.ignoramuses.bing_bing_wahoo.BingBingWahoo; 4 | import net.fabricmc.api.EnvType; 5 | import net.fabricmc.api.Environment; 6 | import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; 7 | import net.fabricmc.fabric.api.networking.v1.PacketByteBufs; 8 | import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; 9 | import net.minecraft.client.Minecraft; 10 | import net.minecraft.client.player.LocalPlayer; 11 | import net.minecraft.resources.ResourceLocation; 12 | import net.minecraft.server.level.ServerPlayer; 13 | 14 | public class RequestStopAllActionsPacket { 15 | public static final ResourceLocation ID = BingBingWahoo.id("request_stop_all_actions"); 16 | 17 | public static void send(ServerPlayer player) { 18 | ServerPlayNetworking.send(player, ID, PacketByteBufs.empty()); 19 | } 20 | 21 | @Environment(EnvType.CLIENT) 22 | public static void initClient() { 23 | ClientPlayNetworking.registerGlobalReceiver(ID, (client, handler, buf, responseSender) -> 24 | client.execute(RequestStopAllActionsPacket::stopAllActions) 25 | ); 26 | } 27 | 28 | private static void stopAllActions() { 29 | LocalPlayer player = Minecraft.getInstance().player; 30 | if (player != null) 31 | player.stopAllActions(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/io/github/ignoramuses/bing_bing_wahoo/mixin/KeyboardInputMixin.java: -------------------------------------------------------------------------------- 1 | package io.github.ignoramuses.bing_bing_wahoo.mixin; 2 | 3 | import net.fabricmc.api.EnvType; 4 | import net.fabricmc.api.Environment; 5 | import io.github.ignoramuses.bing_bing_wahoo.extensions.KeyboardInputExtensions; 6 | import net.minecraft.client.player.Input; 7 | import net.minecraft.client.player.KeyboardInput; 8 | import org.spongepowered.asm.mixin.Mixin; 9 | import org.spongepowered.asm.mixin.Unique; 10 | import org.spongepowered.asm.mixin.injection.At; 11 | import org.spongepowered.asm.mixin.injection.Inject; 12 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 13 | 14 | @Environment(EnvType.CLIENT) 15 | @Mixin(KeyboardInput.class) 16 | public abstract class KeyboardInputMixin extends Input implements KeyboardInputExtensions { 17 | @Unique 18 | private boolean disableControl = false; 19 | 20 | @Inject(at = @At("TAIL"), method = "tick") 21 | public void tick(boolean slowDown, float f, CallbackInfo ci) { 22 | if (disableControl) { 23 | up = false; 24 | down = false; 25 | left = false; 26 | right = false; 27 | forwardImpulse = 0; 28 | leftImpulse = 0; 29 | jumping = false; 30 | shiftKeyDown = false; 31 | } 32 | } 33 | 34 | @Override 35 | public void disableControl() { 36 | disableControl = true; 37 | } 38 | 39 | @Override 40 | public void enableControl() { 41 | disableControl = false; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/io/github/ignoramuses/bing_bing_wahoo/content/cap/MysteriousCapArmorMaterial.java: -------------------------------------------------------------------------------- 1 | package io.github.ignoramuses.bing_bing_wahoo.content.cap; 2 | 3 | import net.minecraft.sounds.SoundEvent; 4 | import net.minecraft.sounds.SoundEvents; 5 | import net.minecraft.tags.ItemTags; 6 | import net.minecraft.world.item.ArmorItem.Type; 7 | import net.minecraft.world.item.ArmorMaterial; 8 | import net.minecraft.world.item.crafting.Ingredient; 9 | import org.jetbrains.annotations.NotNull; 10 | 11 | public enum MysteriousCapArmorMaterial implements ArmorMaterial { 12 | INSTANCE; 13 | 14 | public static final Ingredient REPAIR = Ingredient.of(ItemTags.WOOL); 15 | 16 | @Override 17 | public int getDurabilityForType(Type slot) { 18 | return 128; 19 | } 20 | 21 | @Override 22 | public int getDefenseForType(Type slot) { 23 | return 1; 24 | } 25 | 26 | @Override 27 | public int getEnchantmentValue() { 28 | return 15; 29 | } 30 | 31 | @Override 32 | @NotNull 33 | public SoundEvent getEquipSound() { 34 | return SoundEvents.ARMOR_EQUIP_LEATHER; 35 | } 36 | 37 | @Override 38 | @NotNull 39 | public Ingredient getRepairIngredient() { 40 | return REPAIR; 41 | } 42 | 43 | @Override 44 | @NotNull 45 | public String getName() { 46 | return "mysterious_cap"; 47 | } 48 | 49 | @Override 50 | public float getToughness() { 51 | return 0; 52 | } 53 | 54 | @Override 55 | public float getKnockbackResistance() { 56 | return 0; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/io/github/ignoramuses/bing_bing_wahoo/content/SliderRecordItem.java: -------------------------------------------------------------------------------- 1 | package io.github.ignoramuses.bing_bing_wahoo.content; 2 | 3 | import io.github.ignoramuses.bing_bing_wahoo.BingBingWahoo; 4 | import net.minecraft.ChatFormatting; 5 | import net.minecraft.network.chat.Component; 6 | import net.minecraft.network.chat.MutableComponent; 7 | import net.minecraft.resources.ResourceLocation; 8 | import net.minecraft.sounds.SoundEvent; 9 | import net.minecraft.world.item.ItemStack; 10 | import net.minecraft.world.item.RecordItem; 11 | import net.minecraft.world.item.TooltipFlag; 12 | import net.minecraft.world.level.Level; 13 | import org.jetbrains.annotations.Nullable; 14 | 15 | import java.util.List; 16 | 17 | public class SliderRecordItem extends RecordItem { 18 | public static final ResourceLocation SOUND_ID = BingBingWahoo.id("music_disc_slider"); 19 | public static final int LENGTH_SECONDS = (60 * 2) + 43; // 2:43 20 | public static final MutableComponent SLIDER_DESC_2 = Component.translatable("item.bingbingwahoo.music_disc_slider.desc2") 21 | .withStyle(ChatFormatting.GRAY); 22 | 23 | public SliderRecordItem(int i, SoundEvent soundEvent, Properties properties) { 24 | super(i, soundEvent, properties, LENGTH_SECONDS); 25 | } 26 | 27 | @Override 28 | public void appendHoverText(ItemStack stack, @Nullable Level level, List tooltipComponents, TooltipFlag isAdvanced) { 29 | super.appendHoverText(stack, level, tooltipComponents, isAdvanced); 30 | tooltipComponents.add(SLIDER_DESC_2); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/io/github/ignoramuses/bing_bing_wahoo/packets/GroundPoundPacket.java: -------------------------------------------------------------------------------- 1 | package io.github.ignoramuses.bing_bing_wahoo.packets; 2 | 3 | import io.github.ignoramuses.bing_bing_wahoo.BingBingWahoo; 4 | import io.github.ignoramuses.bing_bing_wahoo.BingBingWahooConfig; 5 | import io.github.ignoramuses.bing_bing_wahoo.content.movement.GroundPoundType; 6 | import net.fabricmc.api.EnvType; 7 | import net.fabricmc.api.Environment; 8 | import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; 9 | import net.fabricmc.fabric.api.networking.v1.PacketByteBufs; 10 | import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; 11 | import net.minecraft.network.FriendlyByteBuf; 12 | import net.minecraft.resources.ResourceLocation; 13 | 14 | public class GroundPoundPacket { 15 | public static final ResourceLocation ID = BingBingWahoo.id("ground_pound_packet"); 16 | 17 | public static void init() { 18 | ServerPlayNetworking.registerGlobalReceiver(ID, (server, player, handler, buf, responseSender) -> { 19 | boolean groundPounding = buf.readBoolean(); 20 | boolean destruction = buf.readBoolean(); 21 | server.execute(() -> player.setGroundPounding(groundPounding, destruction)); 22 | }); 23 | } 24 | 25 | @Environment(EnvType.CLIENT) 26 | public static void send(boolean started) { 27 | FriendlyByteBuf buf = PacketByteBufs.create(); 28 | buf.writeBoolean(started); 29 | buf.writeBoolean(BingBingWahooConfig.groundPoundType == GroundPoundType.DESTRUCTIVE); 30 | ClientPlayNetworking.send(ID, buf); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/io/github/ignoramuses/bing_bing_wahoo/packets/UpdateDivePacket.java: -------------------------------------------------------------------------------- 1 | package io.github.ignoramuses.bing_bing_wahoo.packets; 2 | 3 | import io.github.ignoramuses.bing_bing_wahoo.BingBingWahoo; 4 | import net.fabricmc.api.EnvType; 5 | import net.fabricmc.api.Environment; 6 | import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; 7 | import net.fabricmc.fabric.api.networking.v1.PacketByteBufs; 8 | import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; 9 | import net.minecraft.core.BlockPos; 10 | import net.minecraft.network.FriendlyByteBuf; 11 | import net.minecraft.resources.ResourceLocation; 12 | 13 | public class UpdateDivePacket { 14 | public static final ResourceLocation ID = BingBingWahoo.id("update_dive"); 15 | 16 | public static void init() { 17 | ServerPlayNetworking.registerGlobalReceiver(ID, (server, player, handler, buf, responseSender) -> { 18 | boolean start = buf.readBoolean(); 19 | BlockPos startPos = null; 20 | if (start) { 21 | startPos = buf.readBlockPos(); 22 | } 23 | BlockPos finalStartPos = startPos; 24 | server.execute(() -> player.setDiving(start, finalStartPos)); 25 | }); 26 | } 27 | 28 | @Environment(EnvType.CLIENT) 29 | public static void sendStart(BlockPos startPos) { 30 | FriendlyByteBuf buf = PacketByteBufs.create(); 31 | buf.writeBoolean(true); 32 | buf.writeBlockPos(startPos); 33 | ClientPlayNetworking.send(ID, buf); 34 | } 35 | 36 | @Environment(EnvType.CLIENT) 37 | public static void sendStop() { 38 | FriendlyByteBuf buf = PacketByteBufs.create(); 39 | buf.writeBoolean(false); 40 | ClientPlayNetworking.send(ID, buf); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/io/github/ignoramuses/bing_bing_wahoo/api/BingBingWahooApi.java: -------------------------------------------------------------------------------- 1 | package io.github.ignoramuses.bing_bing_wahoo.api; 2 | 3 | import io.github.ignoramuses.bing_bing_wahoo.content.cap.FlyingCapEntity; 4 | import io.github.ignoramuses.bing_bing_wahoo.extensions.LocalPlayerExtensions; 5 | import io.github.ignoramuses.bing_bing_wahoo.extensions.ServerPlayerExtensions; 6 | import net.minecraft.world.entity.Entity; 7 | import net.minecraft.world.entity.player.Player; 8 | import org.jetbrains.annotations.Nullable; 9 | 10 | /** 11 | * Stable API methods. Unless otherwise noted, these work on client and server. 12 | */ 13 | public class BingBingWahooApi { 14 | public static boolean isGroundPounding(@Nullable Player player) { 15 | if (player instanceof LocalPlayerExtensions ex) 16 | return ex.groundPounding(); 17 | else if (player instanceof ServerPlayerExtensions ex) 18 | return ex.isGroundPounding(); 19 | return false; 20 | } 21 | 22 | public static boolean isDiving(@Nullable Player player) { 23 | if (player instanceof LocalPlayerExtensions ex) 24 | return ex.diving(); 25 | if (player instanceof ServerPlayerExtensions ex) 26 | return ex.isDiving(); 27 | return false; 28 | } 29 | 30 | public static boolean isFlyingCap(@Nullable Entity entity) { 31 | return entity instanceof FlyingCapEntity; 32 | } 33 | 34 | /** 35 | * Only works on client. 36 | */ 37 | public static void startDiving(Player player) { 38 | if (player instanceof LocalPlayerExtensions ex) 39 | ex.startDiving(); 40 | } 41 | 42 | /** 43 | * Only works on client. 44 | */ 45 | public static void stopAllActions(Player player) { 46 | if (player instanceof LocalPlayerExtensions ex) 47 | ex.stopAllActions(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/io/github/ignoramuses/bing_bing_wahoo/packets/CapThrowPacket.java: -------------------------------------------------------------------------------- 1 | package io.github.ignoramuses.bing_bing_wahoo.packets; 2 | 3 | import io.github.ignoramuses.bing_bing_wahoo.BingBingWahoo; 4 | import io.github.ignoramuses.bing_bing_wahoo.compat.TrinketsCompat; 5 | import io.github.ignoramuses.bing_bing_wahoo.content.cap.FlyingCapEntity; 6 | import io.github.ignoramuses.bing_bing_wahoo.content.cap.PreferredCapSlot; 7 | import net.fabricmc.api.EnvType; 8 | import net.fabricmc.api.Environment; 9 | import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; 10 | import net.fabricmc.fabric.api.networking.v1.PacketByteBufs; 11 | import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; 12 | import net.minecraft.network.FriendlyByteBuf; 13 | import net.minecraft.resources.ResourceLocation; 14 | import net.minecraft.world.entity.EquipmentSlot; 15 | import net.minecraft.world.item.ItemStack; 16 | 17 | public class CapThrowPacket { 18 | public static final ResourceLocation ID = BingBingWahoo.id("cap_throw"); 19 | 20 | public static void init() { 21 | ServerPlayNetworking.registerGlobalReceiver(ID, (server, player, handler, buf, responseSender) -> { 22 | boolean fromTrinketSlot = buf.readBoolean(); 23 | server.execute(() -> { 24 | ItemStack cap = fromTrinketSlot ? TrinketsCompat.getCapTrinketStack(player) : player.getItemBySlot(EquipmentSlot.HEAD); 25 | FlyingCapEntity.spawn(player, cap, fromTrinketSlot ? PreferredCapSlot.TRINKETS : PreferredCapSlot.HEAD); 26 | }); 27 | }); 28 | } 29 | 30 | @Environment(EnvType.CLIENT) 31 | public static void send(boolean thrownFromTrinketSlot) { 32 | FriendlyByteBuf buf = PacketByteBufs.create(); 33 | buf.writeBoolean(thrownFromTrinketSlot); 34 | ClientPlayNetworking.send(ID, buf); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/io/github/ignoramuses/bing_bing_wahoo/mixin/HumanoidArmorLayerMixin.java: -------------------------------------------------------------------------------- 1 | package io.github.ignoramuses.bing_bing_wahoo.mixin; 2 | 3 | import com.mojang.blaze3d.vertex.PoseStack; 4 | import net.fabricmc.api.EnvType; 5 | import net.fabricmc.api.Environment; 6 | import io.github.ignoramuses.bing_bing_wahoo.WahooRegistry; 7 | import net.minecraft.client.model.HumanoidModel; 8 | import net.minecraft.client.renderer.MultiBufferSource; 9 | import net.minecraft.client.renderer.entity.RenderLayerParent; 10 | import net.minecraft.client.renderer.entity.layers.HumanoidArmorLayer; 11 | import net.minecraft.client.renderer.entity.layers.RenderLayer; 12 | import net.minecraft.world.entity.EquipmentSlot; 13 | import net.minecraft.world.entity.LivingEntity; 14 | import net.minecraft.world.item.ItemStack; 15 | import org.spongepowered.asm.mixin.Mixin; 16 | import org.spongepowered.asm.mixin.injection.At; 17 | import org.spongepowered.asm.mixin.injection.Inject; 18 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 19 | 20 | @Environment(EnvType.CLIENT) 21 | @Mixin(HumanoidArmorLayer.class) 22 | public abstract class HumanoidArmorLayerMixin, A extends HumanoidModel> extends RenderLayer { 23 | public HumanoidArmorLayerMixin(RenderLayerParent context) { 24 | super(context); 25 | } 26 | 27 | @Inject(at = @At("HEAD"), method = "renderArmorPiece", cancellable = true) 28 | private void renderArmorPiece(PoseStack matrices, MultiBufferSource vertexConsumers, T entity, EquipmentSlot armorSlot, int light, A model, CallbackInfo ci) { 29 | if (armorSlot == EquipmentSlot.HEAD) { 30 | ItemStack itemStack = entity.getItemBySlot(armorSlot); 31 | if (itemStack.is(WahooRegistry.MYSTERIOUS_CAP)) { 32 | ci.cancel(); 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/io/github/ignoramuses/bing_bing_wahoo/content/cap/FlyingCapRenderer.java: -------------------------------------------------------------------------------- 1 | package io.github.ignoramuses.bing_bing_wahoo.content.cap; 2 | 3 | import com.mojang.blaze3d.vertex.PoseStack; 4 | import com.mojang.math.Axis; 5 | import io.github.ignoramuses.bing_bing_wahoo.BingBingWahoo; 6 | import io.github.ignoramuses.bing_bing_wahoo.WahooUtils; 7 | import net.minecraft.client.renderer.MultiBufferSource; 8 | import net.minecraft.client.renderer.entity.EntityRenderer; 9 | import net.minecraft.client.renderer.entity.EntityRendererProvider.Context; 10 | import net.minecraft.resources.ResourceLocation; 11 | import net.minecraft.world.item.ItemStack; 12 | 13 | public class FlyingCapRenderer extends EntityRenderer { 14 | public static final ResourceLocation TEXTURE = new ResourceLocation(BingBingWahoo.ID, "textures/armor/mysterious_cap.png"); 15 | 16 | private final MysteriousCapModel model; 17 | 18 | public FlyingCapRenderer(Context context) { 19 | super(context); 20 | this.model = new MysteriousCapModel(context, null); 21 | } 22 | 23 | @Override 24 | public void render(FlyingCapEntity entity, float yaw, float tickDelta, PoseStack matrices, MultiBufferSource vertexConsumers, int light) { 25 | super.render(entity, yaw, tickDelta, matrices, vertexConsumers, light); 26 | ItemStack stack = entity.getItem(); 27 | if (stack == null || stack.isEmpty()) return; 28 | matrices.pushPose(); 29 | matrices.mulPose(Axis.ZP.rotationDegrees(180)); 30 | matrices.translate(0, -1.55, 0); 31 | float rotation = (entity.tickCount + tickDelta) * 50; 32 | matrices.mulPose(Axis.YP.rotationDegrees(rotation)); 33 | // if (entity.ticksAtEnd > 0 && entity.ticksAtEnd <= 10) matrices.scale(2.5f, 1, 2.5f); // larger surface to jump on 34 | WahooUtils.renderCap(matrices, vertexConsumers, stack, light, tickDelta, model); 35 | matrices.popPose(); 36 | } 37 | 38 | @Override 39 | public ResourceLocation getTextureLocation(FlyingCapEntity entity) { 40 | return TEXTURE; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/resources/fabric.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "id": "bingbingwahoo", 4 | "version": "${version}", 5 | 6 | "name": "Bing Bing Wahoo", 7 | "description": "Bringing the physics of Mario to Minecraft, one wahoo at a time.\nMade for ModFest 1.17.", 8 | "authors": [ 9 | "Tropheus Jay", 10 | "InsaneASockJr" 11 | ], 12 | "contact": { 13 | "sources": "https://github.com/Ignoramuses/bing-bing-wahoo" 14 | }, 15 | 16 | "license": "MIT", 17 | "icon": "assets/bingbingwahoo/icon.png", 18 | 19 | "environment": "*", 20 | "entrypoints": { 21 | "main": [ 22 | "io.github.ignoramuses.bing_bing_wahoo.BingBingWahoo" 23 | ], 24 | "client": [ 25 | "io.github.ignoramuses.bing_bing_wahoo.BingBingWahooClient" 26 | ] 27 | }, 28 | "mixins": [ 29 | "bingbingwahoo.mixins.json" 30 | ], 31 | 32 | "depends": { 33 | "fabricloader": ">=${loader_version}", 34 | "fabric": ">=${fabric_version}", 35 | "minecraft": ">=${minecraft_version}", 36 | "midnightlib": ">=${midnightlib_version}" 37 | }, 38 | "recommends": { 39 | "modmenu": "*" 40 | }, 41 | "custom": { 42 | "loom:injected_interfaces": { 43 | "net/minecraft/class_3222": [ 44 | "io/github/ignoramuses/bing_bing_wahoo/extensions/PlayerExtensions", 45 | "io/github/ignoramuses/bing_bing_wahoo/extensions/ServerPlayerExtensions" 46 | ], 47 | "net/minecraft/class_746": [ 48 | "io/github/ignoramuses/bing_bing_wahoo/extensions/AbstractClientPlayerExtensions", 49 | "io/github/ignoramuses/bing_bing_wahoo/extensions/LocalPlayerExtensions", 50 | "io/github/ignoramuses/bing_bing_wahoo/extensions/PlayerExtensions" 51 | ], 52 | "net/minecraft/class_745": [ 53 | "io/github/ignoramuses/bing_bing_wahoo/extensions/AbstractClientPlayerExtensions" 54 | ], 55 | "net/minecraft/class_743": [ 56 | "io/github/ignoramuses/bing_bing_wahoo/extensions/KeyboardInputExtensions" 57 | ] 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/io/github/ignoramuses/bing_bing_wahoo/packets/CapSpawnPacket.java: -------------------------------------------------------------------------------- 1 | package io.github.ignoramuses.bing_bing_wahoo.packets; 2 | 3 | import io.github.ignoramuses.bing_bing_wahoo.BingBingWahoo; 4 | import io.github.ignoramuses.bing_bing_wahoo.WahooNetworking; 5 | import io.github.ignoramuses.bing_bing_wahoo.content.cap.FlyingCapEntity; 6 | import net.fabricmc.api.EnvType; 7 | import net.fabricmc.api.Environment; 8 | import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; 9 | import net.fabricmc.fabric.api.networking.v1.PacketByteBufs; 10 | import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; 11 | import net.minecraft.nbt.CompoundTag; 12 | import net.minecraft.network.FriendlyByteBuf; 13 | import net.minecraft.network.protocol.Packet; 14 | import net.minecraft.network.protocol.game.ClientGamePacketListener; 15 | import net.minecraft.network.protocol.game.ClientboundAddEntityPacket; 16 | import net.minecraft.resources.ResourceLocation; 17 | 18 | public class CapSpawnPacket { 19 | public static final ResourceLocation ID = BingBingWahoo.id("cap_entity_spawn"); 20 | 21 | public static Packet makePacket(FlyingCapEntity entity) { 22 | FriendlyByteBuf buf = PacketByteBufs.create(); 23 | new ClientboundAddEntityPacket(entity).write(buf); 24 | CompoundTag data = new CompoundTag(); 25 | entity.addAdditionalSaveData(data); 26 | buf.writeNbt(data); 27 | return ServerPlayNetworking.createS2CPacket(ID, buf); 28 | } 29 | 30 | @Environment(EnvType.CLIENT) 31 | public static void clientInit() { 32 | ClientPlayNetworking.registerGlobalReceiver(ID, (client, handler, buf, sender) -> { 33 | ClientboundAddEntityPacket addPacket = new ClientboundAddEntityPacket(buf); 34 | CompoundTag data = buf.readNbt(); 35 | client.execute(() -> { 36 | addPacket.handle(handler); 37 | FlyingCapEntity cap = (FlyingCapEntity) client.level.getEntity(addPacket.getId()); 38 | if (cap != null) { 39 | cap.readAdditionalSaveData(data); 40 | } 41 | }); 42 | }); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/io/github/ignoramuses/bing_bing_wahoo/content/cap/MysteriousCapFeatureRenderer.java: -------------------------------------------------------------------------------- 1 | package io.github.ignoramuses.bing_bing_wahoo.content.cap; 2 | 3 | import com.mojang.blaze3d.vertex.PoseStack; 4 | import net.fabricmc.api.EnvType; 5 | import net.fabricmc.api.Environment; 6 | import io.github.ignoramuses.bing_bing_wahoo.WahooUtils; 7 | import net.minecraft.client.model.EntityModel; 8 | import net.minecraft.client.model.HumanoidModel; 9 | import net.minecraft.client.renderer.MultiBufferSource; 10 | import net.minecraft.client.renderer.entity.RenderLayerParent; 11 | import net.minecraft.client.renderer.entity.layers.RenderLayer; 12 | import net.minecraft.world.entity.Entity; 13 | import net.minecraft.world.entity.monster.ZombifiedPiglin; 14 | import net.minecraft.world.entity.monster.piglin.AbstractPiglin; 15 | import net.minecraft.world.item.ItemStack; 16 | 17 | @Environment(EnvType.CLIENT) 18 | public class MysteriousCapFeatureRenderer> extends RenderLayer { 19 | private final MysteriousCapModel model; 20 | 21 | public MysteriousCapFeatureRenderer(RenderLayerParent context, MysteriousCapModel model) { 22 | super(context); 23 | this.model = model; 24 | } 25 | 26 | @Override 27 | public void render(PoseStack matrices, MultiBufferSource vertexConsumers, int light, T entity, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch) { 28 | if (model.wearerHead == null || !(entity instanceof CapWearer wearer) || !wearer.isWearingCap()) return; 29 | ItemStack cap = wearer.getCap(); 30 | if (cap.hasTag() && cap.getTag().contains("wahoo:skip_render")) { 31 | return; 32 | } 33 | matrices.pushPose(); 34 | model.wearerHead.translateAndRotate(matrices); 35 | if (model.wearerModel instanceof HumanoidModel) matrices.translate(0, -1.8, -0.1); 36 | if (entity instanceof AbstractPiglin || entity instanceof ZombifiedPiglin) 37 | matrices.scale(1.2f, 1, 1); 38 | WahooUtils.renderCap(matrices, vertexConsumers, wearer.getCap(), light, tickDelta, model); 39 | matrices.popPose(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/io/github/ignoramuses/bing_bing_wahoo/content/cap/PreferredCapSlot.java: -------------------------------------------------------------------------------- 1 | package io.github.ignoramuses.bing_bing_wahoo.content.cap; 2 | 3 | import io.github.ignoramuses.bing_bing_wahoo.compat.TrinketsCompat; 4 | import net.minecraft.world.InteractionHand; 5 | import net.minecraft.world.entity.EquipmentSlot; 6 | import net.minecraft.world.entity.LivingEntity; 7 | import net.minecraft.world.item.ItemStack; 8 | 9 | public enum PreferredCapSlot { 10 | // priority order 11 | TRINKETS { 12 | @Override 13 | public boolean shouldEquip(LivingEntity entity, ItemStack stack) { 14 | return TrinketsCompat.getCapTrinketStack(entity) == null; 15 | } 16 | 17 | @Override 18 | public void equip(LivingEntity entity, ItemStack stack) { 19 | TrinketsCompat.equipInHatTrinketSlot(entity, stack); 20 | } 21 | }, 22 | HEAD { 23 | @Override 24 | public boolean shouldEquip(LivingEntity entity, ItemStack stack) { 25 | return entity.getItemBySlot(EquipmentSlot.HEAD).isEmpty(); 26 | } 27 | 28 | @Override 29 | public void equip(LivingEntity entity, ItemStack stack) { 30 | entity.setItemSlot(EquipmentSlot.HEAD, stack); 31 | } 32 | }, 33 | MAIN_HAND { 34 | @Override 35 | public boolean shouldEquip(LivingEntity entity, ItemStack stack) { 36 | return entity.getItemInHand(InteractionHand.MAIN_HAND).isEmpty(); 37 | } 38 | 39 | @Override 40 | public void equip(LivingEntity entity, ItemStack stack) { 41 | entity.setItemInHand(InteractionHand.MAIN_HAND, stack); 42 | } 43 | }, 44 | OFFHAND { 45 | @Override 46 | public boolean shouldEquip(LivingEntity entity, ItemStack stack) { 47 | return entity.getItemInHand(InteractionHand.OFF_HAND).isEmpty(); 48 | } 49 | 50 | @Override 51 | public void equip(LivingEntity entity, ItemStack stack) { 52 | entity.setItemInHand(InteractionHand.OFF_HAND, stack); 53 | } 54 | }; 55 | 56 | public abstract boolean shouldEquip(LivingEntity entity, ItemStack stack); 57 | public abstract void equip(LivingEntity entity, ItemStack stack); 58 | 59 | public static PreferredCapSlot fromHand(InteractionHand hand) { 60 | return hand == InteractionHand.MAIN_HAND ? MAIN_HAND : OFFHAND; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/io/github/ignoramuses/bing_bing_wahoo/BingBingWahooConfig.java: -------------------------------------------------------------------------------- 1 | package io.github.ignoramuses.bing_bing_wahoo; 2 | 3 | import eu.midnightdust.lib.config.MidnightConfig; 4 | import io.github.ignoramuses.bing_bing_wahoo.content.movement.GroundPoundType; 5 | 6 | public class BingBingWahooConfig extends MidnightConfig { 7 | @Comment(centered = true) 8 | public static Comment bljComment0, bljComment1; 9 | @Entry 10 | public static boolean blj = true; 11 | 12 | @Comment(centered = true) 13 | public static Comment spacer0, rapidFireLongJumpsComment0, rapidFireLongJumpsComment1, rapidFireLongJumpsComment2; 14 | @Entry 15 | public static boolean rapidFireLongJumps = false; 16 | 17 | @Comment(centered = true) 18 | public static Comment spacer1, allowNormalWallJumpsComment; 19 | @Entry 20 | public static boolean allowNormalWallJumps = false; 21 | 22 | @Comment(centered = true) 23 | public static Comment spacer2, backFlipsComment; 24 | @Entry 25 | public static boolean backFlips = true; 26 | 27 | @Comment(centered = true) 28 | public static Comment spacer3, groundedDivesComment; 29 | @Entry 30 | public static boolean groundedDives = true; 31 | 32 | @Comment(centered = true) 33 | public static Comment spacer4, bonkingComment; 34 | @Entry 35 | public static boolean bonking = true; 36 | 37 | @Comment(centered = true) 38 | public static Comment spacer5, groundPoundTypeComment0, groundPoundTypeComment1, groundPoundTypeComment2, groundPoundTypeComment3, groundPoundTypeComment4; 39 | @Entry 40 | public static GroundPoundType groundPoundType = GroundPoundType.DESTRUCTIVE; 41 | 42 | @Comment(centered = true) 43 | public static Comment spacer6, maxLongJumpSpeedComment0, maxLongJumpSpeedComment1; 44 | @Entry(min = 0) 45 | public static double maxLongJumpSpeed = 1.5; 46 | 47 | @Comment(centered = true) 48 | public static Comment spacer7, longJumpSpeedMultiplierComment0, longJumpSpeedMultiplierComment1; 49 | @Entry(min = 0) 50 | public static double longJumpSpeedMultiplier = 10; 51 | 52 | @Comment(centered = true) 53 | public static Comment spacer8, flipSpeedMultiplierComment0, flipSpeedMultiplierComment1; 54 | @Entry(min = 0) 55 | public static float flipSpeedMultiplier = 1; 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/io/github/ignoramuses/bing_bing_wahoo/compat/TrinketsCompat.java: -------------------------------------------------------------------------------- 1 | package io.github.ignoramuses.bing_bing_wahoo.compat; 2 | 3 | import dev.emi.trinkets.api.SlotReference; 4 | import dev.emi.trinkets.api.TrinketComponent; 5 | import dev.emi.trinkets.api.TrinketInventory; 6 | import dev.emi.trinkets.api.TrinketsApi; 7 | import net.fabricmc.loader.api.FabricLoader; 8 | import net.minecraft.util.Tuple; 9 | import net.minecraft.world.entity.LivingEntity; 10 | import net.minecraft.world.entity.player.Player; 11 | import net.minecraft.world.item.ItemStack; 12 | import org.jetbrains.annotations.Nullable; 13 | 14 | import java.util.Optional; 15 | 16 | import static io.github.ignoramuses.bing_bing_wahoo.WahooRegistry.MYSTERIOUS_CAP; 17 | 18 | public class TrinketsCompat { 19 | public static final boolean TRINKETS_LOADED = FabricLoader.getInstance().isModLoaded("trinkets"); 20 | 21 | public static boolean capTrinketEquipped(Player player) { 22 | if (!TRINKETS_LOADED) { 23 | return false; 24 | } 25 | Optional component = TrinketsApi.getTrinketComponent(player); 26 | return component.map(trinketComponent -> trinketComponent.isEquipped(MYSTERIOUS_CAP)).orElse(false); 27 | } 28 | 29 | @Nullable 30 | public static ItemStack getCapTrinketStack(LivingEntity entity) { 31 | if (!TRINKETS_LOADED) { 32 | return null; 33 | } 34 | Optional component = TrinketsApi.getTrinketComponent(entity); 35 | if (component.isPresent()) { 36 | for (Tuple pair : component.get().getEquipped(MYSTERIOUS_CAP)) { 37 | return pair.getB(); 38 | } 39 | } 40 | return null; 41 | } 42 | 43 | public static void equipInHatTrinketSlot(LivingEntity entity, ItemStack stack) { 44 | if (!TRINKETS_LOADED) { 45 | return; 46 | } 47 | Optional component = TrinketsApi.getTrinketComponent(entity); 48 | if (component.isPresent()) { 49 | for (Tuple pair : component.get().getEquipped(item -> true)) { 50 | SlotReference slot = pair.getA(); 51 | TrinketInventory inv = slot.inventory(); 52 | if (inv.getSlotType().getName().equals("hat")) { 53 | inv.setItem(slot.index(), stack); 54 | } 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/io/github/ignoramuses/bing_bing_wahoo/compat/TemplatesCompat.java: -------------------------------------------------------------------------------- 1 | package io.github.ignoramuses.bing_bing_wahoo.compat; 2 | 3 | import io.github.cottonmc.templates.block.TemplateSlopeBlock; 4 | import io.github.cottonmc.templates.util.Edge; 5 | import io.github.ignoramuses.bing_bing_wahoo.synced_config.SyncedConfig; 6 | import net.minecraft.core.Direction; 7 | import net.minecraft.world.level.block.state.BlockState; 8 | 9 | import java.util.Map; 10 | import java.util.Set; 11 | import java.util.function.Supplier; 12 | 13 | public class TemplatesCompat { 14 | private static final Set down = tryMake(() -> Set.of( 15 | Edge.DOWN_EAST, Edge.DOWN_WEST, Edge.DOWN_NORTH, Edge.DOWN_SOUTH 16 | )); 17 | private static final Set up = tryMake(() -> Set.of( 18 | Edge.UP_EAST, Edge.UP_WEST, Edge.UP_NORTH, Edge.UP_SOUTH 19 | )); 20 | // directions inverted, facing is opposite of push direction 21 | private static final Map edgeToFacing = tryMake(() -> Map.of( 22 | Edge.DOWN_EAST, Direction.WEST, 23 | Edge.DOWN_WEST, Direction.EAST, 24 | Edge.DOWN_NORTH, Direction.SOUTH, 25 | Edge.DOWN_SOUTH, Direction.NORTH, 26 | 27 | Edge.UP_EAST, Direction.WEST, 28 | Edge.UP_WEST, Direction.EAST, 29 | Edge.UP_NORTH, Direction.SOUTH, 30 | Edge.UP_SOUTH, Direction.NORTH 31 | )); 32 | 33 | // true when all necessary classes and fields are present 34 | public static final boolean IS_LOADED = down != null && up != null && edgeToFacing != null && tryMake(() -> TemplateSlopeBlock.EDGE) != null; 35 | 36 | public static Direction getSlideDirection(BlockState slope) { 37 | if (slope.hasProperty(TemplateSlopeBlock.EDGE) && canSlideOnSlope(slope)) { 38 | Edge edge = slope.getValue(TemplateSlopeBlock.EDGE); 39 | return edgeToFacing.get(edge); 40 | } 41 | return null; 42 | } 43 | 44 | private static boolean canSlideOnSlope(BlockState slope) { 45 | Edge edge = slope.getValue(TemplateSlopeBlock.EDGE); 46 | if (down.contains(edge)) { 47 | return true; // bottom half slopes always work 48 | } else if (SyncedConfig.CONVEYOR_GLITCH.get()) { 49 | return up.contains(edge); // only use top half slopes when conveyor glitch is enabled 50 | } 51 | return false; // some non-slope edge 52 | } 53 | 54 | private static T tryMake(Supplier supplier) { 55 | try { 56 | return supplier.get(); 57 | } catch (Throwable t) { 58 | return null; 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/io/github/ignoramuses/bing_bing_wahoo/packets/UpdateSyncedBooleanPacket.java: -------------------------------------------------------------------------------- 1 | package io.github.ignoramuses.bing_bing_wahoo.packets; 2 | 3 | import io.github.ignoramuses.bing_bing_wahoo.BingBingWahoo; 4 | import io.github.ignoramuses.bing_bing_wahoo.synced_config.SyncedConfig; 5 | import net.fabricmc.api.EnvType; 6 | import net.fabricmc.api.Environment; 7 | import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; 8 | import net.fabricmc.fabric.api.networking.v1.PacketByteBufs; 9 | import net.fabricmc.fabric.api.networking.v1.PacketSender; 10 | import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; 11 | import net.minecraft.network.FriendlyByteBuf; 12 | import net.minecraft.resources.ResourceLocation; 13 | import net.minecraft.server.MinecraftServer; 14 | import net.minecraft.server.level.ServerPlayer; 15 | import net.minecraft.world.level.GameRules; 16 | import net.minecraft.world.level.GameRules.BooleanValue; 17 | 18 | import java.util.List; 19 | 20 | public class UpdateSyncedBooleanPacket { 21 | public static final ResourceLocation ID = BingBingWahoo.id("update_boolean_gamerule"); 22 | 23 | public static void sendToAll(String ruleName, MinecraftServer server, BooleanValue value) { 24 | List players = server.getPlayerList().getPlayers(); 25 | FriendlyByteBuf buf = makeBuf(ruleName, value.get()); 26 | for (ServerPlayer player : players) { 27 | ServerPlayNetworking.send(player, ID, buf); 28 | } 29 | } 30 | 31 | public static void send(PacketSender sender, SyncedConfig config, GameRules rules) { 32 | FriendlyByteBuf buf = makeBuf(config.name, rules.getBoolean(config.ruleKey)); 33 | sender.sendPacket(ID, buf); 34 | } 35 | 36 | private static FriendlyByteBuf makeBuf(String ruleName, boolean value) { 37 | FriendlyByteBuf buf = PacketByteBufs.create(); 38 | buf.writeUtf(ruleName); 39 | buf.writeBoolean(value); 40 | return buf; 41 | } 42 | 43 | @Environment(EnvType.CLIENT) 44 | public static void initClient() { 45 | ClientPlayNetworking.registerGlobalReceiver(ID, (client, handler, buf, responseSender) -> { 46 | String name = buf.readUtf(); 47 | boolean value = buf.readBoolean(); 48 | client.execute(() -> { 49 | SyncedConfig config = SyncedConfig.BOOLEAN_CONFIGS.get(name); 50 | if (config == null) { 51 | BingBingWahoo.LOGGER.error("Unknown synced config: " + name); 52 | } else { 53 | config.currentRuleValue = value; 54 | } 55 | }); 56 | }); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/io/github/ignoramuses/bing_bing_wahoo/packets/UpdateFlipStatePacket.java: -------------------------------------------------------------------------------- 1 | package io.github.ignoramuses.bing_bing_wahoo.packets; 2 | 3 | import io.github.ignoramuses.bing_bing_wahoo.BingBingWahoo; 4 | import io.github.ignoramuses.bing_bing_wahoo.content.movement.FlipState; 5 | import io.github.ignoramuses.bing_bing_wahoo.extensions.AbstractClientPlayerExtensions; 6 | import net.fabricmc.api.EnvType; 7 | import net.fabricmc.api.Environment; 8 | import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; 9 | import net.fabricmc.fabric.api.networking.v1.PacketByteBufs; 10 | import net.fabricmc.fabric.api.networking.v1.PlayerLookup; 11 | import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; 12 | import net.minecraft.client.player.AbstractClientPlayer; 13 | import net.minecraft.network.FriendlyByteBuf; 14 | import net.minecraft.resources.ResourceLocation; 15 | import net.minecraft.server.level.ServerPlayer; 16 | 17 | import java.util.UUID; 18 | 19 | public class UpdateFlipStatePacket { 20 | public static final ResourceLocation ID = BingBingWahoo.id("update_flip"); 21 | 22 | public static void init() { 23 | ServerPlayNetworking.registerGlobalReceiver(ID, (server, player, handler, buf, responseSender) -> { 24 | FlipState state = buf.readEnum(FlipState.class); 25 | server.execute(() -> { 26 | UUID id = player.getGameProfile().getId(); 27 | for (ServerPlayer tracker : PlayerLookup.tracking(player)) { 28 | if (tracker != player) { 29 | FriendlyByteBuf buffer = PacketByteBufs.create(); 30 | buffer.writeEnum(state); 31 | buffer.writeUUID(id); 32 | ServerPlayNetworking.send(tracker, ID, buffer); 33 | } 34 | } 35 | }); 36 | }); 37 | } 38 | 39 | @Environment(EnvType.CLIENT) 40 | public static void initClient() { 41 | ClientPlayNetworking.registerGlobalReceiver(ID, (client, handler, buf, responseSender) -> { 42 | FlipState state = buf.readEnum(FlipState.class); 43 | UUID id = buf.readUUID(); 44 | client.execute(() -> { 45 | for (AbstractClientPlayer player : client.level.players()) { 46 | if (player instanceof AbstractClientPlayerExtensions ex && player.getGameProfile().getId().equals(id)) { 47 | ex.setFlipState(state); 48 | break; 49 | } 50 | } 51 | }); 52 | }); 53 | } 54 | 55 | @Environment(EnvType.CLIENT) 56 | public static void send(FlipState newState) { 57 | FriendlyByteBuf buf = PacketByteBufs.create() 58 | .writeEnum(newState); 59 | ClientPlayNetworking.send(ID, buf); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/io/github/ignoramuses/bing_bing_wahoo/packets/UpdateSyncedDoublePacket.java: -------------------------------------------------------------------------------- 1 | package io.github.ignoramuses.bing_bing_wahoo.packets; 2 | 3 | import io.github.ignoramuses.bing_bing_wahoo.BingBingWahoo; 4 | import io.github.ignoramuses.bing_bing_wahoo.synced_config.SyncedConfig; 5 | import net.fabricmc.api.EnvType; 6 | import net.fabricmc.api.Environment; 7 | import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; 8 | import net.fabricmc.fabric.api.gamerule.v1.rule.DoubleRule; 9 | import net.fabricmc.fabric.api.networking.v1.PacketByteBufs; 10 | import net.fabricmc.fabric.api.networking.v1.PacketSender; 11 | import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; 12 | import net.minecraft.network.FriendlyByteBuf; 13 | import net.minecraft.resources.ResourceLocation; 14 | import net.minecraft.server.MinecraftServer; 15 | import net.minecraft.server.level.ServerPlayer; 16 | import net.minecraft.world.level.GameRules; 17 | import net.minecraft.world.level.GameRules.BooleanValue; 18 | 19 | import java.util.List; 20 | 21 | public class UpdateSyncedDoublePacket { 22 | public static final ResourceLocation ID = BingBingWahoo.id("update_double_gamerule"); 23 | 24 | public static void sendToAll(String ruleName, MinecraftServer server, DoubleRule value) { 25 | List players = server.getPlayerList().getPlayers(); 26 | FriendlyByteBuf buf = makeBuf(ruleName, value.get()); 27 | for (ServerPlayer player : players) { 28 | ServerPlayNetworking.send(player, ID, buf); 29 | } 30 | } 31 | 32 | public static void send(PacketSender sender, SyncedConfig config, GameRules rules) { 33 | FriendlyByteBuf buf = makeBuf(config.name, rules.getRule(config.ruleKey).get()); 34 | sender.sendPacket(ID, buf); 35 | } 36 | 37 | private static FriendlyByteBuf makeBuf(String ruleName, double value) { 38 | FriendlyByteBuf buf = PacketByteBufs.create(); 39 | buf.writeUtf(ruleName); 40 | buf.writeDouble(value); 41 | return buf; 42 | } 43 | 44 | @Environment(EnvType.CLIENT) 45 | public static void initClient() { 46 | ClientPlayNetworking.registerGlobalReceiver(ID, (client, handler, buf, responseSender) -> { 47 | String name = buf.readUtf(); 48 | double value = buf.readDouble(); 49 | client.execute(() -> { 50 | SyncedConfig config = SyncedConfig.DOUBLE_CONFIGS.get(name); 51 | if (config == null) { 52 | BingBingWahoo.LOGGER.error("Unknown synced config: " + name); 53 | } else { 54 | config.currentRuleValue = value; 55 | } 56 | }); 57 | }); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/io/github/ignoramuses/bing_bing_wahoo/mixin/LivingEntityMixin.java: -------------------------------------------------------------------------------- 1 | package io.github.ignoramuses.bing_bing_wahoo.mixin; 2 | 3 | import io.github.ignoramuses.bing_bing_wahoo.content.cap.CapWearer; 4 | import io.github.ignoramuses.bing_bing_wahoo.compat.TrinketsCompat; 5 | import net.minecraft.world.damagesource.DamageSource; 6 | import net.minecraft.world.entity.Entity; 7 | import net.minecraft.world.entity.EntityType; 8 | import net.minecraft.world.entity.EquipmentSlot; 9 | import net.minecraft.world.entity.LivingEntity; 10 | import net.minecraft.world.entity.player.Player; 11 | import net.minecraft.world.item.ItemStack; 12 | import net.minecraft.world.level.Level; 13 | import org.spongepowered.asm.mixin.Mixin; 14 | import org.spongepowered.asm.mixin.Shadow; 15 | import org.spongepowered.asm.mixin.injection.At; 16 | import org.spongepowered.asm.mixin.injection.Inject; 17 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 18 | 19 | import static io.github.ignoramuses.bing_bing_wahoo.WahooRegistry.MYSTERIOUS_CAP; 20 | 21 | @Mixin(LivingEntity.class) 22 | public abstract class LivingEntityMixin extends Entity implements CapWearer { 23 | 24 | @Shadow 25 | public abstract void setItemSlot(EquipmentSlot slot, ItemStack stack); 26 | 27 | @Shadow 28 | public abstract ItemStack getItemBySlot(EquipmentSlot slot); 29 | 30 | public LivingEntityMixin(EntityType type, Level world) { 31 | super(type, world); 32 | } 33 | 34 | @Override 35 | public boolean isWearingCap() { 36 | ItemStack head = getItemBySlot(EquipmentSlot.HEAD); 37 | if ((head == null || head.isEmpty() || !head.is(MYSTERIOUS_CAP))) { 38 | head = TrinketsCompat.getCapTrinketStack((LivingEntity) (Object) this); 39 | } 40 | return head != null && head.is(MYSTERIOUS_CAP); 41 | } 42 | 43 | @Override 44 | public ItemStack getCap() { 45 | ItemStack head = getItemBySlot(EquipmentSlot.HEAD); 46 | if ((head == null || head.isEmpty() || !head.is(MYSTERIOUS_CAP))) { 47 | head = TrinketsCompat.getCapTrinketStack((LivingEntity) (Object) this); 48 | } 49 | return head != null ? head : ItemStack.EMPTY; 50 | } 51 | 52 | @Inject(at = @At("HEAD"), method = {"hurtHelmet", "hurtArmor"}) 53 | private void damageCap(DamageSource source, float amount, CallbackInfo ci) { 54 | if (isWearingCap()) { 55 | getCap().hurtAndBreak((int) amount, (LivingEntity) (Object) this, (entity) -> { 56 | if (entity instanceof Player player) { 57 | player.broadcastBreakEvent(EquipmentSlot.byTypeAndIndex(EquipmentSlot.Type.ARMOR, 3)); // 3: head slot index 58 | } 59 | entity.setItemSlot(EquipmentSlot.HEAD, ItemStack.EMPTY); 60 | }); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/resources/data/bingbingwahoo/tags/blocks/standard_slopes.json: -------------------------------------------------------------------------------- 1 | { 2 | "values": [ 3 | { 4 | "id": "automobility:slope", 5 | "required": false 6 | }, 7 | { 8 | "id": "automobility:steep_slope", 9 | "required": false 10 | }, 11 | { 12 | "id": "automobility:slope_with_dash_panel", 13 | "required": false 14 | }, 15 | { 16 | "id": "automobility:steep_slope_with_dash_panel", 17 | "required": false 18 | }, 19 | 20 | 21 | 22 | { 23 | "id": "conventional_cubes:celestite_brick_slope", 24 | "required": false 25 | }, 26 | { 27 | "id": "conventional_cubes:celestite_smooth_slope", 28 | "required": false 29 | }, 30 | { 31 | "id": "conventional_cubes:celestite_checker_slope", 32 | "required": false 33 | }, 34 | 35 | 36 | { 37 | "id": "conventional_cubes:dolomite_brick_slope", 38 | "required": false 39 | }, 40 | { 41 | "id": "conventional_cubes:dolomite_smooth_slope", 42 | "required": false 43 | }, 44 | { 45 | "id": "conventional_cubes:dolomite_checker_slope", 46 | "required": false 47 | }, 48 | 49 | 50 | { 51 | "id": "conventional_cubes:peridot_brick_slope", 52 | "required": false 53 | }, 54 | { 55 | "id": "conventional_cubes:peridot_rock_slope", 56 | "required": false 57 | }, 58 | { 59 | "id": "conventional_cubes:peridot_checker_slope", 60 | "required": false 61 | }, 62 | 63 | 64 | { 65 | "id": "conventional_cubes:sanic_white_tiles_slope", 66 | "required": false 67 | }, 68 | { 69 | "id": "conventional_cubes:sanic_bright_tiles_slope", 70 | "required": false 71 | }, 72 | { 73 | "id": "conventional_cubes:sanic_medium_tiles_slope", 74 | "required": false 75 | }, 76 | { 77 | "id": "conventional_cubes:sanic_dark_tiles_slope", 78 | "required": false 79 | }, 80 | { 81 | "id": "conventional_cubes:sanic_darkest_tiles_slope", 82 | "required": false 83 | }, 84 | 85 | 86 | { 87 | "id": "conventional_cubes:sanic_oil_checker_slope", 88 | "required": false 89 | }, 90 | 91 | 92 | { 93 | "id": "conventional_cubes:figaro_roof_slate_slope", 94 | "required": false 95 | }, 96 | { 97 | "id": "conventional_cubes:figaro_zozo_slope", 98 | "required": false 99 | }, 100 | { 101 | "id": "conventional_cubes:figaro_bright_canal_slope", 102 | "required": false 103 | } 104 | ] 105 | } -------------------------------------------------------------------------------- /src/main/java/io/github/ignoramuses/bing_bing_wahoo/BingBingWahooClient.java: -------------------------------------------------------------------------------- 1 | package io.github.ignoramuses.bing_bing_wahoo; 2 | 3 | import io.github.ignoramuses.bing_bing_wahoo.packets.*; 4 | import net.fabricmc.api.ClientModInitializer; 5 | import net.fabricmc.api.EnvType; 6 | import net.fabricmc.api.Environment; 7 | import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; 8 | import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; 9 | import net.fabricmc.fabric.api.client.rendering.v1.ColorProviderRegistry; 10 | import net.fabricmc.fabric.api.client.rendering.v1.EntityModelLayerRegistry; 11 | import net.fabricmc.fabric.api.client.rendering.v1.EntityRendererRegistry; 12 | import io.github.ignoramuses.bing_bing_wahoo.content.cap.FlyingCapRenderer; 13 | import io.github.ignoramuses.bing_bing_wahoo.content.cap.MysteriousCapModel; 14 | import io.github.ignoramuses.bing_bing_wahoo.compat.TrinketsCompat; 15 | import net.minecraft.client.KeyMapping; 16 | import net.minecraft.client.player.LocalPlayer; 17 | import net.minecraft.world.entity.EquipmentSlot; 18 | import org.lwjgl.glfw.GLFW; 19 | 20 | @Environment(EnvType.CLIENT) 21 | public class BingBingWahooClient implements ClientModInitializer { 22 | public static KeyMapping THROW_CAP = KeyBindingHelper.registerKeyBinding( 23 | new KeyMapping("bingbingwahoo.key.throw_cap", GLFW.GLFW_KEY_G, "bingbingwahoo.key.category") 24 | ); 25 | 26 | @Override 27 | public void onInitializeClient() { 28 | CapSpawnPacket.clientInit(); 29 | RequestStopAllActionsPacket.initClient(); 30 | UpdateFlipStatePacket.initClient(); 31 | UpdateSyncedBooleanPacket.initClient(); 32 | UpdateSyncedDoublePacket.initClient(); 33 | 34 | EntityRendererRegistry.register(WahooRegistry.FLYING_CAP, FlyingCapRenderer::new); 35 | EntityModelLayerRegistry.registerModelLayer(MysteriousCapModel.MODEL_LAYER, MysteriousCapModel::getTexturedModelData); 36 | 37 | ColorProviderRegistry.ITEM.register((stack, tintIndex) -> tintIndex == 0 38 | ? WahooRegistry.MYSTERIOUS_CAP.getColor(stack) 39 | : 0xFFFFFF, 40 | WahooRegistry.MYSTERIOUS_CAP 41 | ); 42 | 43 | ClientTickEvents.START_CLIENT_TICK.register(client -> { 44 | while (THROW_CAP.consumeClick()) { 45 | LocalPlayer player = client.player; 46 | if (player != null) { 47 | boolean trinketEquipped = TrinketsCompat.capTrinketEquipped(player); 48 | // trinket takes priority 49 | boolean capEquipped = trinketEquipped || player.getItemBySlot(EquipmentSlot.HEAD).is(WahooRegistry.MYSTERIOUS_CAP); 50 | if (trinketEquipped || capEquipped) { 51 | CapThrowPacket.send(trinketEquipped); 52 | } 53 | } 54 | } 55 | }); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/io/github/ignoramuses/bing_bing_wahoo/mixin/LivingEntityRendererMixin.java: -------------------------------------------------------------------------------- 1 | package io.github.ignoramuses.bing_bing_wahoo.mixin; 2 | 3 | import com.mojang.blaze3d.vertex.PoseStack; 4 | import net.fabricmc.api.EnvType; 5 | import net.fabricmc.api.Environment; 6 | import io.github.ignoramuses.bing_bing_wahoo.content.cap.MysteriousCapFeatureRenderer; 7 | import io.github.ignoramuses.bing_bing_wahoo.content.cap.MysteriousCapModel; 8 | import io.github.ignoramuses.bing_bing_wahoo.extensions.LocalPlayerExtensions; 9 | import net.minecraft.client.model.EntityModel; 10 | import net.minecraft.client.renderer.MultiBufferSource; 11 | import net.minecraft.client.renderer.entity.EntityRenderer; 12 | import net.minecraft.client.renderer.entity.EntityRendererProvider; 13 | import net.minecraft.client.renderer.entity.LivingEntityRenderer; 14 | import net.minecraft.client.renderer.entity.RenderLayerParent; 15 | import net.minecraft.client.renderer.entity.layers.RenderLayer; 16 | import net.minecraft.world.entity.LivingEntity; 17 | import org.spongepowered.asm.mixin.Mixin; 18 | import org.spongepowered.asm.mixin.Shadow; 19 | import org.spongepowered.asm.mixin.injection.At; 20 | import org.spongepowered.asm.mixin.injection.Inject; 21 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 22 | 23 | @Environment(EnvType.CLIENT) 24 | @Mixin(LivingEntityRenderer.class) 25 | public abstract class LivingEntityRendererMixin> extends EntityRenderer implements RenderLayerParent { 26 | @Shadow 27 | protected M model; 28 | 29 | @Shadow 30 | protected abstract boolean addLayer(RenderLayer layer); 31 | 32 | protected LivingEntityRendererMixin(EntityRendererProvider.Context ctx) { 33 | super(ctx); 34 | } 35 | 36 | @Inject(at = @At("RETURN"), method = "") 37 | public void LivingEntityRenderer(EntityRendererProvider.Context ctx, M model, float shadowRadius, CallbackInfo ci) { 38 | addLayer(new MysteriousCapFeatureRenderer<>(this, new MysteriousCapModel(ctx, this.model))); 39 | } 40 | 41 | @Inject( 42 | at = @At(value = "INVOKE", 43 | target = "Lnet/minecraft/client/model/EntityModel;prepareMobModel(Lnet/minecraft/world/entity/Entity;FFF)V" 44 | ), 45 | method = "render(Lnet/minecraft/world/entity/LivingEntity;FFLcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;I)V" 46 | ) 47 | private void render(T livingEntity, float f, float g, PoseStack matrixStack, MultiBufferSource vertexConsumerProvider, int i, CallbackInfo ci) { 48 | if (livingEntity instanceof LocalPlayerExtensions extendedPlayer) { 49 | if (extendedPlayer.slidingOnGround()) { 50 | model.riding = true; 51 | matrixStack.translate(0, 0.65, 0); 52 | } else if (extendedPlayer.slidingOnSlope()) { 53 | model.riding = true; 54 | matrixStack.translate(0, 1, 0); 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /src/main/java/io/github/ignoramuses/bing_bing_wahoo/WahooRegistry.java: -------------------------------------------------------------------------------- 1 | package io.github.ignoramuses.bing_bing_wahoo; 2 | 3 | import net.fabricmc.fabric.api.gamerule.v1.GameRuleFactory; 4 | import net.fabricmc.fabric.api.gamerule.v1.GameRuleRegistry; 5 | import net.fabricmc.fabric.api.item.v1.FabricItemSettings; 6 | import net.fabricmc.fabric.api.itemgroup.v1.FabricItemGroupEntries; 7 | import net.fabricmc.fabric.api.itemgroup.v1.ItemGroupEvents; 8 | import net.fabricmc.fabric.api.object.builder.v1.entity.FabricEntityTypeBuilder; 9 | import io.github.ignoramuses.bing_bing_wahoo.content.SliderRecordItem; 10 | import io.github.ignoramuses.bing_bing_wahoo.content.cap.FlyingCapEntity; 11 | import io.github.ignoramuses.bing_bing_wahoo.content.cap.MysteriousCapArmorMaterial; 12 | import io.github.ignoramuses.bing_bing_wahoo.content.cap.MysteriousCapItem; 13 | import net.minecraft.core.Registry; 14 | import net.minecraft.core.registries.BuiltInRegistries; 15 | import net.minecraft.sounds.SoundEvent; 16 | import net.minecraft.world.entity.EntityDimensions; 17 | import net.minecraft.world.entity.EntityType; 18 | import net.minecraft.world.entity.EquipmentSlot; 19 | import net.minecraft.world.entity.MobCategory; 20 | import net.minecraft.world.item.*; 21 | import net.minecraft.world.item.ArmorItem.Type; 22 | import net.minecraft.world.level.GameRules; 23 | import net.minecraft.world.level.GameRules.BooleanValue; 24 | import net.minecraft.world.level.GameRules.Category; 25 | 26 | public class WahooRegistry { 27 | 28 | // sound 29 | 30 | public static final SoundEvent SLIDER_SOUND = Registry.register( 31 | BuiltInRegistries.SOUND_EVENT, 32 | SliderRecordItem.SOUND_ID, 33 | SoundEvent.createVariableRangeEvent(SliderRecordItem.SOUND_ID) 34 | ); 35 | 36 | // items 37 | 38 | public static final DyeableArmorItem MYSTERIOUS_CAP = Registry.register( 39 | BuiltInRegistries.ITEM, 40 | BingBingWahoo.id("mysterious_cap"), 41 | new MysteriousCapItem( 42 | MysteriousCapArmorMaterial.INSTANCE, 43 | Type.HELMET, 44 | new FabricItemSettings() 45 | .rarity(Rarity.RARE) 46 | .durability(128) 47 | ) 48 | ); 49 | 50 | public static final RecordItem MUSIC_DISC_SLIDER = Registry.register( 51 | BuiltInRegistries.ITEM, 52 | BingBingWahoo.id("music_disc_slider"), 53 | new SliderRecordItem( 54 | 14, 55 | SLIDER_SOUND, 56 | new FabricItemSettings() 57 | .rarity(Rarity.RARE) 58 | .maxCount(1) 59 | ) 60 | ); 61 | 62 | // entity 63 | 64 | public static final EntityType FLYING_CAP = Registry.register( 65 | BuiltInRegistries.ENTITY_TYPE, 66 | BingBingWahoo.id("flying_cap"), 67 | FabricEntityTypeBuilder.create(MobCategory.MISC, FlyingCapEntity::new) 68 | .dimensions(EntityDimensions.fixed(0.75f, 0.5f)) 69 | .fireImmune() 70 | .disableSummon() 71 | .build() 72 | ); 73 | 74 | public static void init() { 75 | ItemGroupEvents.modifyEntriesEvent(CreativeModeTabs.COMBAT).register(entries -> entries.addAfter(Items.TURTLE_HELMET, MYSTERIOUS_CAP)); 76 | ItemGroupEvents.modifyEntriesEvent(CreativeModeTabs.TOOLS_AND_UTILITIES).register(entries -> entries.accept(MUSIC_DISC_SLIDER)); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/io/github/ignoramuses/bing_bing_wahoo/WahooCommands.java: -------------------------------------------------------------------------------- 1 | package io.github.ignoramuses.bing_bing_wahoo; 2 | 3 | import com.mojang.brigadier.arguments.BoolArgumentType; 4 | import io.github.ignoramuses.bing_bing_wahoo.packets.RequestStopAllActionsPacket; 5 | import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; 6 | import net.fabricmc.fabric.api.gamerule.v1.GameRuleFactory; 7 | import net.fabricmc.fabric.api.gamerule.v1.GameRuleRegistry; 8 | import net.fabricmc.fabric.api.gamerule.v1.rule.DoubleRule; 9 | import net.fabricmc.fabric.api.networking.v1.PacketByteBufs; 10 | import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; 11 | import io.github.ignoramuses.bing_bing_wahoo.extensions.PlayerExtensions; 12 | import io.github.ignoramuses.bing_bing_wahoo.extensions.ServerPlayerExtensions; 13 | import io.github.ignoramuses.bing_bing_wahoo.mixin.EntitySelectorOptionsAccessor; 14 | import net.minecraft.commands.SharedSuggestionProvider; 15 | import net.minecraft.commands.arguments.EntityArgument; 16 | import net.minecraft.network.FriendlyByteBuf; 17 | import net.minecraft.network.chat.Component; 18 | import net.minecraft.resources.ResourceLocation; 19 | import net.minecraft.server.level.ServerPlayer; 20 | import net.minecraft.world.level.GameRules; 21 | import java.util.List; 22 | 23 | import static net.minecraft.commands.Commands.argument; 24 | import static net.minecraft.commands.Commands.literal; 25 | 26 | public class WahooCommands { 27 | public static void init() { 28 | EntitySelectorOptionsAccessor.callRegister("sliding", reader -> { 29 | boolean sliding = reader.getReader().readBoolean(); 30 | reader.setIncludesEntities(false); 31 | reader.setWorldLimited(); 32 | reader.setSuggestions((suggestionsBuilder, suggestionsBuilderConsumer) -> SharedSuggestionProvider.suggest(new String[]{"true", "false"}, suggestionsBuilder)); 33 | reader.addPredicate(entity -> { 34 | if (entity instanceof PlayerExtensions extendedPlayer) { 35 | return extendedPlayer.getSliding() == sliding; 36 | } 37 | return false; 38 | }); 39 | }, entitySelectorReader -> true, Component.translatable("argument.entity.options.sliding.description")); 40 | 41 | CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> { 42 | dispatcher.register(literal("bingbingwahoo:setDestructionPerms") 43 | .requires(source -> source.hasPermission(2)) 44 | .then(argument("target", EntityArgument.player()) 45 | .then(argument("value", BoolArgumentType.bool()) 46 | .executes(context -> { 47 | ServerPlayer target = EntityArgument.getPlayer(context, "target"); 48 | target.setDestructionPermOverride(BoolArgumentType.getBool(context, "value")); 49 | return 0; 50 | }) 51 | ) 52 | ) 53 | ); 54 | dispatcher.register(literal("bingbingwahoo:stopAllActions") 55 | .requires(source -> source.hasPermission(2)) 56 | .then(argument("target", EntityArgument.player()) 57 | .executes(ctx -> { 58 | ServerPlayer player = EntityArgument.getPlayer(ctx, "target"); 59 | RequestStopAllActionsPacket.send(player); 60 | return 1; 61 | }) 62 | ) 63 | ); 64 | } 65 | ); 66 | 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/io/github/ignoramuses/bing_bing_wahoo/content/movement/Slopes.java: -------------------------------------------------------------------------------- 1 | package io.github.ignoramuses.bing_bing_wahoo.content.movement; 2 | 3 | import io.github.ignoramuses.bing_bing_wahoo.BingBingWahoo; 4 | import io.github.ignoramuses.bing_bing_wahoo.compat.TemplatesCompat; 5 | import io.github.ignoramuses.bing_bing_wahoo.synced_config.SyncedConfig; 6 | import net.fabricmc.api.EnvType; 7 | import net.fabricmc.api.Environment; 8 | import net.minecraft.core.Direction; 9 | import net.minecraft.core.registries.BuiltInRegistries; 10 | import net.minecraft.core.registries.Registries; 11 | import net.minecraft.resources.ResourceLocation; 12 | import net.minecraft.tags.TagKey; 13 | import net.minecraft.world.level.block.Block; 14 | import net.minecraft.world.level.block.HorizontalDirectionalBlock; 15 | import net.minecraft.world.level.block.StairBlock; 16 | import net.minecraft.world.level.block.state.BlockState; 17 | import net.minecraft.world.level.block.state.properties.BlockStateProperties; 18 | import net.minecraft.world.level.block.state.properties.Half; 19 | import net.minecraft.world.level.block.state.properties.StairsShape; 20 | import org.jetbrains.annotations.Nullable; 21 | 22 | import java.util.List; 23 | import java.util.concurrent.CopyOnWriteArrayList; 24 | 25 | @Environment(EnvType.CLIENT) 26 | public class Slopes { 27 | public static final TagKey STANDARD_SLOPES = TagKey.create(Registries.BLOCK, BingBingWahoo.id("standard_slopes")); 28 | public static final TagKey BACKWARDS_SLOPES = TagKey.create(Registries.BLOCK, BingBingWahoo.id("backwards_slopes")); 29 | 30 | private static final List invalid = new CopyOnWriteArrayList<>(); 31 | 32 | @Nullable 33 | public static Direction getSlideDirection(BlockState slope) { 34 | Block block = slope.getBlock(); 35 | if (invalid.contains(block)) { 36 | return null; // checked previously, no facing property 37 | } else if (slope.is(STANDARD_SLOPES)) { 38 | return getSlideDirectionInternal(slope, "standard_slopes"); 39 | } else if (slope.is(BACKWARDS_SLOPES)) { 40 | Direction direction = getSlideDirectionInternal(slope, "backwards_slopes"); 41 | if (direction != null) 42 | return direction.getOpposite(); 43 | } else if (TemplatesCompat.IS_LOADED) { 44 | return TemplatesCompat.getSlideDirection(slope); 45 | } 46 | 47 | return null; 48 | } 49 | 50 | public static boolean isSlope(BlockState state) { 51 | return getSlideDirection(state) != null; 52 | } 53 | 54 | @Nullable 55 | private static Direction getSlideDirectionInternal(BlockState slope, String tagName) { 56 | if (slope.hasProperty(BlockStateProperties.HORIZONTAL_FACING)) { 57 | if (slope.hasProperty(BlockStateProperties.HALF)) { 58 | Half half = slope.getValue(BlockStateProperties.HALF); 59 | if (half == Half.TOP && !SyncedConfig.CONVEYOR_GLITCH.get()) { 60 | return null; // top half, conveyor glitch disabled. no sliding. 61 | } 62 | } 63 | return slope.getValue(BlockStateProperties.HORIZONTAL_FACING); 64 | } else { 65 | Block block = slope.getBlock(); 66 | ResourceLocation id = BuiltInRegistries.BLOCK.getKey(block); 67 | BingBingWahoo.LOGGER.error("Invalid block in " + tagName + " tag; does not have the horizontal facing property: " + id); 68 | invalid.add(block); 69 | return null; 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/io/github/ignoramuses/bing_bing_wahoo/mixin/PlayerRendererMixin.java: -------------------------------------------------------------------------------- 1 | package io.github.ignoramuses.bing_bing_wahoo.mixin; 2 | 3 | import com.mojang.blaze3d.vertex.PoseStack; 4 | import com.mojang.math.Axis; 5 | import net.fabricmc.api.EnvType; 6 | import net.fabricmc.api.Environment; 7 | import io.github.ignoramuses.bing_bing_wahoo.extensions.AbstractClientPlayerExtensions; 8 | import io.github.ignoramuses.bing_bing_wahoo.extensions.LocalPlayerExtensions; 9 | import net.minecraft.client.model.PlayerModel; 10 | import net.minecraft.client.player.AbstractClientPlayer; 11 | import net.minecraft.client.player.LocalPlayer; 12 | import net.minecraft.client.renderer.MultiBufferSource; 13 | import net.minecraft.client.renderer.entity.EntityRendererProvider; 14 | import net.minecraft.client.renderer.entity.LivingEntityRenderer; 15 | import net.minecraft.client.renderer.entity.player.PlayerRenderer; 16 | import net.minecraft.world.phys.Vec3; 17 | import org.joml.Quaternionf; 18 | import org.joml.Vector3f; 19 | import org.spongepowered.asm.mixin.Mixin; 20 | import org.spongepowered.asm.mixin.injection.At; 21 | import org.spongepowered.asm.mixin.injection.Inject; 22 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 23 | 24 | @Environment(EnvType.CLIENT) 25 | @Mixin(PlayerRenderer.class) 26 | public abstract class PlayerRendererMixin extends LivingEntityRenderer> { 27 | public PlayerRendererMixin(EntityRendererProvider.Context ctx, PlayerModel model, float shadowRadius) { 28 | super(ctx, model, shadowRadius); 29 | } 30 | 31 | @Inject(at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/entity/player/PlayerRenderer;getArmPose(Lnet/minecraft/client/player/AbstractClientPlayer;Lnet/minecraft/world/InteractionHand;)Lnet/minecraft/client/model/HumanoidModel$ArmPose;", shift = At.Shift.BEFORE), method = "setModelProperties") 32 | private void setModelPoseWhileGroundPoundingAndSliding(AbstractClientPlayer player, CallbackInfo ci) { 33 | if (player instanceof LocalPlayerExtensions extendedPlayer && extendedPlayer.groundPounding()) { 34 | getModel().crouching = true; 35 | } 36 | } 37 | 38 | @Inject(method = "render(Lnet/minecraft/client/player/AbstractClientPlayer;FFLcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;I)V", 39 | at = @At("HEAD")) 40 | private void flip(AbstractClientPlayer entity, float entityYaw, float partialTicks, PoseStack matrixStack, MultiBufferSource buffer, int packedLight, CallbackInfo ci) { 41 | if (entity instanceof AbstractClientPlayerExtensions ex && entity instanceof EntityAccessor access) { 42 | int ticksFlipping = ex.ticksFlipping(); 43 | if (ticksFlipping != 0) { 44 | float yaw = entity.getYRot(); 45 | Vec3 lookVec = access.callCalculateViewVector(0, yaw - 90); 46 | Vector3f vec = lookVec.toVector3f(); 47 | int mult = ex.flippingForwards() ? 1 : -1; 48 | if (entity instanceof LocalPlayer) { // some stuff is reversed locally. 49 | partialTicks = -partialTicks; 50 | mult = -mult; 51 | } 52 | vec.set(mult * vec.x(), 0, mult * vec.z()); 53 | Quaternionf q = Axis.of(vec).rotationDegrees((ticksFlipping + partialTicks) * 24); // magical speed number 54 | float height = entity.getBbHeight(); 55 | matrixStack.translate(0, height / 2, 0); // offset pivot point 56 | matrixStack.mulPose(q); // rotate 57 | matrixStack.translate(0, -height / 2, 0); // offset model 58 | } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/io/github/ignoramuses/bing_bing_wahoo/content/cap/MysteriousCapItem.java: -------------------------------------------------------------------------------- 1 | package io.github.ignoramuses.bing_bing_wahoo.content.cap; 2 | 3 | import net.minecraft.ChatFormatting; 4 | import net.minecraft.nbt.CompoundTag; 5 | import net.minecraft.network.chat.Component; 6 | import net.minecraft.network.chat.MutableComponent; 7 | import net.minecraft.server.level.ServerPlayer; 8 | import net.minecraft.sounds.SoundEvents; 9 | import net.minecraft.sounds.SoundSource; 10 | import net.minecraft.world.InteractionHand; 11 | import net.minecraft.world.InteractionResult; 12 | import net.minecraft.world.InteractionResultHolder; 13 | import net.minecraft.world.entity.EquipmentSlot; 14 | import net.minecraft.world.entity.EquipmentSlot.Type; 15 | import net.minecraft.world.entity.LivingEntity; 16 | import net.minecraft.world.entity.monster.EnderMan; 17 | import net.minecraft.world.entity.monster.Zombie; 18 | import net.minecraft.world.entity.monster.piglin.AbstractPiglin; 19 | import net.minecraft.world.entity.player.Player; 20 | import net.minecraft.world.item.ArmorMaterial; 21 | import net.minecraft.world.item.DyeableArmorItem; 22 | import net.minecraft.world.item.ItemStack; 23 | import net.minecraft.world.item.TooltipFlag; 24 | import net.minecraft.world.level.Level; 25 | import org.jetbrains.annotations.Nullable; 26 | 27 | import java.util.List; 28 | 29 | public class MysteriousCapItem extends DyeableArmorItem { 30 | public static final MutableComponent LUIGI_NUMBER_ONE = Component.translatable("bingbingwahoo.luigi_number_one") 31 | .withStyle(ChatFormatting.ITALIC, ChatFormatting.GREEN); 32 | 33 | public MysteriousCapItem(ArmorMaterial armorMaterial, Type type, Properties settings) { 34 | super(armorMaterial, type, settings); 35 | } 36 | 37 | public int getColor(ItemStack stack) { 38 | CompoundTag nbtCompound = stack.getTagElement("display"); 39 | return nbtCompound != null && nbtCompound.contains("color", 99) ? nbtCompound.getInt("color") : 0xFFFFFF; 40 | } 41 | 42 | @Override 43 | public InteractionResultHolder use(Level world, Player user, InteractionHand hand) { 44 | ItemStack held = user.getItemInHand(hand); 45 | boolean client = world.isClientSide(); 46 | if (!client && user instanceof ServerPlayer player) { 47 | FlyingCapEntity.spawn(player, held, PreferredCapSlot.fromHand(hand)); 48 | } 49 | return InteractionResultHolder.sidedSuccess(held, client); 50 | } 51 | 52 | @Override 53 | public InteractionResult interactLivingEntity(ItemStack stack, Player player, LivingEntity entity, InteractionHand hand) { 54 | if (player.isCrouching() && !entity.isBaby() && entity.getItemBySlot(EquipmentSlot.HEAD).isEmpty()) { 55 | // all bipeds 56 | if (entity instanceof Zombie || entity instanceof EnderMan || entity instanceof AbstractPiglin) { 57 | Level level = player.level(); 58 | if (!level.isClientSide()) { 59 | ItemStack toSet = stack.copy(); 60 | toSet.setCount(1); 61 | entity.setItemSlot(EquipmentSlot.HEAD, toSet); 62 | if (!player.isCreative()) 63 | stack.shrink(1); 64 | } 65 | level.playSound(null, entity.blockPosition(), SoundEvents.CHICKEN_EGG, SoundSource.NEUTRAL, 1, 1); 66 | return InteractionResult.SUCCESS; 67 | } 68 | } 69 | return InteractionResult.PASS; 70 | } 71 | 72 | @Override 73 | public void appendHoverText(ItemStack stack, @Nullable Level level, List tooltipComponents, TooltipFlag isAdvanced) { 74 | super.appendHoverText(stack, level, tooltipComponents, isAdvanced); 75 | if (getColor(stack) == 0x80C71F) { 76 | tooltipComponents.add(1, LUIGI_NUMBER_ONE); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/io/github/ignoramuses/bing_bing_wahoo/content/cap/MysteriousCapModel.java: -------------------------------------------------------------------------------- 1 | // Made with Model Converter by Globox_Z 2 | // Generate all required imports 3 | // Made with Blockbench 3.9.2 4 | // Exported for Minecraft version 1.15 5 | // Paste this class into your mod and generate all required imports 6 | package io.github.ignoramuses.bing_bing_wahoo.content.cap; 7 | 8 | import com.mojang.blaze3d.vertex.PoseStack; 9 | import com.mojang.blaze3d.vertex.VertexConsumer; 10 | import io.github.ignoramuses.bing_bing_wahoo.BingBingWahoo; 11 | import io.github.ignoramuses.bing_bing_wahoo.WahooUtils; 12 | import net.minecraft.client.model.*; 13 | import net.minecraft.client.model.geom.ModelLayerLocation; 14 | import net.minecraft.client.model.geom.ModelPart; 15 | import net.minecraft.client.model.geom.PartPose; 16 | import net.minecraft.client.model.geom.builders.CubeDeformation; 17 | import net.minecraft.client.model.geom.builders.CubeListBuilder; 18 | import net.minecraft.client.model.geom.builders.LayerDefinition; 19 | import net.minecraft.client.model.geom.builders.MeshDefinition; 20 | import net.minecraft.client.model.geom.builders.PartDefinition; 21 | import net.minecraft.client.renderer.RenderType; 22 | import net.minecraft.client.renderer.entity.EntityRendererProvider; 23 | import net.minecraft.resources.ResourceLocation; 24 | import org.jetbrains.annotations.Nullable; 25 | 26 | public class MysteriousCapModel extends Model { 27 | public static final ModelLayerLocation MODEL_LAYER = new ModelLayerLocation(new ResourceLocation(BingBingWahoo.ID, "mysterious_cap"), "cap"); 28 | private final ModelPart bone; // everything 29 | private final ModelPart cube_r1; // that one slanted cube 30 | @Nullable 31 | public final EntityModel wearerModel; 32 | @Nullable 33 | public final ModelPart wearerHead; 34 | 35 | public MysteriousCapModel(EntityRendererProvider.Context ctx, @Nullable EntityModel wearerModel) { 36 | super(RenderType::entityCutoutNoCull); 37 | bone = ctx.bakeLayer(MODEL_LAYER).getChild("bone"); 38 | cube_r1 = bone.getChild("cube_r1"); 39 | this.wearerModel = wearerModel; 40 | this.wearerHead = WahooUtils.getHeadModel(wearerModel); 41 | setRotationAngle(cube_r1, 0.0F, 0.0F, -0.3927F); 42 | } 43 | 44 | public static LayerDefinition getTexturedModelData() { 45 | MeshDefinition modelData = new MeshDefinition(); 46 | PartDefinition modelPartData = modelData.getRoot(); 47 | PartDefinition modelPartData1 = modelPartData.addOrReplaceChild("bone", 48 | CubeListBuilder.create() 49 | .texOffs(0, 0) 50 | .addBox(-12.0F, -3.0F, 4.0F, 9.0F, 3.0F, 9.0F) 51 | .texOffs(0, 0) 52 | .addBox(-12.0F, -5.0F, 4.0F, 9.0F, 3.0F, 9.0F, new CubeDeformation(-0.01F)) 53 | .texOffs(0, 23) 54 | .addBox(-7.0F, -4.05F, 4.0F, 4.0F, 1.0F, 9.0F) 55 | .texOffs(0, 0) 56 | .addBox(-3.75F, -3.75F, 7.5F, 1.0F, 2.0F, 2.0F) 57 | .texOffs(23, 14) 58 | .addBox(-3.075F, -1.0F, 4.0F, 4.0F, 1.0F, 9.0F), 59 | PartPose.offset(5.5F, 24.0F, -8.5F) 60 | ); 61 | modelPartData1.addOrReplaceChild("cube_r1", 62 | CubeListBuilder.create() 63 | .texOffs(0, 12) 64 | .addBox(-3.7F, -1.6F, -4.5F, 7.0F, 2.0F, 9.0F, new CubeDeformation(0.01F)), 65 | PartPose.offset(-6.1945F, -3.9853F, 8.5F) 66 | ); 67 | return LayerDefinition.create(modelData, 64, 64); 68 | } 69 | 70 | @Override 71 | public void renderToBuffer(PoseStack matrixStack, VertexConsumer buffer, int packedLight, int packedOverlay, float red, float green, float blue, float alpha) { 72 | bone.render(matrixStack, buffer, packedLight, packedOverlay, red, green, blue, 1); 73 | } 74 | 75 | public void setRotationAngle(ModelPart bone, float x, float y, float z) { 76 | bone.xRot = x; 77 | bone.yRot = y; 78 | bone.zRot = z; 79 | } 80 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bing Bing Wahoo 2 | Bringing the physics of Super Mario 64(+ more) to Minecraft, one wahoo at a time.
3 | Made for ModFest 1.17. 4 | # Features 5 | Bing Bing Wahoo has a variety of features designed to fill your wahooing needs. To get started, craft yourself a Mysterious Cap with 5 wool in the helmet formation.
6 | The Mysterious Cap can also be worn in the hat slot when using Trinkets. 7 | - Double / Triple Jump 8 | - After jumping, jump again shortly after you land to jump higher! 9 | - The 3rd jump in a row (Triple Jump) will make you do a flip! 10 | - Higher jumps will not activate when holding the jump button, it 11 | must be tapped each time. 12 | 13 | - Long Jump 14 | - Activated by quickly holding crouch and jumping while moving. 15 | - Backwards long jump included! 16 | 17 | - Dive 18 | - Triggered by clicking the attack button while sprinting. Works midair and on 19 | the ground, however ground activation may be annoying, so it is configurable 20 | through Mod Menu. 21 | - Once you land, you begin sliding on your stomach. 22 | 23 | - Sliding 24 | - By diving or sneaking on a slope, you will begin to slide. 25 | - Both stairs and slopes from [Automobility](https://github.com/FoundationGames/Automobility) count as slopes. 26 | - Once you're sliding on flat ground, you can control your movement by looking around. 27 | - If the floor you're sliding on is in the `#bingbingwahoo:slides` tag, sliding mechanics are changed slightly, such as being able to go a longer distance. 28 | 29 | - Bonking 30 | - Hitting a wall too hard will knock you over for a bit! 31 | - Disabled in creative mode. 32 | - Toggleable in config. 33 | 34 | - Wall Jump 35 | - Once you jump into a wall, you have a short time to jump again, and 36 | you will be sent in the opposite direction. 37 | - Does not activate for regular jumps by default, too annoying. However, 38 | can be enabled through Mod Menu. 39 | 40 | - Ground Pound 41 | - Crouching midair will make you do a flip and send you flying downwards! 42 | - Soft blocks will crumble beneath you as you plummet! 43 | - Gain enough velocity by falling for long enough, and the impact will 44 | cause an explosion! 45 | - Fall damage from a Ground Pound will never be lethal. 46 | 47 | - Ledge Grab 48 | - If you jump up to the edge of a flat block, you'll grab onto it! 49 | - Disables looking left and right. 50 | - Pressing forwards or jump will boost you up! 51 | - Pressing back or sneak will drop you down. 52 | - You can move slowly left and right across ledges. 53 | 54 | - Backflip 55 | - If you jump while sneaking and standing still, you will do a backflip 56 | and be sent backwards! 57 | - Could be annoying, so it can be disabled through Mod Menu. 58 | 59 | - Cap throwing 60 | - When held, the cap can be thrown by clicking. 61 | - When worn, it can be thrown via the G key by default. 62 | - The thrown cap will damage any entity it hits. 63 | - It will also retrieve any items or experience it finds! 64 | 65 | - Slider music disc 66 | - A music disc which plays a version of Slider. This version has been modified to be in the Minecraft soundfont. 67 | 68 | # Moderation 69 | Some of Bing Bing Wahoo's features are controllable from the server side by Gamerules. These include: 70 | - Destructive ground pounds - `destructiveGroundPounds` 71 | - Backwards long jumps - `backwardsLongJumps` 72 | - Rapid Fire long jumps - `rapidFireLongJumps` 73 | - Maximum long jump speed - `longJumpMaxSpeed` 74 | - Long jump speed multiplier - `longJumpSpeedMultiplier` 75 | - Mysterious Cap needed for abilities - `mysteriousCapRequired` 76 | 77 | On top of this, destructiveGroundPounds can be overridden on a per-player basis using the command `/bingbingwahoo:setDestructionPerms`. However, this is not persistent and will be lost on world reload. 78 | 79 | # Photosensitivity warning 80 | The flips caused by some moves may be too much for some people. If 81 | this is the case for you, you can lower the Flip Speed Multiplier in the 82 | mod's config menu, accessed through Mod Menu. Setting it to 0 will 83 | disable flips completely. -------------------------------------------------------------------------------- /src/main/resources/assets/bingbingwahoo/lang/en_us.json: -------------------------------------------------------------------------------- 1 | { 2 | "bingbingwahoo.midnightconfig.title": "Bing Bing Wahoo Config", 3 | 4 | "bingbingwahoo.midnightconfig.blj": "Backwards Long Jumps", 5 | "bingbingwahoo.midnightconfig.bljComment0": "Whether backwards long jumps are enabled or not.", 6 | "bingbingwahoo.midnightconfig.bljComment1": "Overridable by backwardsLongJumps gamerule.", 7 | 8 | "bingbingwahoo.midnightconfig.rapidFireLongJumps": "Rapid Fire Long Jumps", 9 | "bingbingwahoo.midnightconfig.rapidFireLongJumpsComment0": "Whether backwards long jumps can be used \"rapid fire\" or not.", 10 | "bingbingwahoo.midnightconfig.rapidFireLongJumpsComment1": "\"Rapid fire\" means that you can jump again without releasing jump in between.", 11 | "bingbingwahoo.midnightconfig.rapidFireLongJumpsComment2": "Overridable by rapidFireLongJumps gamerule.", 12 | 13 | "bingbingwahoo.midnightconfig.maxLongJumpSpeed": "Max long jump speed", 14 | "bingbingwahoo.midnightconfig.maxLongJumpSpeedComment0": "Maximum velocity of a long jump.", 15 | "bingbingwahoo.midnightconfig.maxLongJumpSpeedComment1": "Overridable by longJumpMaxSpeed gamerule.", 16 | 17 | "bingbingwahoo.midnightconfig.longJumpSpeedMultiplier": "Long jump speed multiplier", 18 | "bingbingwahoo.midnightconfig.longJumpSpeedMultiplierComment0": "Multiplier used for speed of long jumps.", 19 | "bingbingwahoo.midnightconfig.longJumpSpeedMultiplierComment1": "Overridable by longJumpSpeedMultiplier gamerule.", 20 | 21 | "bingbingwahoo.midnightconfig.flipSpeedMultiplier": "Flip Speed Multiplier", 22 | "bingbingwahoo.midnightconfig.flipSpeedMultiplierComment0": "Multiplier for the speed of flips and backflips.", 23 | "bingbingwahoo.midnightconfig.flipSpeedMultiplierComment1": "Set to 0 to disable.", 24 | 25 | "bingbingwahoo.midnightconfig.allowNormalWallJumps": "Allow wall jumping from normal jumps", 26 | "bingbingwahoo.midnightconfig.allowNormalWallJumpsComment": "Enable wall jumping out of a normal jump.", 27 | 28 | "bingbingwahoo.midnightconfig.backFlips": "Backflips", 29 | "bingbingwahoo.midnightconfig.backFlipsComment": "Enables backflipping, triggered by jumping out of a crouch.", 30 | 31 | "bingbingwahoo.midnightconfig.groundedDives": "Grounded dives", 32 | "bingbingwahoo.midnightconfig.groundedDivesComment": "Enable diving while on the ground.", 33 | 34 | "bingbingwahoo.midnightconfig.groundPoundType": "Ground Pound Type", 35 | "bingbingwahoo.midnightconfig.groundPoundTypeComment0": "How Ground Pounds should behave.", 36 | "bingbingwahoo.midnightconfig.groundPoundTypeComment1": "Enabled - Normal behavior.", 37 | "bingbingwahoo.midnightconfig.groundPoundTypeComment2": "Disabled - Ground Pounds are disabled, and cannot be used.", 38 | "bingbingwahoo.midnightconfig.groundPoundTypeComment3": "Destructive - The same as Enabled, but the Ground Pound will cause destruction.", 39 | "bingbingwahoo.midnightconfig.groundPoundTypeComment4": "Can be overridden by destructiveGroundPounds gamerule.", 40 | 41 | "bingbingwahoo.midnightconfig.enum.GroundPoundType.ENABLED": "Enabled", 42 | "bingbingwahoo.midnightconfig.enum.GroundPoundType.DISABLED": "Disabled", 43 | "bingbingwahoo.midnightconfig.enum.GroundPoundType.DESTRUCTIVE": "Destructive", 44 | 45 | "bingbingwahoo.midnightconfig.bonking": "Bonking", 46 | "bingbingwahoo.midnightconfig.bonkingComment": "Whether bonks should happen or not.", 47 | 48 | "bingbingwahoo.midnightconfig.spacer0": "", 49 | "bingbingwahoo.midnightconfig.spacer1": "", 50 | "bingbingwahoo.midnightconfig.spacer2": "", 51 | "bingbingwahoo.midnightconfig.spacer3": "", 52 | "bingbingwahoo.midnightconfig.spacer4": "", 53 | "bingbingwahoo.midnightconfig.spacer5": "", 54 | "bingbingwahoo.midnightconfig.spacer6": "", 55 | "bingbingwahoo.midnightconfig.spacer7": "", 56 | "bingbingwahoo.midnightconfig.spacer8": "", 57 | 58 | "item.bingbingwahoo.mysterious_cap": "Mysterious Cap", 59 | "bingbingwahoo.key.throw_cap": "Throw Cap", 60 | "bingbingwahoo.key.category": "Bing Bing Wahoo", 61 | 62 | "item.bingbingwahoo.music_disc_slider": "Music Disc", 63 | "item.bingbingwahoo.music_disc_slider.desc": "Koji Kondo - Slider", 64 | "item.bingbingwahoo.music_disc_slider.desc2": "...but in the Minecraft soundfont", 65 | 66 | "bingbingwahoo.luigi_number_one": "Luigi Number One!", 67 | 68 | "argument.entity.options.sliding.description": "Players sliding", 69 | 70 | "gamerule.backwardsLongJumps": "Backwards Long Jumps", 71 | "gamerule.destructiveGroundPounds": "Destructive Ground Pounds", 72 | "gamerule.rapidFireLongJumps": "Rapid Fire Long Jumps", 73 | "gamerule.longJumpMaxSpeed": "Max long jump speed", 74 | "gamerule.longJumpSpeedMultiplier": "Long jump speed multiplier", 75 | "gamerule.mysteriousCapRequired": "Mysterious Cap Required" 76 | } -------------------------------------------------------------------------------- /src/main/java/io/github/ignoramuses/bing_bing_wahoo/synced_config/SyncedConfig.java: -------------------------------------------------------------------------------- 1 | package io.github.ignoramuses.bing_bing_wahoo.synced_config; 2 | 3 | import io.github.ignoramuses.bing_bing_wahoo.BingBingWahooConfig; 4 | import io.github.ignoramuses.bing_bing_wahoo.content.movement.GroundPoundType; 5 | import io.github.ignoramuses.bing_bing_wahoo.packets.UpdateSyncedBooleanPacket; 6 | import io.github.ignoramuses.bing_bing_wahoo.packets.UpdateSyncedDoublePacket; 7 | import net.fabricmc.api.EnvType; 8 | import net.fabricmc.api.Environment; 9 | import net.fabricmc.fabric.api.gamerule.v1.GameRuleFactory; 10 | import net.fabricmc.fabric.api.gamerule.v1.GameRuleRegistry; 11 | import net.fabricmc.fabric.api.gamerule.v1.rule.DoubleRule; 12 | 13 | import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents; 14 | import net.minecraft.world.level.GameRules; 15 | import net.minecraft.world.level.GameRules.BooleanValue; 16 | import net.minecraft.world.level.GameRules.Category; 17 | import net.minecraft.world.level.GameRules.Value; 18 | import org.jetbrains.annotations.ApiStatus.Internal; 19 | 20 | import java.util.HashMap; 21 | import java.util.Map; 22 | import java.util.function.Supplier; 23 | 24 | /** 25 | * SyncedConfigs are client-side configurable values that can be overriden by GameRules. 26 | * @param the configurable type 27 | * @param the GameRule Value type 28 | */ 29 | public class SyncedConfig> { 30 | public static final Map> BOOLEAN_CONFIGS = new HashMap<>(); 31 | public static final Map> DOUBLE_CONFIGS = new HashMap<>(); 32 | 33 | public static final SyncedConfig 34 | DESTRUCTIVE_GROUND_POUNDS = registerBoolean("destructiveGroundPounds", true, () -> BingBingWahooConfig.groundPoundType == GroundPoundType.DESTRUCTIVE), 35 | BACKWARDS_LONG_JUMPS = registerBoolean("backwardsLongJumps", true, () -> BingBingWahooConfig.blj), 36 | RAPID_FIRE_LONG_JUMPS = registerBoolean("rapidFireLongJumps", true, () -> BingBingWahooConfig.rapidFireLongJumps), 37 | CONVEYOR_GLITCH = registerBoolean("conveyorGlitch", true, () -> true), 38 | CAP_REQUIRED = registerBoolean("mysteriousCapRequired", true, () -> true); 39 | 40 | public static final SyncedConfig 41 | MAX_LONG_JUMP_SPEED = registerDouble("longJumpMaxSpeed", 1.5, 0, () -> BingBingWahooConfig.maxLongJumpSpeed), 42 | LONG_JUMP_SPEED_MULTIPLIER = registerDouble("longJumpSpeedMultiplier", 10, 0, () -> BingBingWahooConfig.longJumpSpeedMultiplier); 43 | 44 | public final GameRules.Key ruleKey; 45 | public final String name; 46 | 47 | private final Supplier configValue; 48 | private final T defaultRuleValue; 49 | @Internal 50 | public T currentRuleValue; 51 | 52 | public SyncedConfig(String name, T defaultRuleValue, Supplier configValue, Category category, GameRules.Type type) { 53 | this.name = name; 54 | this.defaultRuleValue = defaultRuleValue; 55 | this.currentRuleValue = defaultRuleValue; 56 | this.configValue = configValue; 57 | this.ruleKey = GameRuleRegistry.register(name, category, type); 58 | } 59 | 60 | public SyncedConfig(String name, T defaultRuleValue, Supplier configValue, GameRules.Type type) { 61 | this(name, defaultRuleValue, configValue, Category.PLAYER, type); 62 | } 63 | 64 | public static SyncedConfig registerBoolean(String name, boolean defaultRuleValue, Supplier configValue) { 65 | SyncedConfig config = new SyncedConfig<>( 66 | name, defaultRuleValue, configValue, 67 | GameRuleFactory.createBooleanRule(defaultRuleValue, new SyncedBooleanUpdater(name)) 68 | ); 69 | BOOLEAN_CONFIGS.put(name, config); 70 | return config; 71 | } 72 | 73 | public static SyncedConfig registerDouble(String name, double defaultRuleValue, double min, Supplier configValue) { 74 | SyncedConfig config = new SyncedConfig<>( 75 | name, defaultRuleValue, configValue, 76 | GameRuleFactory.createDoubleRule(defaultRuleValue, min, Double.MAX_VALUE, new SyncedDoubleUpdater(name)) 77 | ); 78 | DOUBLE_CONFIGS.put(name, config); 79 | return config; 80 | } 81 | 82 | /** 83 | * May only be used on the client. 84 | * For server-side getting, use {@link SyncedConfig#ruleKey} on a {@link GameRules} instance. 85 | */ 86 | @Environment(EnvType.CLIENT) 87 | public T get() { 88 | // if rule is default, let client choose 89 | if (defaultRuleValue == currentRuleValue) { 90 | return configValue.get(); 91 | } else { // otherwise force override 92 | return currentRuleValue; 93 | } 94 | } 95 | 96 | public static void init() { 97 | // sync values to players on join 98 | ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> { 99 | GameRules rules = server.getGameRules(); 100 | for (SyncedConfig config : BOOLEAN_CONFIGS.values()) { 101 | UpdateSyncedBooleanPacket.send(sender, config, rules); 102 | } 103 | for (SyncedConfig config : DOUBLE_CONFIGS.values()) { 104 | UpdateSyncedDoublePacket.send(sender, config, rules); 105 | } 106 | }); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /src/main/java/io/github/ignoramuses/bing_bing_wahoo/WahooUtils.java: -------------------------------------------------------------------------------- 1 | package io.github.ignoramuses.bing_bing_wahoo; 2 | 3 | import com.mojang.math.Axis; 4 | import io.github.ignoramuses.bing_bing_wahoo.content.movement.Slopes; 5 | import net.fabricmc.api.EnvType; 6 | import net.fabricmc.api.Environment; 7 | import io.github.ignoramuses.bing_bing_wahoo.content.cap.MysteriousCapModel; 8 | import net.minecraft.client.model.HeadedModel; 9 | import net.minecraft.client.model.HumanoidModel; 10 | import net.minecraft.client.model.Model; 11 | import net.minecraft.client.model.geom.ModelPart; 12 | import net.minecraft.client.renderer.MultiBufferSource; 13 | import net.minecraft.client.renderer.RenderType; 14 | import net.minecraft.client.renderer.entity.ItemRenderer; 15 | import net.minecraft.core.Direction; 16 | import net.minecraft.nbt.DoubleTag; 17 | import net.minecraft.nbt.ListTag; 18 | import net.minecraft.resources.ResourceLocation; 19 | import net.minecraft.world.item.ItemStack; 20 | import net.minecraft.world.level.block.HorizontalDirectionalBlock; 21 | import net.minecraft.world.level.block.StairBlock; 22 | import net.minecraft.world.level.block.state.BlockState; 23 | import net.minecraft.world.level.block.state.properties.BlockStateProperties; 24 | import net.minecraft.world.level.block.state.properties.Half; 25 | import net.minecraft.world.level.block.state.properties.StairsShape; 26 | import net.minecraft.world.phys.Vec3; 27 | import net.minecraft.world.phys.shapes.VoxelShape; 28 | import org.jetbrains.annotations.Nullable; 29 | 30 | import com.mojang.blaze3d.vertex.PoseStack; 31 | import com.mojang.blaze3d.vertex.VertexConsumer; 32 | 33 | import static io.github.ignoramuses.bing_bing_wahoo.WahooRegistry.MYSTERIOUS_CAP; 34 | 35 | public class WahooUtils { 36 | public static final double SIXTEENTH = 1 / 16f; 37 | public static final ResourceLocation CAP_TEXTURE = new ResourceLocation(BingBingWahoo.ID, "textures/armor/mysterious_cap.png"); 38 | public static final ResourceLocation EMBLEM_TEXTURE = new ResourceLocation(BingBingWahoo.ID, "textures/armor/mysterious_cap_emblem.png"); 39 | 40 | public static ListTag toNbtList(double... values) { 41 | ListTag nbtList = new ListTag(); 42 | 43 | for(double d : values) { 44 | nbtList.add(DoubleTag.valueOf(d)); 45 | } 46 | 47 | return nbtList; 48 | } 49 | 50 | @Environment(EnvType.CLIENT) 51 | public static void renderCap(PoseStack matrices, MultiBufferSource vertexConsumers, ItemStack hatStack, int light, float tickDelta, MysteriousCapModel model) { 52 | int color = MYSTERIOUS_CAP.getColor(hatStack); 53 | float r = (color >> 16 & 255) / 255.0F; 54 | float g = (color >> 8 & 255) / 255.0F; 55 | float b = (color & 255) / 255.0F; 56 | matrices.pushPose(); 57 | matrices.mulPose(Axis.YP.rotationDegrees(90)); 58 | VertexConsumer capConsumer = ItemRenderer.getArmorFoilBuffer(vertexConsumers, RenderType.armorCutoutNoCull(CAP_TEXTURE), false, hatStack.hasFoil()); 59 | model.renderToBuffer(matrices, capConsumer, light, 1, r, g, b, 1); 60 | VertexConsumer emblemConsumer = ItemRenderer.getArmorFoilBuffer(vertexConsumers, RenderType.armorCutoutNoCull(EMBLEM_TEXTURE), false, hatStack.hasFoil()); 61 | model.renderToBuffer(matrices, emblemConsumer, light, 1, 1, 1, 1, 1); 62 | matrices.popPose(); 63 | } 64 | 65 | @Nullable 66 | @Environment(EnvType.CLIENT) 67 | public static ModelPart getHeadModel(@Nullable Model base) { 68 | if (base instanceof HumanoidModel biped) { 69 | return biped.head; 70 | } else if (base instanceof HeadedModel model) { 71 | return model.getHead(); 72 | } 73 | 74 | return null; 75 | } 76 | 77 | public static double getVelocityForSlopeDirection(Direction directionOfSlope) { 78 | return switch (directionOfSlope) { 79 | case NORTH, WEST -> -0.1; 80 | case SOUTH, EAST -> 0.1; 81 | default -> throw new IllegalStateException("Unexpected value: " + directionOfSlope); 82 | }; 83 | } 84 | 85 | public static double getVelocityForSlidingOnGround(Direction direction) { 86 | return switch (direction) { 87 | case NORTH, WEST -> -0.035; 88 | case SOUTH, EAST -> 0.035; 89 | default -> throw new IllegalStateException("Unexpected value: " + direction); 90 | }; 91 | } 92 | 93 | public static double capWithSign(double value, double max) { 94 | double valueAbs = Math.abs(value); 95 | double newValue = Math.min(valueAbs, max); 96 | return Math.copySign(newValue, value); 97 | } 98 | 99 | public static Direction getHorizontalDirectionFromVector(Vec3 vector) { 100 | double x = vector.x(); 101 | double z = vector.z(); 102 | 103 | if (Math.abs(x) > Math.abs(z)) { 104 | if (x > 0) { 105 | return Direction.EAST; 106 | } 107 | return Direction.WEST; 108 | } else { 109 | if (z > 0) { 110 | return Direction.SOUTH; 111 | } 112 | return Direction.NORTH; 113 | } 114 | } 115 | 116 | /** 117 | * Whether a number is close to another number or not. 118 | * @param number The number to compare 119 | * @param target The number to compare against 120 | * @param range The range in which the numbers are approximately the same, inclusive 121 | * @return Whether the difference between number and target is less than or equal to range. 122 | */ 123 | public static boolean aprox(double number, double target, double range) { 124 | if (number == target) return true; 125 | double difference; 126 | if (number > target) { 127 | difference = number - target; 128 | } else { 129 | difference = target - number; 130 | } 131 | return difference <= range; 132 | } 133 | 134 | public static boolean voxelShapeEligibleForGrab(VoxelShape shape, Direction facing) { 135 | double xMin = shape.min(Direction.Axis.X); 136 | double xMax = shape.max(Direction.Axis.X); 137 | 138 | double yMin = shape.min(Direction.Axis.Y); 139 | double yMax = shape.max(Direction.Axis.Y); 140 | 141 | double zMin = shape.min(Direction.Axis.Z); 142 | double zMax = shape.max(Direction.Axis.Z); 143 | 144 | // this is unholy 145 | // iterate over each pixel of a 16x16x16 block, checking for a solid row across which can be grabbed. 146 | int solidPixelsInARow = 0; 147 | return switch (facing) { 148 | case NORTH -> { 149 | // x+, z- 150 | for (double y = yMax; y > yMin; y -= SIXTEENTH) { 151 | for (double x = xMin; x < xMax; x += SIXTEENTH) { 152 | for (double z = zMax; z >= zMin; z -= SIXTEENTH) { 153 | if (shape.bounds().contains(xMin + x, yMin + y, zMin + z)) { 154 | solidPixelsInARow++; 155 | } else { 156 | solidPixelsInARow = 0; 157 | } 158 | if (solidPixelsInARow == 16) { 159 | yield true; 160 | } 161 | } 162 | } 163 | } 164 | yield false; 165 | } 166 | case EAST -> { 167 | // x+, z+ 168 | for (double y = yMax; y > yMin; y -= SIXTEENTH) { 169 | for (double x = xMin; x < xMax; x += SIXTEENTH) { 170 | for (double z = zMin; z < zMax; z += SIXTEENTH) { 171 | if (shape.bounds().contains(xMin + x, yMin + y, zMin + z)) { 172 | solidPixelsInARow++; 173 | } else { 174 | solidPixelsInARow = 0; 175 | } 176 | if (solidPixelsInARow == 16) { 177 | yield true; 178 | } 179 | } 180 | } 181 | } 182 | yield false; 183 | } 184 | case SOUTH -> { 185 | // x-, z+ 186 | for (double y = yMax; y > yMin; y -= SIXTEENTH) { 187 | for (double x = xMax; x >= xMin; x -= SIXTEENTH) { 188 | for (double z = zMin; z < zMax; z += SIXTEENTH) { 189 | if (shape.bounds().contains(xMin + x, yMin + y, zMin + z)) { 190 | solidPixelsInARow++; 191 | } else { 192 | solidPixelsInARow = 0; 193 | } 194 | if (solidPixelsInARow == 16) { 195 | yield true; 196 | } 197 | } 198 | } 199 | } 200 | yield false; 201 | } 202 | case WEST -> { 203 | // x-, z- 204 | for (double y = yMax; y > yMin; y -= SIXTEENTH) { 205 | for (double x = xMax; x >= xMin; x -= SIXTEENTH) { 206 | for (double z = zMax; z >= zMin; z -= SIXTEENTH) { 207 | if (shape.bounds().contains(xMin + x, yMin + y, zMin + z)) { 208 | solidPixelsInARow++; 209 | } else { 210 | solidPixelsInARow = 0; 211 | } 212 | if (solidPixelsInARow == 16) { 213 | yield true; 214 | } 215 | } 216 | } 217 | } 218 | yield false; 219 | } 220 | default -> throw new RuntimeException("this should never be called, if it did something has gone catastrophically wrong"); 221 | }; 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /src/main/java/io/github/ignoramuses/bing_bing_wahoo/mixin/ServerPlayerMixin.java: -------------------------------------------------------------------------------- 1 | package io.github.ignoramuses.bing_bing_wahoo.mixin; 2 | 3 | import com.mojang.authlib.GameProfile; 4 | import io.github.ignoramuses.bing_bing_wahoo.BingBingWahoo; 5 | import io.github.ignoramuses.bing_bing_wahoo.extensions.PlayerExtensions; 6 | import io.github.ignoramuses.bing_bing_wahoo.extensions.ServerPlayerExtensions; 7 | import io.github.ignoramuses.bing_bing_wahoo.content.movement.JumpType; 8 | import io.github.ignoramuses.bing_bing_wahoo.synced_config.SyncedConfig; 9 | import net.minecraft.core.BlockPos; 10 | import net.minecraft.nbt.CompoundTag; 11 | import net.minecraft.nbt.Tag; 12 | import net.minecraft.server.level.ServerPlayer; 13 | import net.minecraft.world.damagesource.DamageSource; 14 | import net.minecraft.world.entity.Entity; 15 | import net.minecraft.world.entity.EquipmentSlot; 16 | import net.minecraft.world.entity.LivingEntity; 17 | import net.minecraft.world.entity.Pose; 18 | import net.minecraft.world.entity.player.Player; 19 | import net.minecraft.world.item.ItemStack; 20 | import net.minecraft.world.level.Level; 21 | import net.minecraft.world.level.Level.ExplosionInteraction; 22 | import net.minecraft.world.level.block.state.BlockState; 23 | import net.minecraft.world.phys.AABB; 24 | import net.minecraft.world.phys.Vec3; 25 | import org.jetbrains.annotations.Nullable; 26 | import org.spongepowered.asm.mixin.Mixin; 27 | import org.spongepowered.asm.mixin.Shadow; 28 | import org.spongepowered.asm.mixin.Unique; 29 | import org.spongepowered.asm.mixin.injection.At; 30 | import org.spongepowered.asm.mixin.injection.Inject; 31 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 32 | 33 | @Mixin(ServerPlayer.class) 34 | public abstract class ServerPlayerMixin extends Player implements ServerPlayerExtensions, PlayerExtensions { 35 | @Shadow 36 | public abstract void teleportTo(double x, double y, double z); 37 | 38 | @Shadow public abstract void moveTo(double x, double y, double z); 39 | 40 | @Shadow public abstract boolean mayInteract(Level world, BlockPos pos); 41 | 42 | @Unique 43 | private final BlockPos.MutableBlockPos groundPoundBlockBreakPos = new BlockPos.MutableBlockPos(); 44 | @Unique 45 | private final BlockPos.MutableBlockPos groundPoundStartPos = new BlockPos.MutableBlockPos(); 46 | @Unique 47 | private final BlockPos.MutableBlockPos divingStartPos = new BlockPos.MutableBlockPos(); 48 | @Unique 49 | private JumpType previousJumpType = JumpType.NORMAL; 50 | @Unique 51 | private boolean groundPounding = false; 52 | @Unique 53 | private long ticksGroundPoundingFor = 0; 54 | @Unique 55 | private boolean destructiveGroundPound = true; 56 | @Unique 57 | private boolean diving = false; 58 | @Unique 59 | private boolean destructionPermOverride = false; 60 | @Unique 61 | private boolean sliding = false; 62 | @Unique 63 | private boolean bonked = false; 64 | 65 | public ServerPlayerMixin(Level world, BlockPos pos, float yaw, GameProfile gameProfile) { 66 | super(world, pos, yaw, gameProfile); 67 | } 68 | 69 | @Override 70 | public boolean getSliding() { 71 | return diving || sliding; 72 | } 73 | 74 | @Inject(at = @At("HEAD"), method = "tick()V") 75 | public void tick(CallbackInfo ci) { 76 | if (groundPounding) { 77 | ticksGroundPoundingFor++; 78 | Level level = level(); 79 | AABB bounds = getBoundingBox(); 80 | for (int x = (int) Math.floor(bounds.minX); x <= Math.floor(bounds.maxX); x++) { 81 | for (int z = (int) Math.floor(bounds.minZ); z <= Math.floor(bounds.maxZ); z++) { 82 | groundPoundBlockBreakPos.set(x, blockPosition().below().getY(), z); 83 | if (mayBreakBlock(groundPoundBlockBreakPos)) { 84 | BlockState state = level.getBlockState(groundPoundBlockBreakPos); 85 | float destroySpeed = state.getDestroySpeed(level, groundPoundBlockBreakPos); 86 | if (destroySpeed <= 0 || destroySpeed > 0.5) { // unbreakable 87 | if (!state.is(BingBingWahoo.GROUND_POUND_WHITELIST)) 88 | continue; // if it's not in the whitelist, skip it 89 | } 90 | 91 | if (state.is(BingBingWahoo.GROUND_POUND_BLACKLIST)) 92 | continue; // skip if blacklisted 93 | 94 | level.destroyBlock(groundPoundBlockBreakPos, true, this); 95 | } 96 | } 97 | } 98 | } 99 | } 100 | 101 | @Unique 102 | private boolean mayBreakBlock(BlockPos pos) { 103 | if (!onGround()) 104 | return false; // must be grounded 105 | 106 | // overrides 107 | if (destructionPermOverride) 108 | return true; // forced true by command 109 | // check for override on boots 110 | Level level = level(); 111 | ItemStack boots = getItemBySlot(EquipmentSlot.FEET); 112 | if (!boots.isEmpty()) { 113 | CompoundTag nbt = boots.getTag(); 114 | if (nbt != null && nbt.contains("DestructionArea", Tag.TAG_COMPOUND)) { 115 | CompoundTag tag = nbt.getCompound("DestructionArea"); 116 | if (tag.contains("Dimension", Tag.TAG_STRING) && tag.contains("Bounds", Tag.TAG_INT_ARRAY)) { 117 | String dim = tag.getString("Dimension"); 118 | int[] bounds = tag.getIntArray("Bounds"); 119 | if (bounds.length == 6 && level.dimension().location().toString().equals(dim)) { 120 | int minX = bounds[0]; 121 | int minY = bounds[1]; 122 | int minZ = bounds[2]; 123 | int maxX = bounds[3]; 124 | int maxY = bounds[4]; 125 | int maxZ = bounds[5]; 126 | return pos.getX() >= minX && pos.getX() <= maxX 127 | && pos.getY() >= minY && pos.getY() <= maxY 128 | && pos.getZ() >= minZ && pos.getZ() <= maxZ; 129 | } 130 | } 131 | } 132 | } 133 | 134 | if (!destructiveGroundPound) 135 | return false; // player preference 136 | if (!level.getGameRules().getBoolean(SyncedConfig.DESTRUCTIVE_GROUND_POUNDS.ruleKey)) 137 | return false; // forbidden by gamerule 138 | 139 | return mayBuild(); 140 | } 141 | 142 | @Override 143 | protected boolean isStayingOnGroundSurface() { 144 | if (previousJumpType == JumpType.LONG) return false; 145 | return super.isStayingOnGroundSurface(); 146 | } 147 | 148 | @Override 149 | protected int calculateFallDamage(float fallDistance, float damageMultiplier) { 150 | if (groundPounding) { 151 | fallDistance = groundPoundStartPos.subtract(blockPosition()).getY(); 152 | } else if (diving) { 153 | fallDistance = divingStartPos.subtract(blockPosition()).getY(); 154 | divingStartPos.set(blockPosition()); 155 | } else { 156 | fallDistance -= switch (previousJumpType) { 157 | case DOUBLE, LONG -> 4; 158 | case BACK_FLIP -> 6; 159 | case TRIPLE -> 7; 160 | default -> 0; 161 | }; 162 | } 163 | 164 | int fallDamage = super.calculateFallDamage(fallDistance, damageMultiplier); 165 | 166 | if (fallDamage > getHealth() && groundPounding) { 167 | return (int) (getHealth() - 1); 168 | } 169 | 170 | return fallDamage; 171 | } 172 | 173 | @Override 174 | public void setBonked(boolean value) { 175 | if (bonked == value) 176 | return; 177 | bonked = value; 178 | if (bonked) { 179 | moveTo(Vec3.atCenterOf(blockPosition())); // don't suffocate in wall 180 | setPose(Pose.SLEEPING); 181 | } else { 182 | setPose(Pose.STANDING); 183 | } 184 | } 185 | 186 | @Override 187 | public void setPreviousJumpType(JumpType type) { 188 | previousJumpType = type; 189 | } 190 | 191 | @Override 192 | public void setGroundPounding(boolean value, boolean destruction) { 193 | destructiveGroundPound = destruction; 194 | if (groundPounding == value) 195 | return; 196 | groundPounding = value; 197 | if (groundPounding) { 198 | groundPoundStartPos.set(blockPosition()); 199 | } else { 200 | if (destruction) { 201 | Level level = level(); 202 | Vec3 pos = position(); 203 | if (ticksGroundPoundingFor > 60) { 204 | level.explode(this, pos.x(), pos.y(), pos.z(), 2, ExplosionInteraction.NONE); 205 | } else { 206 | AABB box = new AABB(pos.x() - 1, pos.y() - 1, pos.z() - 1, pos.x() + 1, pos.y() + 1, pos.z() + 1); 207 | int damage = ticksGroundPoundingFor >= 15 208 | ? ticksGroundPoundingFor >= 30 209 | ? ticksGroundPoundingFor >= 45 210 | ? 20 211 | : 10 212 | : 5 213 | : 0; 214 | 215 | if (ticksGroundPoundingFor >= 15) { 216 | for (Entity entity : level.getEntities(this, box)) { 217 | if (entity instanceof LivingEntity) { 218 | DamageSource source = level.damageSources().anvil(this); 219 | entity.hurt(source, damage); 220 | } 221 | } 222 | } 223 | } 224 | } 225 | ticksGroundPoundingFor = 0; 226 | } 227 | } 228 | 229 | @Override 230 | public void setDiving(boolean value, @Nullable BlockPos startPos) { 231 | if (value) { 232 | setPose(Pose.SWIMMING); 233 | divingStartPos.set(startPos); 234 | } else { 235 | setPose(Pose.STANDING); 236 | } 237 | diving = value; 238 | } 239 | 240 | @Override 241 | public void setSliding(boolean value) { 242 | sliding = value; 243 | } 244 | 245 | @Override 246 | protected void updatePlayerPose() { 247 | if (diving || bonked || groundPounding) { 248 | return; 249 | } 250 | super.updatePlayerPose(); 251 | } 252 | 253 | @Override 254 | public void setDestructionPermOverride(boolean value) { 255 | destructionPermOverride = value; 256 | } 257 | 258 | @Override 259 | public boolean isGroundPounding() { 260 | return groundPounding; 261 | } 262 | 263 | @Override 264 | public boolean isDiving() { 265 | return diving; 266 | } 267 | } 268 | -------------------------------------------------------------------------------- /src/main/java/io/github/ignoramuses/bing_bing_wahoo/content/cap/FlyingCapEntity.java: -------------------------------------------------------------------------------- 1 | package io.github.ignoramuses.bing_bing_wahoo.content.cap; 2 | 3 | import io.github.ignoramuses.bing_bing_wahoo.packets.CapSpawnPacket; 4 | import net.fabricmc.api.EnvType; 5 | import net.fabricmc.api.Environment; 6 | import net.fabricmc.fabric.api.networking.v1.PacketByteBufs; 7 | import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; 8 | import io.github.ignoramuses.bing_bing_wahoo.WahooNetworking; 9 | import io.github.ignoramuses.bing_bing_wahoo.WahooRegistry; 10 | import io.github.ignoramuses.bing_bing_wahoo.WahooUtils; 11 | import net.minecraft.client.Minecraft; 12 | import net.minecraft.nbt.CompoundTag; 13 | import net.minecraft.network.FriendlyByteBuf; 14 | import net.minecraft.network.protocol.Packet; 15 | import net.minecraft.network.protocol.game.ClientGamePacketListener; 16 | import net.minecraft.network.protocol.game.ClientboundAddEntityPacket; 17 | import net.minecraft.network.syncher.EntityDataAccessor; 18 | import net.minecraft.network.syncher.EntityDataSerializers; 19 | import net.minecraft.network.syncher.SynchedEntityData; 20 | import net.minecraft.server.level.ServerPlayer; 21 | import net.minecraft.sounds.SoundEvents; 22 | import net.minecraft.sounds.SoundSource; 23 | import net.minecraft.util.RandomSource; 24 | import net.minecraft.world.InteractionHand; 25 | import net.minecraft.world.damagesource.DamageSource; 26 | import net.minecraft.world.entity.*; 27 | import net.minecraft.world.entity.item.ItemEntity; 28 | import net.minecraft.world.entity.player.Inventory; 29 | import net.minecraft.world.entity.player.Player; 30 | import net.minecraft.world.entity.projectile.ItemSupplier; 31 | import net.minecraft.world.item.ItemCooldowns; 32 | import net.minecraft.world.item.ItemStack; 33 | import net.minecraft.world.level.Level; 34 | import net.minecraft.world.phys.Vec3; 35 | import org.jetbrains.annotations.Nullable; 36 | 37 | import java.util.*; 38 | 39 | import static io.github.ignoramuses.bing_bing_wahoo.WahooRegistry.MYSTERIOUS_CAP; 40 | import static net.minecraft.world.entity.Entity.RemovalReason.*; 41 | 42 | public class FlyingCapEntity extends Entity implements ItemSupplier { 43 | private static final EntityDataAccessor COLOR = SynchedEntityData.defineId(FlyingCapEntity.class, EntityDataSerializers.INT); 44 | 45 | // synced 46 | private ItemStack stack; 47 | private UUID throwerId; 48 | private Vec3 startAngle; 49 | private Vec3 startPos; 50 | public int ticksAtEnd; 51 | private PreferredCapSlot preferredSlot; 52 | // not synced 53 | @Nullable 54 | private Player thrower; 55 | private final List carriedEntities = new ArrayList<>(); 56 | private boolean leftThrower = false; 57 | @Environment(EnvType.CLIENT) 58 | private CapFlyingSoundInstance whooshSound; 59 | 60 | public FlyingCapEntity(EntityType entityType, Level world) { 61 | super(entityType, world); 62 | } 63 | 64 | public FlyingCapEntity(Level world, ItemStack itemStack, Player thrower, double x, double y, double z, PreferredCapSlot slot) { 65 | super(WahooRegistry.FLYING_CAP, world); 66 | this.setItem(itemStack.copy()); 67 | this.thrower = thrower; 68 | this.throwerId = thrower.getGameProfile().getId(); 69 | this.startAngle = adjustStartAngle(thrower.getViewVector(0).normalize().scale(0.1)); 70 | setPosRaw(x, y, z); 71 | this.startPos = position(); 72 | this.preferredSlot = slot; 73 | } 74 | 75 | @Override 76 | protected void defineSynchedData() { 77 | entityData.define(COLOR, 0xFFFFFF); 78 | } 79 | 80 | public void setItem(ItemStack stack) { 81 | this.stack = stack; 82 | if (stack.getItem() instanceof MysteriousCapItem cap) { 83 | setColor(cap.getColor(stack)); 84 | } 85 | } 86 | 87 | public void setColor(int color) { 88 | entityData.set(COLOR, color); 89 | } 90 | 91 | @Override 92 | public void readAdditionalSaveData(CompoundTag nbt) { 93 | ItemStack stack = ItemStack.of(nbt.getCompound("Item")); 94 | setItem(stack); 95 | this.throwerId = nbt.getUUID("Thrower"); 96 | this.startAngle = new Vec3(nbt.getDouble("StartAngleX"), nbt.getDouble("StartAngleY"), nbt.getDouble("StartAngleZ")); 97 | this.startPos = new Vec3(nbt.getDouble("StartPosX"), nbt.getDouble("StartPosY"), nbt.getDouble("StartPosZ")); 98 | this.leftThrower = nbt.getBoolean("LeftThrower"); 99 | this.ticksAtEnd = nbt.getInt("TicksAtEnd"); 100 | this.preferredSlot = PreferredCapSlot.values()[nbt.getInt("PreferredSlot")]; 101 | } 102 | 103 | @Override 104 | public void addAdditionalSaveData(CompoundTag nbt) { 105 | nbt.put("Item", getItem().save(new CompoundTag())); 106 | nbt.putUUID("Thrower", throwerId); 107 | nbt.putDouble("StartAngleX", startAngle.x()); 108 | nbt.putDouble("StartAngleY", startAngle.y()); 109 | nbt.putDouble("StartAngleZ", startAngle.z()); 110 | nbt.putDouble("StartPosX", startPos.x()); 111 | nbt.putDouble("StartPosY", startPos.y()); 112 | nbt.putDouble("StartPosZ", startPos.z()); 113 | nbt.putBoolean("LeftThrower", leftThrower); 114 | nbt.putInt("TicksAtEnd", ticksAtEnd); 115 | nbt.putInt("PreferredSlot", preferredSlot.ordinal()); 116 | } 117 | 118 | @Override 119 | public void tick() { 120 | super.tick(); 121 | tryFindThrower(); 122 | tryMove(); 123 | moveCarriedEntities(); 124 | handleCollisions(); 125 | 126 | if (level().isClientSide()) { 127 | playWhoosh(); 128 | } else { 129 | if (tickCount > 500) kill(); 130 | } 131 | } 132 | 133 | private void handleCollisions() { 134 | Level level = level(); 135 | List collisions = level.getEntities( 136 | this, 137 | getBoundingBox().expandTowards(getDeltaMovement()).inflate(1), 138 | e -> !e.isSpectator() 139 | ); 140 | 141 | if (shouldLeaveThrower(collisions)) { 142 | leftThrower = true; 143 | } 144 | 145 | for (Entity entity : collisions) { 146 | if (thrower != null) { 147 | boolean client = level.isClientSide(); 148 | if (entity == thrower) { 149 | if (!client && (leftThrower || ticksAtEnd != 0)) { 150 | giveThrowerItem(); 151 | dropCarried(); 152 | remove(KILLED); // bypass item drop 153 | level.playSound(null, 154 | thrower.getX(), 155 | thrower.getY(), 156 | thrower.getZ(), 157 | SoundEvents.ITEM_PICKUP, 158 | SoundSource.PLAYERS, 159 | 0.2F, 160 | ((thrower.getRandom().nextFloat() - thrower.getRandom().nextFloat()) * 0.7F + 1.0F) * 2.0F 161 | ); 162 | } 163 | } else { 164 | if (entity instanceof ItemEntity || entity instanceof ExperienceOrb) { 165 | carriedEntities.add(entity); 166 | } else if (entity instanceof LivingEntity living && !client) { 167 | DamageSource source = level.damageSources().thrown(this, thrower); 168 | living.hurt(source, 3); 169 | ticksAtEnd = 10; 170 | } 171 | } 172 | } 173 | } 174 | } 175 | 176 | private void giveThrowerItem() { 177 | if (thrower != null) { 178 | if (!tryReequipCap()) { // set in correct slot 179 | thrower.getInventory().placeItemBackInInventory(getItem()); // throw in inv or on ground if no space 180 | } 181 | } 182 | } 183 | 184 | private void dropCarried() { 185 | if (thrower != null && !carriedEntities.isEmpty()) { 186 | Inventory inv = thrower.getInventory(); 187 | for (Entity entity : carriedEntities) { 188 | if (entity.isRemoved()) continue; 189 | if (entity instanceof ItemEntity item) { 190 | ItemStack stack = item.getItem(); 191 | inv.placeItemBackInInventory(stack); 192 | item.discard(); 193 | } 194 | } 195 | } 196 | } 197 | 198 | private void moveCarriedEntities() { 199 | if (!carriedEntities.isEmpty()) { 200 | Vec3 pos = position().add(0, 0.4, 0); 201 | for (ListIterator itr = carriedEntities.listIterator(); itr.hasNext();) { 202 | Entity carried = itr.next(); 203 | if (carried.isRemoved()) { 204 | itr.remove(); 205 | continue; 206 | } 207 | carried.setPos(pos); 208 | carried.resetFallDistance(); 209 | } 210 | } 211 | } 212 | 213 | private void tryFindThrower() { 214 | if (thrower == null) { 215 | for (Player player : level().players()) { 216 | if (player.getGameProfile().getId().equals(throwerId)) { 217 | thrower = player; 218 | } 219 | } 220 | } 221 | } 222 | 223 | private void tryMove() { 224 | Vec3 toMove = Vec3.ZERO; 225 | if (ticksAtEnd == 0) { 226 | double mult = Math.cos(tickCount / 8f) * 10; 227 | toMove = startAngle.scale(mult); 228 | // valley of the cosine wave - slow down as it approaches the end 229 | ticksAtEnd = mult <= 0 ? 1 : 0; 230 | } else if (thrower != null) { 231 | ticksAtEnd++; 232 | if (ticksAtEnd > 10) { 233 | Vec3 distance = thrower.getEyePosition().subtract(position()); 234 | toMove = distance.scale(0.2); 235 | } 236 | } 237 | 238 | if (toMove != Vec3.ZERO) { 239 | move(MoverType.SELF, toMove); 240 | } 241 | } 242 | 243 | private boolean shouldLeaveThrower(List collisions) { 244 | for (Entity entity : collisions) { 245 | if (entity == thrower) { 246 | return false; 247 | } 248 | } 249 | return true; 250 | } 251 | 252 | private boolean tryReequipCap() { 253 | if (thrower != null) { 254 | ItemStack stack = getItem(); 255 | if (preferredSlot.shouldEquip(thrower, stack)) { 256 | preferredSlot.equip(thrower, stack); 257 | return true; 258 | } 259 | } 260 | return false; 261 | } 262 | 263 | @Environment(EnvType.CLIENT) 264 | private void playWhoosh() { 265 | if (whooshSound == null) { 266 | Minecraft mc = Minecraft.getInstance(); 267 | whooshSound = new CapFlyingSoundInstance(this); 268 | mc.getSoundManager().play(whooshSound); 269 | } 270 | } 271 | 272 | @Override 273 | public boolean canChangeDimensions() { 274 | return false; 275 | } 276 | 277 | @Override 278 | public void kill() { 279 | super.kill(); 280 | spawnAtLocation(stack.copy()); 281 | } 282 | 283 | @Override 284 | public boolean isPickable() { 285 | return super.isPickable(); 286 | } 287 | 288 | @Override 289 | public ItemStack getItem() { 290 | return stack; 291 | } 292 | 293 | @Override 294 | public Packet getAddEntityPacket() { 295 | return CapSpawnPacket.makePacket(this); 296 | } 297 | 298 | public RandomSource getRandom() { 299 | return this.random; 300 | } 301 | 302 | private static Vec3 adjustStartAngle(Vec3 startAngle) { 303 | // x and z near zero, but not y 304 | // you don't even want to know why this is needed. 305 | // you want to know anyway? fine. 306 | // somehow, only on dedicated servers, only when looking straight down with 307 | // sub-pixel accuracy, 8 ghost-voxelshapes prevented the cap from moving down. 308 | // I don't know how. I don't want to know how. All I know is that this slight 309 | // offset is somehow able to fix it. 310 | // Nearly a full day was spent figuring this out, I give up. You win, MC. 311 | if (WahooUtils.aprox(startAngle.x, 0, 0.001)) { 312 | if (WahooUtils.aprox(startAngle.z, 0, 0.001)) { 313 | if (!WahooUtils.aprox(startAngle.y, 0, 0.001)) { 314 | return new Vec3(0.001, startAngle.y, 0.001); 315 | } 316 | } 317 | } 318 | return startAngle; 319 | } 320 | 321 | public static void spawn(ServerPlayer thrower, ItemStack capStack, PreferredCapSlot preferredSlot) { 322 | ItemCooldowns cooldowns = thrower.getCooldowns(); 323 | if (capStack != null && capStack.is(MYSTERIOUS_CAP) && !cooldowns.isOnCooldown(MYSTERIOUS_CAP)) { 324 | Level level = thrower.level(); 325 | FlyingCapEntity cap = new FlyingCapEntity(level, capStack.copy(), thrower, thrower.getX(), thrower.getEyeY() - 0.1, thrower.getZ(), preferredSlot); 326 | level.addFreshEntity(cap); 327 | cooldowns.addCooldown(MYSTERIOUS_CAP, 20); 328 | thrower.swing(InteractionHand.MAIN_HAND, true); 329 | preferredSlot.equip(thrower, ItemStack.EMPTY); 330 | } 331 | } 332 | } 333 | -------------------------------------------------------------------------------- /src/main/java/io/github/ignoramuses/bing_bing_wahoo/mixin/LocalPlayerMixin.java: -------------------------------------------------------------------------------- 1 | package io.github.ignoramuses.bing_bing_wahoo.mixin; 2 | 3 | import com.mojang.authlib.GameProfile; 4 | import io.github.ignoramuses.bing_bing_wahoo.content.movement.FlipState; 5 | import io.github.ignoramuses.bing_bing_wahoo.content.movement.GroundPoundType; 6 | import io.github.ignoramuses.bing_bing_wahoo.content.movement.Slopes; 7 | import io.github.ignoramuses.bing_bing_wahoo.packets.*; 8 | import io.github.ignoramuses.bing_bing_wahoo.synced_config.SyncedConfig; 9 | import net.fabricmc.api.EnvType; 10 | import net.fabricmc.api.Environment; 11 | import io.github.ignoramuses.bing_bing_wahoo.*; 12 | import io.github.ignoramuses.bing_bing_wahoo.compat.TrinketsCompat; 13 | import io.github.ignoramuses.bing_bing_wahoo.extensions.AbstractClientPlayerExtensions; 14 | import io.github.ignoramuses.bing_bing_wahoo.extensions.KeyboardInputExtensions; 15 | import io.github.ignoramuses.bing_bing_wahoo.extensions.LocalPlayerExtensions; 16 | import io.github.ignoramuses.bing_bing_wahoo.extensions.PlayerExtensions; 17 | import io.github.ignoramuses.bing_bing_wahoo.content.movement.JumpType; 18 | import net.minecraft.client.Minecraft; 19 | import net.minecraft.client.multiplayer.ClientLevel; 20 | import net.minecraft.client.player.AbstractClientPlayer; 21 | import net.minecraft.client.player.Input; 22 | import net.minecraft.client.player.LocalPlayer; 23 | import net.minecraft.core.BlockPos; 24 | import net.minecraft.core.BlockPos.MutableBlockPos; 25 | import net.minecraft.core.Direction; 26 | import net.minecraft.core.Direction.Plane; 27 | import net.minecraft.util.Mth; 28 | import net.minecraft.world.entity.Entity; 29 | import net.minecraft.world.entity.EquipmentSlot; 30 | import net.minecraft.world.entity.Pose; 31 | import net.minecraft.world.level.Level; 32 | import net.minecraft.world.level.block.Blocks; 33 | import net.minecraft.world.level.block.state.BlockState; 34 | import net.minecraft.world.phys.AABB; 35 | import net.minecraft.world.phys.Vec2; 36 | import net.minecraft.world.phys.Vec3; 37 | import org.spongepowered.asm.mixin.Mixin; 38 | import org.spongepowered.asm.mixin.Shadow; 39 | import org.spongepowered.asm.mixin.Unique; 40 | import org.spongepowered.asm.mixin.injection.At; 41 | import org.spongepowered.asm.mixin.injection.Inject; 42 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 43 | 44 | import java.util.List; 45 | 46 | import static io.github.ignoramuses.bing_bing_wahoo.BingBingWahoo.*; 47 | 48 | @Environment(EnvType.CLIENT) 49 | @Mixin(LocalPlayer.class) 50 | public abstract class LocalPlayerMixin extends AbstractClientPlayer 51 | implements PlayerExtensions, LocalPlayerExtensions, AbstractClientPlayerExtensions { 52 | @Shadow public Input input; 53 | @Shadow private boolean lastOnGround; 54 | @Shadow private boolean handsBusy; 55 | @Shadow private boolean wasShiftKeyDown; 56 | 57 | public LocalPlayerMixin(ClientLevel world, GameProfile profile) { 58 | super(world, profile); 59 | } 60 | 61 | @Shadow public abstract boolean isShiftKeyDown(); 62 | @Shadow public abstract float getViewXRot(float tickDelta); 63 | @Shadow public abstract float getViewYRot(float tickDelta); 64 | @Shadow public abstract boolean startRiding(Entity entity, boolean force); 65 | @Shadow protected abstract boolean hasEnoughImpulseToStartSprinting(); 66 | 67 | @Unique private final MutableBlockPos lastPos = new MutableBlockPos(); 68 | @Unique private boolean jumpHeldSinceLastJump = false; 69 | @Unique private boolean lastJumping = false; 70 | @Unique private long ticksSinceSneakingChanged = 0; 71 | @Unique private long ticksLeftToLongJump = 0; 72 | @Unique private long ticksLeftToDoubleJump = 0; 73 | @Unique private long ticksLeftToTripleJump = 0; 74 | @Unique private JumpType previousJumpType = JumpType.NORMAL; 75 | @Unique private boolean midTripleJump = false; 76 | @Unique private long tripleJumpTicks = 0; 77 | @Unique private boolean isDiving = false; 78 | @Unique private Vec3 currentDivingVelocity = Vec3.ZERO; 79 | @Unique private boolean bonked = false; 80 | @Unique private long bonkTime = 0; 81 | @Unique private boolean diveFlip = false; 82 | @Unique private int flipTimer = 0; 83 | @Unique private long ticksLeftToWallJump = 0; 84 | @Unique private boolean startingGroundPound = false; 85 | @Unique private long ticksInAirDuringGroundPound = 0; 86 | @Unique private double groundPoundSpeedMultiplier = 1.0; 87 | @Unique private boolean wallJumping = false; 88 | @Unique private boolean isGroundPounding = false; 89 | @Unique private boolean hasGroundPounded = false; 90 | @Unique private boolean ledgeGrabbing = false; 91 | @Unique private long ledgeGrabCooldown = 0; 92 | @Unique private long ledgeGrabExitCooldown = 0; 93 | @Unique private boolean longJumping = false; 94 | @Unique private boolean isBackFlipping = false; 95 | @Unique private long ticksGroundPounded = 0; 96 | @Unique private long diveCooldown = 0; 97 | @Unique private long bonkCooldown = 0; 98 | @Unique private boolean lastRiding = false; 99 | @Unique private boolean slidingOnGround = false; 100 | @Unique private boolean slidingOnSlope = false; 101 | @Unique private boolean wasRiding = false; 102 | @Unique private boolean canWahoo = false; 103 | @Unique private long ticksStillInDive = 0; 104 | @Unique private boolean forwardSliding = false; 105 | @Unique private long ticksSlidingOnGround = 0; 106 | 107 | @Inject( 108 | method = "onSyncedDataUpdated", 109 | at = @At( 110 | value = "INVOKE", 111 | target = "Lnet/minecraft/client/resources/sounds/ElytraOnPlayerSoundInstance;(Lnet/minecraft/client/player/LocalPlayer;)V" 112 | ) 113 | ) 114 | public void onFallFlyingStatusChange(CallbackInfo ci) { 115 | if (isDiving) 116 | exitDive(); 117 | if (isGroundPounding) 118 | exitGroundPound(); 119 | if (forwardSliding) 120 | exitForwardSlide(); 121 | } 122 | 123 | @Inject(at = @At("HEAD"), method = "aiStep") 124 | public void savePreviousValues(CallbackInfo ci) { 125 | lastRiding = handsBusy; 126 | lastJumping = input.jumping; 127 | lastPos.set(blockPosition()); 128 | } 129 | 130 | @Inject(at = @At("RETURN"), method = "aiStep") 131 | public void wahooLogic(CallbackInfo ci) { 132 | tickTimers(); 133 | 134 | canWahoo = false; 135 | if (SyncedConfig.CAP_REQUIRED.get()) { 136 | if (getItemBySlot(EquipmentSlot.HEAD).is(WahooRegistry.MYSTERIOUS_CAP) 137 | || TrinketsCompat.capTrinketEquipped(this)) { 138 | canWahoo = true; 139 | } 140 | } else { 141 | canWahoo = true; 142 | } 143 | 144 | if (jumpHeldSinceLastJump && lastJumping && !jumping) { 145 | jumpHeldSinceLastJump = false; 146 | } 147 | 148 | if (isPassenger()) { 149 | wasRiding = true; 150 | } else if (wasRiding) { 151 | if (onGround()) { 152 | wasRiding = false; 153 | } 154 | } 155 | 156 | Level level = level(); 157 | 158 | // ----- TRIPLE JUMPS ----- 159 | 160 | if (midTripleJump) { 161 | tripleJumpTicks++; 162 | if (tripleJumpTicks > 10) { 163 | StartFallFlyPacket.send(); 164 | } 165 | if ((onGround() || !level.getFluidState(blockPosition()).isEmpty()) && tripleJumpTicks > 3 || isDiving || isGroundPounding || getAbilities().flying) { 166 | exitTripleJump(); 167 | } 168 | 169 | } 170 | 171 | // ----- DIVING AND SLIDING ----- 172 | 173 | if (isDiving || forwardSliding) { 174 | BlockPos floorPos = blockPosition(); 175 | BlockState floor = level.getBlockState(floorPos); 176 | if (!Slopes.isSlope(floor)) { 177 | floorPos = blockPosition().below(); 178 | floor = level.getBlockState(floorPos); 179 | } 180 | if (onGround()) { 181 | if (floor.is(SLIDES)) { 182 | if (Slopes.isSlope(floor)) { 183 | handleSlidingOnSlope(floor); 184 | } else { 185 | // assume flat surface 186 | handleSlidingOnGround(true); 187 | } 188 | } else { 189 | // not on a slide, slow you down faster 190 | handleSlidingOnGround(false); 191 | } 192 | } else { 193 | if (lastOnGround) { 194 | if (floor.is(SLIDES) || level.getBlockState(floorPos.below()).is(SLIDES) || level.getBlockState(floorPos.below(2)).is(SLIDES)) { 195 | push(0, -5, 0); 196 | } 197 | } else { 198 | setDeltaMovement(currentDivingVelocity.x(), getDeltaMovement().y() - 0.05, currentDivingVelocity.z()); 199 | } 200 | } 201 | 202 | if (WahooUtils.aprox(currentDivingVelocity.x(), 0, 0.07) && 203 | WahooUtils.aprox(currentDivingVelocity.z(), 0, 0.07)) { 204 | ticksStillInDive++; 205 | } else { 206 | ticksStillInDive = 0; 207 | } 208 | 209 | if (ticksStillInDive > 20) { 210 | if (isDiving) exitDive(); 211 | if (forwardSliding) exitForwardSlide(); 212 | } else if (ticksSlidingOnGround > 50) { 213 | if (!floor.is(SLIDES)) { 214 | if (isDiving) exitDive(); 215 | if (forwardSliding) exitForwardSlide(); 216 | } else if (ticksSlidingOnGround > 100) { 217 | if (isDiving) exitDive(); 218 | if (forwardSliding) exitForwardSlide(); 219 | } 220 | } 221 | 222 | if (!level.getFluidState(blockPosition()).isEmpty() || getAbilities().flying) { 223 | if (isDiving) exitDive(); 224 | if (forwardSliding) exitForwardSlide(); 225 | } 226 | 227 | Direction looking = Direction.fromYRot(getYRot()); 228 | BlockPos posInFront = blockPosition().relative(looking); 229 | if (forwardSliding) posInFront = posInFront.below(); 230 | BlockState inFront = level.getBlockState(posInFront); 231 | 232 | // if (horizontalCollision && !inFront.isAir()) { 233 | // if (isDiving) exitDive(); 234 | // if (forwardSliding) exitForwardSlide(); 235 | // if (!ledgeGrabbing && !isCreative() && bonkCooldown == 0) { 236 | // Vec3 dMovement = getDeltaMovement(); 237 | // boolean movingEnough = Math.abs(dMovement.x()) > 0.07 || Math.abs(dMovement.z()) > 0.07; 238 | // if (movingEnough) { 239 | // BlockPos offset = blockPosition().relative(looking); 240 | // if (level.getBlockState(offset).isRedstoneConductor(level, offset) && 241 | // level.getBlockState(offset.above()).isRedstoneConductor(level, offset.above())) { 242 | // bonk(); 243 | // } 244 | // } 245 | // } 246 | // } 247 | } 248 | 249 | // Initiates Diving 250 | // ugly but it works 251 | if (diveCooldown == 0 && isSprinting() && !isDiving && !diveFlip && getPose() != Pose.SWIMMING && previousJumpType != JumpType.LONG && previousJumpType != JumpType.DIVE && !handsBusy && (onGround() 252 | ? BingBingWahooConfig.groundedDives && Minecraft.getInstance().options.keyAttack.isDown() 253 | : Minecraft.getInstance().options.keyAttack.isDown())) { 254 | dive(); 255 | previousJumpType = JumpType.DIVE; 256 | } 257 | 258 | // ----- BONKING ----- 259 | 260 | // keep you bonked 261 | if (bonked) { 262 | if (ledgeGrabbing) { 263 | exitLedgeGrab(true); 264 | } 265 | if (getPose() != Pose.SLEEPING) { 266 | UpdatePosePacket.send(Pose.SLEEPING); 267 | } 268 | setDeltaMovement(getDeltaMovement().multiply(-0.8, 1, -0.8)); 269 | --bonkTime; 270 | if (bonkTime == 0 || !level.getFluidState(blockPosition()).isEmpty()) { 271 | setDeltaMovement(0, getDeltaMovement().y(), 0); 272 | exitBonk(); 273 | } 274 | } 275 | 276 | // ----- LEDGE GRABS ----- 277 | 278 | Direction facing = getDirection(); 279 | BlockPos head = blockPosition().above(); 280 | BlockPos aboveHead = blockPosition().above(2); 281 | BlockPos inFrontOfHead = head.relative(facing); 282 | // checks block above and block adjacent to it in look direction 283 | if (!isDiving && !ledgeGrabbing && !isGroundPounding && !onGround() && !getAbilities().flying && !onClimbable() && !isSwimming() && ledgeGrabCooldown == 0 && 284 | level.getBlockState(head).isAir() && 285 | level.getBlockState(aboveHead).isAir() && 286 | level.getBlockState(aboveHead.relative(facing)).isAir() && 287 | !level.getBlockState(aboveHead.relative(facing)).is(Blocks.LADDER) && 288 | // checks distance to block in front of eyes 289 | position().distanceTo(Vec3.atCenterOf(blockPosition().relative(facing))) < 1.2 && 290 | WahooUtils.voxelShapeEligibleForGrab(level.getBlockState(inFrontOfHead).getCollisionShape(level, inFrontOfHead), facing)) { 291 | ledgeGrab(); 292 | } 293 | 294 | if (ledgeGrabbing) { 295 | if (!level.getBlockState(inFrontOfHead.above()).isAir() || 296 | !WahooUtils.voxelShapeEligibleForGrab(level.getBlockState(inFrontOfHead).getCollisionShape(level, inFrontOfHead), facing)) { 297 | exitLedgeGrab(true); 298 | } 299 | 300 | setDeltaMovement(0, 0, 0); 301 | if ((input.jumping || input.up) && ledgeGrabExitCooldown == 0) { 302 | exitLedgeGrab(false); 303 | } else if ((isShiftKeyDown() || input.down) && ledgeGrabExitCooldown == 0) { 304 | exitLedgeGrab(true); 305 | } 306 | } 307 | 308 | // ----- WALL JUMPS ----- 309 | 310 | // runs on the first tick a player collides with a wall 311 | if (horizontalCollision) { 312 | if (ticksLeftToWallJump <= 0 && !isDiving && !onGround() && ( 313 | BingBingWahooConfig.allowNormalWallJumps 314 | ? previousJumpType.canWallJumpFrom() || previousJumpType == JumpType.NORMAL 315 | : previousJumpType.canWallJumpFrom())) { 316 | ticksLeftToWallJump = 4; 317 | } 318 | } 319 | 320 | // this is ugly but it works 321 | if (ticksLeftToWallJump > 0 && !isDiving && input.jumping && !jumpHeldSinceLastJump && ( 322 | BingBingWahooConfig.allowNormalWallJumps 323 | ? previousJumpType.canWallJumpFrom() || previousJumpType == JumpType.NORMAL 324 | : previousJumpType.canWallJumpFrom()) && !getAbilities().flying && !isInWater()) { 325 | wallJump(); 326 | } 327 | 328 | if (wallJumping && onGround()) { 329 | exitWallJump(); 330 | } 331 | 332 | // ----- GROUND POUND ----- 333 | 334 | if (BingBingWahooConfig.groundPoundType.enabled() && !onGround() && isShiftKeyDown() && !wasShiftKeyDown && !longJumping && !getAbilities().flying && !handsBusy && !lastRiding && !isInWater() && !level.getBlockState(blockPosition().below()).isRedstoneConductor(level, blockPosition().below())) { 335 | List entities = level.getEntities(this, new AABB(blockPosition()).inflate(1.5, 1, 1.5)); 336 | boolean canPound = entities.size() == 0; 337 | if (canPound) { 338 | groundPound(); 339 | } 340 | } 341 | 342 | if (isGroundPounding) { 343 | UpdatePosePacket.send(Pose.CROUCHING); 344 | hasGroundPounded = true; 345 | ticksInAirDuringGroundPound++; 346 | if (startingGroundPound) { 347 | setDeltaMovement(0, 0, 0); 348 | } 349 | 350 | if (startingGroundPound && flipTimer == 0) { 351 | startingGroundPound = false; 352 | UpdateFlipStatePacket.send(FlipState.NONE); 353 | if (BingBingWahooConfig.flipSpeedMultiplier != 0 && Minecraft.getInstance().options.getCameraType().isFirstPerson()) { 354 | setXRot(0); 355 | } 356 | } 357 | 358 | if (!startingGroundPound) { 359 | if (ticksInAirDuringGroundPound > 6) { 360 | setDeltaMovement(0, -0.5 * groundPoundSpeedMultiplier, 0); 361 | if (groundPoundSpeedMultiplier < 4) { 362 | groundPoundSpeedMultiplier = groundPoundSpeedMultiplier + 0.5; 363 | } 364 | } 365 | } 366 | 367 | if (hasGroundPounded && !startingGroundPound && lastPos.equals(blockPosition())) { 368 | ticksGroundPounded++; 369 | if (ticksGroundPounded > 5) { 370 | exitGroundPound(); 371 | } 372 | } 373 | 374 | if (hasGroundPounded && !startingGroundPound && ticksGroundPounded > 0 && !lastPos.equals(blockPosition())) { 375 | ticksGroundPounded = 0; 376 | } 377 | } 378 | 379 | // ----- BACK FLIPS ----- 380 | 381 | if (onGround() && isBackFlipping) { 382 | exitBackFlip(); 383 | } 384 | 385 | // ----- LONG JUMPS ----- 386 | 387 | if (longJumping && onGround()) { 388 | exitLongJump(); 389 | } 390 | 391 | // ----- FORWARD SLIDING ----- 392 | if (isShiftKeyDown() && (Slopes.isSlope(level.getBlockState(blockPosition())) || 393 | Slopes.isSlope(level.getBlockState(blockPosition().below())))) { 394 | startForwardSliding(); 395 | } 396 | } 397 | 398 | @Unique 399 | private void handleSlidingOnSlope(BlockState floor) { 400 | ticksSlidingOnGround = 0; 401 | slidingOnSlope = true; 402 | slidingOnGround = false; 403 | Direction facing = Slopes.getSlideDirection(floor); 404 | if (facing == null) 405 | return; 406 | double velocityToAdd = WahooUtils.getVelocityForSlopeDirection(facing); 407 | Vec3 newVelocity; 408 | if (facing.getAxis().equals(Direction.Axis.Z)) { 409 | // north and south 410 | newVelocity = new Vec3( 411 | currentDivingVelocity.x(), 412 | currentDivingVelocity.y() - 1, 413 | WahooUtils.capWithSign(currentDivingVelocity.z() + velocityToAdd, Math.max(0.7 - (ticksSlidingOnGround * 0.05), 0)) 414 | ); 415 | } else if (facing.getAxis().equals(Direction.Axis.X)) { 416 | // east and west 417 | newVelocity = new Vec3( 418 | WahooUtils.capWithSign(currentDivingVelocity.x() + velocityToAdd, Math.max(0.7 - (ticksSlidingOnGround * 0.05), 0)), 419 | currentDivingVelocity.y() - 1, 420 | currentDivingVelocity.z() 421 | ); 422 | } else { 423 | // up and down? how did this happen? 424 | throw new RuntimeException("what"); 425 | } 426 | currentDivingVelocity = newVelocity; 427 | setDeltaMovement(newVelocity); 428 | } 429 | 430 | @Unique 431 | private void handleSlidingOnGround(boolean slide) { 432 | ticksSlidingOnGround++; 433 | slidingOnSlope = false; 434 | slidingOnGround = true; 435 | Direction moving = WahooUtils.getHorizontalDirectionFromVector(currentDivingVelocity); 436 | Direction looking = WahooUtils.getHorizontalDirectionFromVector(getLookAngle()); 437 | 438 | Vec3 newVelocity = currentDivingVelocity; 439 | 440 | if (looking != moving.getOpposite()) { 441 | double velocityToAdd = WahooUtils.getVelocityForSlidingOnGround(looking); // move towards look direction 442 | if (looking.getAxis().equals(Direction.Axis.Z)) { 443 | // north and south 444 | newVelocity = new Vec3( 445 | currentDivingVelocity.x() * 0.8, 446 | currentDivingVelocity.y(), 447 | WahooUtils.capWithSign(currentDivingVelocity.z() + velocityToAdd, Math.max(0.7 - (ticksSlidingOnGround * (slide ? 0.01 : 0.02)), 0)) 448 | ); 449 | } else if (looking.getAxis().equals(Direction.Axis.X)) { 450 | // east and west 451 | newVelocity = new Vec3( 452 | WahooUtils.capWithSign(currentDivingVelocity.x() + velocityToAdd, Math.max(0.7 - (ticksSlidingOnGround * (slide ? 0.01 : 0.02)), 0)), 453 | currentDivingVelocity.y(), 454 | currentDivingVelocity.z() * 0.8 455 | ); 456 | } else { 457 | // up and down? how did this happen? 458 | throw new RuntimeException("what"); 459 | } 460 | } 461 | 462 | Vec3 divingVelocity; 463 | if (slide) { 464 | if (ticksSlidingOnGround > 100) { 465 | divingVelocity = new Vec3(newVelocity.x() * 0.95, 0, newVelocity.z() * 0.95); 466 | } else { 467 | divingVelocity = newVelocity; 468 | } 469 | } else { 470 | if (ticksSlidingOnGround > 50) { 471 | divingVelocity = new Vec3(newVelocity.x() * 0.95, 0, newVelocity.z() * 0.95); 472 | } else { 473 | divingVelocity = newVelocity; 474 | } 475 | } 476 | 477 | setDeltaMovement(divingVelocity); 478 | currentDivingVelocity = divingVelocity; 479 | } 480 | 481 | /** 482 | * Handles triggering of jump-based physics 483 | */ 484 | @Override 485 | public void jumpFromGround() { 486 | if (bonked) { 487 | return; 488 | } 489 | 490 | super.jumpFromGround(); 491 | if (input.jumping) { 492 | if ((onGround())) { 493 | if (isDiving || forwardSliding) { 494 | diveFlip = true; 495 | flipTimer = 15; 496 | push(0, 0.25, 0); 497 | setDeltaMovement(getDeltaMovement().multiply(1.25, 1, 1.25)); 498 | UpdateFlipStatePacket.send(FlipState.FORWARDS); 499 | } else if (((SyncedConfig.RAPID_FIRE_LONG_JUMPS.get()) || ticksLeftToLongJump > 0) && (isShiftKeyDown() || wasShiftKeyDown) && previousJumpType.canLongJumpFrom() && (isSprinting() || hasEnoughImpulseToStartSprinting() || input.down)) { 500 | longJump(); 501 | } else if (ticksLeftToDoubleJump > 0 && !jumpHeldSinceLastJump && previousJumpType == JumpType.NORMAL) { 502 | doubleJump(); 503 | } else if (ticksLeftToTripleJump > 0 && ticksLeftToTripleJump < 5 && previousJumpType == JumpType.DOUBLE && (isSprinting() || hasEnoughImpulseToStartSprinting() || input.down)) { 504 | tripleJump(); 505 | } else if (isShiftKeyDown() && getDeltaMovement().x() == 0 && getDeltaMovement().z() == 0 && !jumpHeldSinceLastJump && BingBingWahooConfig.backFlips) { 506 | backFlip(); 507 | } else { 508 | previousJumpType = JumpType.NORMAL; 509 | } 510 | } 511 | } 512 | lastJumping = true; 513 | jumpHeldSinceLastJump = true; 514 | UpdatePreviousJumpTypePacket.send(previousJumpType); 515 | } 516 | 517 | @Unique 518 | private void tickTimers() { 519 | // double jump 520 | if (!lastOnGround && onGround()) { 521 | ticksLeftToDoubleJump = 6; 522 | } 523 | if (ticksLeftToDoubleJump > 0) { 524 | ticksLeftToDoubleJump--; 525 | } 526 | 527 | // triple jump 528 | if (!lastOnGround && onGround()) { 529 | ticksLeftToTripleJump = 6; 530 | } 531 | if (ticksLeftToTripleJump > 0) { 532 | ticksLeftToTripleJump--; 533 | } 534 | 535 | // long jump 536 | if (isShiftKeyDown() != wasShiftKeyDown) { 537 | ticksSinceSneakingChanged = 0; 538 | } 539 | ticksSinceSneakingChanged++; 540 | if (ticksSinceSneakingChanged == 1 && isShiftKeyDown()) { 541 | ticksLeftToLongJump = 10; 542 | } 543 | if (ticksLeftToLongJump > 0) { 544 | ticksLeftToLongJump--; 545 | } 546 | 547 | // wall jump 548 | if (ticksLeftToWallJump > 0) { 549 | ticksLeftToWallJump--; 550 | 551 | if (ticksLeftToWallJump == 0 && previousJumpType != JumpType.NORMAL && !wallJumping && !ledgeGrabbing && !isCreative() && bonkCooldown == 0 && !(Math.abs(getDeltaMovement().x()) < 0.07 && Math.abs(getDeltaMovement().y()) < 0.07)) { 552 | Direction looking = Direction.fromYRot(getYRot()); 553 | Level level = level(); 554 | if (level.getBlockState(blockPosition().relative(looking)).isRedstoneConductor(level, blockPosition().relative(looking)) && 555 | level.getBlockState(blockPosition().relative(looking).above()).isRedstoneConductor(level, blockPosition().relative(looking).above())) { 556 | bonk(); 557 | } 558 | } 559 | } 560 | 561 | // ledge grab 562 | if (ledgeGrabCooldown > 0) { 563 | ledgeGrabCooldown--; 564 | } 565 | if (ledgeGrabExitCooldown > 0) { 566 | ledgeGrabExitCooldown--; 567 | } 568 | 569 | // dive and slide 570 | if (!isDiving && !diveFlip && diveCooldown > 0) { 571 | diveCooldown--; 572 | } 573 | 574 | // bonk 575 | if (!bonked && bonkCooldown > 0) { 576 | bonkCooldown--; 577 | } 578 | 579 | // all flips 580 | if (flipTimer > 0) { 581 | flipTimer--; 582 | } 583 | } 584 | 585 | @Override 586 | protected void updatePlayerPose() { 587 | if (isDiving || bonked || isGroundPounding) { 588 | return; 589 | } 590 | super.updatePlayerPose(); 591 | } 592 | 593 | @Override 594 | public void setXRot(float pitch) { 595 | float tickDelta = Minecraft.getInstance().getFrameTime(); 596 | if (isBackFlipping && Minecraft.getInstance().options.getCameraType().isFirstPerson()) { 597 | if (BingBingWahooConfig.flipSpeedMultiplier != 0) { 598 | ((EntityAccessor) this).setXRotRaw(-(Math.max(0, flipTimer - tickDelta) * BingBingWahooConfig.flipSpeedMultiplier * -24) - 90); 599 | } else { 600 | super.setXRot(pitch); 601 | } 602 | return; 603 | } 604 | 605 | if (midTripleJump && Minecraft.getInstance().options.getCameraType().isFirstPerson()) { 606 | if (BingBingWahooConfig.flipSpeedMultiplier != 0) { 607 | ((EntityAccessor) this).setXRotRaw(Math.max(0, flipTimer - tickDelta) * BingBingWahooConfig.flipSpeedMultiplier * -24); 608 | } else { 609 | super.setXRot(pitch); 610 | } 611 | if (flipTimer == 0) { 612 | exitTripleJump(); 613 | } 614 | return; 615 | } 616 | 617 | if (diveFlip) { 618 | if (flipTimer == 0) { 619 | if (isDiving) exitDive(); 620 | if (forwardSliding) exitForwardSlide(); 621 | } 622 | 623 | if (Minecraft.getInstance().options.getCameraType().isFirstPerson() && BingBingWahooConfig.flipSpeedMultiplier != 0) { 624 | ((EntityAccessor) this).setXRotRaw(Math.max(0, flipTimer - tickDelta) * BingBingWahooConfig.flipSpeedMultiplier * -24); 625 | } 626 | return; 627 | } 628 | 629 | if (bonked) { 630 | return; 631 | } 632 | 633 | if (isGroundPounding && startingGroundPound) { 634 | if (Minecraft.getInstance().options.getCameraType().isFirstPerson() && BingBingWahooConfig.flipSpeedMultiplier != 0) { 635 | ((EntityAccessor) this).setXRotRaw(Math.max(0, flipTimer - tickDelta) * BingBingWahooConfig.flipSpeedMultiplier * -24); 636 | } 637 | return; 638 | } 639 | 640 | super.setXRot(pitch); 641 | } 642 | 643 | @Override 644 | public void setYRot(float yaw) { 645 | if (bonked || ledgeGrabbing) { 646 | return; 647 | } 648 | 649 | super.setYRot(yaw); 650 | } 651 | 652 | @Override 653 | public boolean getSliding() { 654 | return isDiving || forwardSliding; 655 | } 656 | 657 | @Override 658 | public boolean groundPounding() { 659 | return isGroundPounding; 660 | } 661 | 662 | @Override 663 | public boolean slidingOnSlope() { 664 | return slidingOnSlope && forwardSliding; 665 | } 666 | 667 | @Override 668 | public boolean slidingOnGround() { 669 | return slidingOnGround && forwardSliding; 670 | } 671 | 672 | @Override 673 | public boolean diving() { 674 | return isDiving; 675 | } 676 | 677 | @Override 678 | public void startDiving() { 679 | dive(); 680 | } 681 | 682 | // ---------- JUMPS ---------- 683 | 684 | public void longJump() { 685 | if (!canWahoo || wasRiding) return; 686 | // ---------- warning: scary math ---------- 687 | Vec2 velocity = new Vec2((float) getDeltaMovement().x(), (float) getDeltaMovement().z()); 688 | Vec2 rotation = new Vec2((float) getLookAngle().x(), (float) getLookAngle().z()); 689 | 690 | double cosOfVecs = (rotation.dot(velocity)) / (rotation.length() * velocity.length()); 691 | double degreesDiff = Math.toDegrees(Math.acos(cosOfVecs)); 692 | // ----------- end of scary math ----------- 693 | 694 | // todo: use this to make a cleaner implementation 695 | // float x = MathHelper.sin(getYaw() * (float) (Math.PI / 180.0)) * MathHelper.cos(getPitch() * (float) (Math.PI / 180.0)); 696 | // float z = MathHelper.cos(getYaw() * (float) (Math.PI / 180.0)) * MathHelper.cos(getPitch() * (float) (Math.PI / 180.0)); 697 | // this.setVelocity(x * 0.5, 0.75, z * 0.5); 698 | 699 | if (degreesDiff > 85 && degreesDiff < 95) { // don't long jump for moving straight left or right 700 | ticksLeftToLongJump = 0; 701 | previousJumpType = JumpType.NORMAL; 702 | return; 703 | } 704 | 705 | double newVelXAbs; 706 | double newVelZAbs; 707 | 708 | double multiplier = SyncedConfig.LONG_JUMP_SPEED_MULTIPLIER.get(); 709 | double maxSpeed = SyncedConfig.MAX_LONG_JUMP_SPEED.get(); 710 | 711 | if (degreesDiff > 170) { // BLJ 712 | if (SyncedConfig.BACKWARDS_LONG_JUMPS.get()) { 713 | newVelXAbs = Math.abs(getDeltaMovement().x()) * multiplier; 714 | newVelZAbs = Math.abs(getDeltaMovement().z()) * multiplier; 715 | } else { 716 | ticksLeftToLongJump = 0; 717 | previousJumpType = JumpType.NORMAL; 718 | return; 719 | } 720 | } else { 721 | newVelXAbs = Math.min(Math.abs(getDeltaMovement().x()) * multiplier, maxSpeed); 722 | newVelZAbs = Math.min(Math.abs(getDeltaMovement().z()) * multiplier, maxSpeed); 723 | } 724 | 725 | double newVelX = Math.copySign(newVelXAbs, getDeltaMovement().x()); 726 | double newVelZ = Math.copySign(newVelZAbs, getDeltaMovement().z()); 727 | 728 | // todo: see https://github.com/n64decomp/sm64/blob/ecd3d152fb7c6f658d18543c0f4e8147b50d5dde/src/game/mario.c#L863 729 | 730 | setDeltaMovement(newVelX, Math.min(getDeltaMovement().y() * 1.5, 1), newVelZ); 731 | ticksLeftToLongJump = 0; 732 | longJumping = true; 733 | previousJumpType = JumpType.LONG; 734 | } 735 | 736 | private void exitLongJump() { 737 | longJumping = false; 738 | } 739 | 740 | private void doubleJump() { 741 | if (!canWahoo || wasRiding) return; 742 | setDeltaMovement(getDeltaMovement().multiply(1, 1.75, 1)); 743 | ticksLeftToDoubleJump = 0; 744 | previousJumpType = JumpType.DOUBLE; 745 | } 746 | 747 | private void tripleJump() { 748 | if (!canWahoo || wasRiding) return; 749 | setDeltaMovement(getDeltaMovement().multiply(1, 2.5, 1)); 750 | ticksLeftToTripleJump = 0; 751 | previousJumpType = JumpType.TRIPLE; 752 | midTripleJump = true; 753 | flipTimer = 40; 754 | UpdateFlipStatePacket.send(FlipState.FORWARDS); 755 | } 756 | 757 | public void exitTripleJump() { 758 | midTripleJump = false; 759 | tripleJumpTicks = 0; 760 | flipTimer = 0; 761 | UpdateFlipStatePacket.send(FlipState.NONE); 762 | if (BingBingWahooConfig.flipSpeedMultiplier != 0 && Minecraft.getInstance().options.getCameraType().isFirstPerson()) { 763 | setXRot(0); 764 | } 765 | } 766 | 767 | private void dive() { 768 | if (!canWahoo || wasRiding || isBackFlipping || isFallFlying()) return; 769 | if (midTripleJump) exitTripleJump(); 770 | if (wallJumping) exitWallJump(); 771 | Level level = level(); 772 | if (level.getBlockState(blockPosition()).isAir() && level.getBlockState(blockPosition().above()).isAir()) { 773 | setPosRaw(position().x(), position().y() + 1, position().z()); 774 | } 775 | isDiving = true; 776 | UpdatePosePacket.send(Pose.SWIMMING); 777 | currentDivingVelocity = new Vec3( 778 | Math.copySign(Math.min(1.5, Math.abs(getDeltaMovement().x()) * 2.25), getDeltaMovement().x()), 779 | getDeltaMovement().y(), 780 | Math.copySign(Math.min(1.5, Math.abs(getDeltaMovement().z()) * 2.25), getDeltaMovement().z()) 781 | ); 782 | setDeltaMovement(currentDivingVelocity.add(0, 0.5, 0)); 783 | diveCooldown = 20; 784 | UpdateDivePacket.sendStart(blockPosition()); 785 | } 786 | 787 | public void exitDive() { 788 | slidingOnSlope = false; 789 | slidingOnGround = false; 790 | isDiving = false; 791 | ticksSlidingOnGround = 0; 792 | flipTimer = 0; 793 | UpdateFlipStatePacket.send(FlipState.NONE); 794 | ticksStillInDive = 0; 795 | if (BingBingWahooConfig.flipSpeedMultiplier != 0 && diveFlip && Minecraft.getInstance().options.getCameraType().isFirstPerson()) setXRot(0); 796 | diveFlip = false; 797 | UpdateDivePacket.sendStop(); 798 | } 799 | 800 | public void bonk() { 801 | if (!canWahoo || wasRiding || !BingBingWahooConfig.bonking) return; 802 | ((KeyboardInputExtensions) input).disableControl(); 803 | if (isDiving) exitDive(); 804 | if (midTripleJump) { 805 | exitTripleJump(); 806 | } 807 | 808 | if (wallJumping) { 809 | exitWallJump(); 810 | } 811 | 812 | setDeltaMovement(-getDeltaMovement().x(), getDeltaMovement().y(), -getDeltaMovement().z()); 813 | UpdateBonkPacket.send(true); 814 | setXRot(-90); 815 | bonked = true; 816 | bonkTime = 30; 817 | bonkCooldown = 20; 818 | } 819 | 820 | public void exitBonk() { 821 | ((KeyboardInputExtensions) input).enableControl(); 822 | bonked = false; 823 | UpdateBonkPacket.send(false); 824 | tryCheckInsideBlocks(); 825 | bonkTime = 0; 826 | setXRot(0); 827 | } 828 | 829 | private void wallJump() { 830 | if (!canWahoo || wasRiding) return; 831 | if (midTripleJump) exitTripleJump(); 832 | 833 | wallJumping = true; 834 | ticksLeftToWallJump = 0; 835 | previousJumpType = JumpType.WALL; 836 | Direction directionOfNearestWall = Direction.UP; 837 | double distanceToNearestWall = 1; 838 | for (Direction direction : Plane.HORIZONTAL) { 839 | BlockState adjacentState = level().getBlockState(blockPosition().relative(direction)); 840 | if (!adjacentState.isAir()) { 841 | double distance = position().distanceTo(Vec3.atCenterOf(blockPosition().relative(direction))); 842 | if (distance <= distanceToNearestWall) { 843 | directionOfNearestWall = direction; 844 | distanceToNearestWall = distance; 845 | } 846 | } 847 | } 848 | 849 | if (directionOfNearestWall == Direction.UP) { 850 | // this shouldn't happen but it does somehow, just cancel the wall jump :bigbrain: 851 | exitWallJump(); 852 | return; 853 | } 854 | 855 | Vec3 directionToGo = Vec3.atLowerCornerOf(directionOfNearestWall.getOpposite().getNormal()); 856 | this.setDeltaMovement(directionToGo.x() / 2, 0.75, directionToGo.z() / 2); 857 | } 858 | 859 | private void exitWallJump() { 860 | wallJumping = false; 861 | } 862 | 863 | private void ledgeGrab() { 864 | if (!canWahoo || wasRiding) return; 865 | if (midTripleJump) exitTripleJump(); 866 | if (isDiving) exitDive(); 867 | if (forwardSliding) exitForwardSlide(); 868 | if (wallJumping) exitWallJump(); 869 | 870 | setYRot(getDirection().toYRot()); 871 | ledgeGrabbing = true; 872 | ledgeGrabExitCooldown = 10; 873 | bonkCooldown = 20; 874 | } 875 | 876 | private void exitLedgeGrab(boolean fall) { 877 | ledgeGrabbing = false; 878 | ledgeGrabCooldown = 20; 879 | if (!fall) { 880 | setDeltaMovement(0, 0.75, 0); 881 | } 882 | } 883 | 884 | private void groundPound() { 885 | if (!canWahoo || wasRiding || isGroundPounding) return; 886 | if (midTripleJump) exitTripleJump(); 887 | if (wallJumping) exitWallJump(); 888 | if (isDiving) exitDive(); 889 | 890 | isGroundPounding = true; 891 | startingGroundPound = true; 892 | flipTimer = 15; 893 | UpdateFlipStatePacket.send(FlipState.FORWARDS); 894 | GroundPoundPacket.send(true); 895 | } 896 | 897 | private void exitGroundPound() { 898 | isGroundPounding = false; 899 | hasGroundPounded = false; 900 | ticksInAirDuringGroundPound = 0; 901 | flipTimer = 0; 902 | UpdateFlipStatePacket.send(FlipState.NONE); 903 | ticksGroundPounded = 0; 904 | GroundPoundPacket.send(false); 905 | } 906 | 907 | private void backFlip() { 908 | if (!canWahoo || wasRiding) return; 909 | isBackFlipping = true; 910 | flipTimer = 20; 911 | UpdateFlipStatePacket.send(FlipState.BACKWARDS); 912 | float x = -Mth.sin(getYRot() * (float) (Math.PI / 180.0)) * Mth.cos(getXRot() * (float) (Math.PI / 180.0)); 913 | float z = Mth.cos(getYRot() * (float) (Math.PI / 180.0)) * Mth.cos(getXRot() * (float) (Math.PI / 180.0)); 914 | this.setDeltaMovement(-x * 0.5, 1, -z * 0.5); 915 | previousJumpType = JumpType.BACK_FLIP; 916 | } 917 | 918 | private void exitBackFlip() { 919 | flipTimer = 0; 920 | UpdateFlipStatePacket.send(FlipState.NONE); 921 | isBackFlipping = false; 922 | if (BingBingWahooConfig.flipSpeedMultiplier != 0 && Minecraft.getInstance().options.getCameraType().isFirstPerson()) setXRot(0); 923 | } 924 | 925 | private void startForwardSliding() { 926 | if (!canWahoo || wasRiding) return; 927 | if (getDeltaMovement() != null) currentDivingVelocity = getDeltaMovement(); 928 | forwardSliding = true; 929 | UpdateSlidePacket.send(true); 930 | } 931 | 932 | private void exitForwardSlide() { 933 | isDiving = false; 934 | slidingOnSlope = false; 935 | slidingOnGround = false; 936 | forwardSliding = false; 937 | if (BingBingWahooConfig.flipSpeedMultiplier != 0 && diveFlip && Minecraft.getInstance().options.getCameraType().isFirstPerson()) setXRot(0); 938 | diveFlip = false; 939 | flipTimer = 0; 940 | UpdateFlipStatePacket.send(FlipState.NONE); 941 | ticksSlidingOnGround = 0; 942 | ticksStillInDive = 0; 943 | UpdateSlidePacket.send(false); 944 | } 945 | 946 | @Override 947 | public int ticksFlipping() { 948 | return flipTimer; 949 | } 950 | 951 | @Override 952 | public void setFlipState(FlipState state) { 953 | flipTimer = state != FlipState.NONE ? 1 : 0; 954 | } 955 | 956 | @Override 957 | public boolean flippingForwards() { 958 | return ticksFlipping() > 0 && !isBackFlipping; 959 | } 960 | 961 | @Override 962 | public void stopAllActions() { 963 | // temporarily make non-destructive 964 | boolean destructive = BingBingWahooConfig.groundPoundType == GroundPoundType.DESTRUCTIVE; 965 | if (destructive) 966 | BingBingWahooConfig.groundPoundType = GroundPoundType.ENABLED; 967 | if (isGroundPounding) 968 | exitGroundPound(); 969 | if (destructive) 970 | BingBingWahooConfig.groundPoundType = GroundPoundType.DESTRUCTIVE; 971 | 972 | if (midTripleJump) 973 | exitTripleJump(); 974 | if (isDiving) 975 | exitDive(); 976 | if (bonked) 977 | exitBonk(); 978 | if (wallJumping) 979 | exitWallJump(); 980 | if (ledgeGrabbing) 981 | exitLedgeGrab(true); 982 | if (isBackFlipping) 983 | exitBackFlip(); 984 | if (forwardSliding) 985 | exitForwardSlide(); 986 | } 987 | } 988 | --------------------------------------------------------------------------------