├── .gitattributes ├── .gitignore ├── LICENSE ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── src └── main ├── java └── com │ └── gluecode │ └── fpvdrone │ ├── Main.java │ ├── Resource.java │ ├── audio │ └── DroneSound.java │ ├── entity │ ├── ColorModelRenderer.java │ ├── DroneBuild.java │ ├── DroneEntity.java │ ├── DroneModel.java │ ├── DronePropsLayer.java │ ├── DroneRenderer.java │ └── PropModelRenderer.java │ ├── gui │ ├── GuiEvents.java │ ├── entry │ │ ├── BooleanEntry.java │ │ ├── CategoryEntry.java │ │ ├── ChannelEntry.java │ │ ├── DoubleButtonEntry.java │ │ ├── DoubleNavEntry.java │ │ ├── FPVEntry.java │ │ ├── FloatEntry.java │ │ ├── IntEntry.java │ │ └── SingleButtonEntry.java │ ├── list │ │ ├── ChannelMappingList.java │ │ ├── ControllerChoicesList.java │ │ ├── DroneBuildList.java │ │ ├── FPVList.java │ │ ├── MainSettingsList.java │ │ ├── ModelChoiceList.java │ │ ├── ModelSettingsList.java │ │ ├── OtherSettingsList.java │ │ └── RatesList.java │ ├── screen │ │ ├── ChannelMappingScreen.java │ │ ├── ControllerChoicesScreen.java │ │ ├── DroneBuildScreen.java │ │ ├── EmptyListScreen.java │ │ ├── FpvScreen.java │ │ ├── MainSettingsScreen.java │ │ ├── ModelChoicesScreen.java │ │ ├── ModelSettingsScreen.java │ │ ├── OtherSettingsScreen.java │ │ ├── RatesScreen.java │ │ ├── addon │ │ │ ├── BackFooter.java │ │ │ ├── BackHelpFooter.java │ │ │ ├── BackProceedFooter.java │ │ │ ├── DoneFooter.java │ │ │ ├── ScreenAddon.java │ │ │ ├── ServerTitleWikiHeader.java │ │ │ ├── WizardDoneFooter.java │ │ │ └── WizardHeader.java │ │ └── wizard │ │ │ ├── CalibrateControllerArmScreen.java │ │ │ ├── CalibrateControllerStickScreen.java │ │ │ ├── CalibrateKeyboardScreen.java │ │ │ ├── ChooseControllerScreen.java │ │ │ ├── CompleteScreen.java │ │ │ ├── DecideControllerScreen.java │ │ │ ├── HelpQAScreen.java │ │ │ ├── HelpScreen.java │ │ │ ├── WelcomeScreen.java │ │ │ └── WizardConfig.java │ └── widget │ │ ├── FpvSettingsButton.java │ │ ├── RateChart.java │ │ └── SnappySlider.java │ ├── input │ ├── ControllerConfig.java │ ├── ControllerEvent.java │ ├── ControllerReader.java │ ├── JoystickCallback.java │ ├── KeyManager.java │ ├── KeyMappingInterceptor.java │ └── MouseManager.java │ ├── network │ ├── DroneState.java │ ├── Network.java │ ├── VersionNotifier.java │ └── packet │ │ ├── DroneBuildPacket.java │ │ ├── DroneStatePacket.java │ │ ├── GateIndexPacket.java │ │ ├── LapBestPacket.java │ │ ├── LapStartPacket.java │ │ ├── PacketHandler.java │ │ ├── SetArmPacket.java │ │ ├── SetBuildModePacket.java │ │ └── SetRaceModePacket.java │ ├── physics │ ├── AdvancedPhysicsCore.java │ ├── CollisionResults.java │ ├── DefaultPhysicsCore.java │ ├── IPhysicsCore.java │ ├── PhysicsCollision.java │ ├── PhysicsConstants.java │ ├── PhysicsCoreLoader.java │ ├── PhysicsEvent.java │ └── PhysicsState.java │ ├── race │ ├── LapTime.java │ ├── RaceClient.java │ ├── SerialRaceGate.java │ └── SerialRaceTrack.java │ ├── render │ ├── BarrelRenderer.java │ ├── BlurryPropRenderer.java │ ├── BuildModeRenderer.java │ ├── CameraManager.java │ ├── DebugRenderer.java │ ├── HUD.java │ ├── LapRender.java │ ├── RenderingHelper.java │ ├── StickOverlayRenderer.java │ └── shader │ │ ├── ShaderObject.java │ │ └── ShaderProgram.java │ └── util │ ├── SettingsLoader.java │ └── Transforms.java └── resources ├── META-INF ├── accesstransformer.cfg └── mods.toml ├── assets └── fpvdrone │ ├── lang │ └── en_us.json │ ├── shaders │ ├── barrel_fragment.glsl │ ├── barrel_vertex.glsl │ ├── build_gate_fragment.glsl │ ├── build_gate_vertex.glsl │ └── copy_fragment.glsl │ └── textures │ └── entity │ ├── drone.png │ ├── props.png │ ├── props2.png │ ├── props3.png │ ├── props4.png │ └── props5.png └── pack.mcmeta /.gitattributes: -------------------------------------------------------------------------------- 1 | # 2 | # https://help.github.com/articles/dealing-with-line-endings/ 3 | # 4 | # These are explicitly windows files and should use crlf 5 | *.bat text eol=crlf 6 | 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | run 2 | out 3 | logs 4 | build 5 | .idea 6 | .gradle 7 | .env 8 | release.sh 9 | releaseToServer.sh 10 | checkRelease.sh 11 | jd-cli.jar 12 | proguard.jar 13 | proguardconf 14 | version.json 15 | settings.gradle 16 | .DS_Store 17 | .vscode 18 | bin 19 | # For Manifold: 20 | build.properties 21 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Sets default memory used for gradle commands. Can be overridden by user or command line properties. 2 | # This is required to provide enough memory for the Minecraft decompilation process. 3 | org.gradle.jvmargs=-Xmx3G 4 | org.gradle.daemon=false -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minecraft-fpv/mfpv-client/d567eaffe3badd899ef77461ed422f9a2043702b/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minecraft-fpv/mfpv-client/d567eaffe3badd899ef77461ed422f9a2043702b/gradlew -------------------------------------------------------------------------------- /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/com/gluecode/fpvdrone/Resource.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone; 2 | 3 | public class Resource { 4 | public static String shaders_build_gate_vertex = "shaders/build_gate_vertex.glsl"; 5 | public static String shaders_build_gate_fragment = "shaders/build_gate_fragment.glsl"; 6 | public static String shaders_copy_fragment = "shaders/copy_fragment.glsl"; 7 | public static String shaders_barrel_fragment = "shaders/barrel_fragment.glsl"; 8 | public static String shaders_barrel_vertex = "shaders/barrel_vertex.glsl"; 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/audio/DroneSound.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.audio; 2 | 3 | import com.gluecode.fpvdrone.entity.DroneBuild; 4 | import com.gluecode.fpvdrone.input.ControllerReader; 5 | import com.gluecode.fpvdrone.physics.PhysicsState; 6 | import com.jme3.math.FastMath; 7 | import net.minecraft.client.player.LocalPlayer; 8 | import net.minecraft.client.resources.sounds.AbstractTickableSoundInstance; 9 | import net.minecraft.sounds.SoundEvents; 10 | import net.minecraft.sounds.SoundSource; 11 | import net.minecraft.util.Mth; 12 | import net.minecraftforge.api.distmarker.Dist; 13 | import net.minecraftforge.api.distmarker.OnlyIn; 14 | 15 | @OnlyIn(Dist.CLIENT) 16 | public class DroneSound extends AbstractTickableSoundInstance { 17 | private final LocalPlayer player; 18 | 19 | public DroneSound(LocalPlayer playerIn) { 20 | super(SoundEvents.ELYTRA_FLYING, SoundSource.PLAYERS); 21 | this.player = playerIn; 22 | this.looping = true; 23 | this.delay = 0; 24 | this.volume = 0.1F; 25 | } 26 | 27 | public void tick() { 28 | // float throt = FastMath.clamp((InputHandler.throttle + 1F) * 0.5F, 0F, 1.0F); 29 | if (!this.player.isRemoved() && ControllerReader.getArm()) { 30 | this.x = (float) this.player.getX(); 31 | this.y = (float) this.player.getY(); 32 | this.z = (float) this.player.getZ(); 33 | 34 | float kv = (DroneBuild.getMotorKv() * 0.104719755f); 35 | float maxSpeed = kv * DroneBuild.getBatteryCells() * 4.2f; 36 | float lerp = FastMath.abs(PhysicsState.getCore().getMotorVel()[0]) / maxSpeed; 37 | 38 | this.volume = Mth.lerp(lerp, 0.25f, 1f); 39 | this.pitch = Mth.lerp(lerp, 0.5f, 2f); 40 | 41 | // Main.LOGGER.debug("volume: " + this.volume); 42 | // Main.LOGGER.debug("pitch: " + this.pitch); 43 | } else { 44 | this.stop(); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/entity/ColorModelRenderer.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.entity; 2 | 3 | import com.mojang.blaze3d.vertex.PoseStack; 4 | import com.mojang.blaze3d.vertex.VertexConsumer; 5 | import net.minecraft.client.model.Model; 6 | import net.minecraft.client.renderer.model.ModelRenderer; 7 | 8 | public class ColorModelRenderer extends ModelRenderer { 9 | public float red; 10 | public float green; 11 | public float blue; 12 | public float alpha; 13 | 14 | public ColorModelRenderer( 15 | Model model, 16 | float red, 17 | float green, 18 | float blue, 19 | float alpha 20 | ) { 21 | super(model); 22 | this.red = red; 23 | this.green = green; 24 | this.blue = blue; 25 | this.alpha = alpha; 26 | } 27 | 28 | @Override 29 | public void compile( 30 | PoseStack.Entry matrixEntryIn, 31 | VertexConsumer bufferIn, 32 | int packedLightIn, 33 | int packedOverlayIn, 34 | float red, 35 | float green, 36 | float blue, 37 | float alpha 38 | ) { 39 | super.compile( 40 | matrixEntryIn, 41 | bufferIn, 42 | packedLightIn, 43 | packedOverlayIn, 44 | this.red, 45 | this.green, 46 | this.blue, 47 | this.alpha 48 | ); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/entity/DroneEntity.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.entity; 2 | 3 | import com.gluecode.fpvdrone.Main; 4 | import com.mojang.authlib.GameProfile; 5 | import net.minecraft.client.player.AbstractClientPlayer; 6 | import net.minecraft.client.world.ClientWorld; 7 | import net.minecraft.entity.*; 8 | import net.minecraft.entity.ai.attributes.AttributeModifierMap; 9 | import net.minecraft.entity.ai.attributes.Attributes; 10 | import net.minecraft.entity.item.BoatEntity; 11 | import net.minecraft.entity.player.PlayerEntity; 12 | import net.minecraft.core.BlockPos; 13 | import net.minecraft.world.level.Level; 14 | import net.minecraftforge.event.RegistryEvent; 15 | import net.minecraftforge.eventbus.api.SubscribeEvent; 16 | 17 | public class DroneEntity extends AbstractClientPlayer { 18 | public DroneEntity(ClientWorld world, GameProfile profile) { 19 | super(world, profile); 20 | } 21 | // public DroneEntity(EntityType type, World worldIn) { 22 | // super(type, worldIn); 23 | // } 24 | 25 | // public static AttributeModifierMap.MutableAttribute setCustomAttributes() { 26 | // // func_233666_p_() = registerAttributes 27 | // return MobEntity.func_233666_p_() 28 | // .createMutableAttribute(Attributes.MAX_HEALTH, 20) 29 | // .createMutableAttribute(Attributes.MOVEMENT_SPEED, 4.317); 30 | // } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/entity/DronePropsLayer.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.entity; 2 | 3 | import com.gluecode.fpvdrone.Main; 4 | import com.mojang.blaze3d.vertex.PoseStack; 5 | import com.mojang.blaze3d.vertex.VertexConsumer; 6 | import net.minecraft.client.player.AbstractClientPlayer; 7 | import net.minecraft.client.renderer.IRenderTypeBuffer; 8 | import net.minecraft.client.renderer.RenderType; 9 | import net.minecraft.client.renderer.entity.IEntityRenderer; 10 | import net.minecraft.client.renderer.entity.LivingEntityRenderer; 11 | import net.minecraft.client.renderer.entity.layers.LayerRenderer; 12 | import net.minecraft.entity.LivingEntity; 13 | import net.minecraft.resources.ResourceLocation; 14 | 15 | import java.util.UUID; 16 | 17 | public class DronePropsLayer extends LayerRenderer> { 18 | private final DroneModel droneModel; 19 | 20 | public DronePropsLayer( 21 | UUID uuid, 22 | IEntityRenderer> entityRendererIn, 23 | DroneBuild build 24 | ) { 25 | super(entityRendererIn); 26 | droneModel = new DroneModel<>( 27 | uuid, 28 | build, 29 | true 30 | ); // make a new drone model with different params 31 | droneModel.renderer = (DroneRenderer) entityRendererIn; 32 | } 33 | 34 | @Override 35 | public void render( 36 | PoseStack matrixStackIn, 37 | IRenderTypeBuffer bufferIn, 38 | int packedLightIn, 39 | AbstractClientPlayer entitylivingbaseIn, 40 | float limbSwing, 41 | float limbSwingAmount, 42 | float partialTicks, 43 | float ageInTicks, 44 | float netHeadYaw, 45 | float headPitch 46 | ) { 47 | if (!entitylivingbaseIn.isInvisible()) { 48 | this.getParentModel().copyPropertiesTo(this.droneModel); 49 | this.droneModel.prepareMobModel( 50 | entitylivingbaseIn, 51 | limbSwing, 52 | limbSwingAmount, 53 | partialTicks 54 | ); 55 | this.droneModel.setupAnim( 56 | entitylivingbaseIn, 57 | limbSwing, 58 | limbSwingAmount, 59 | ageInTicks, 60 | netHeadYaw, 61 | headPitch 62 | ); 63 | // VertexConsumer ivertexbuilder = bufferIn.getBuffer(RenderType.entityTranslucentCull(this.getEntityTexture(entitylivingbaseIn))); 64 | 65 | VertexConsumer ivertexbuilder; 66 | // if (this.blur) { 67 | // // What we set here doesn't actually matter because PropModelRenderer will override it. 68 | // ivertexbuilder = bufferIn.getBuffer(RenderType.entityTranslucentCull( 69 | // new ResourceLocation( 70 | // Main.MOD_ID, 71 | // "textures/entity/props3.png" 72 | // ))); 73 | // } else { 74 | ivertexbuilder = bufferIn.getBuffer(RenderType.entityTranslucentCull( 75 | new ResourceLocation( 76 | Main.MOD_ID, 77 | "textures/entity/drone.png" 78 | ))); 79 | // } 80 | 81 | this.droneModel.renderToBuffer( 82 | matrixStackIn, 83 | ivertexbuilder, 84 | packedLightIn, 85 | LivingEntityRenderer.getOverlayCoords(entitylivingbaseIn, 0.0F), 86 | 1.0F, 87 | 1.0F, 88 | 1.0F, 89 | 1.0F 90 | ); 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/gui/GuiEvents.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.gui; 2 | 3 | import com.gluecode.fpvdrone.render.CameraManager; 4 | import com.gluecode.fpvdrone.Main; 5 | import com.gluecode.fpvdrone.gui.screen.wizard.*; 6 | import com.gluecode.fpvdrone.gui.widget.FpvSettingsButton; 7 | import com.gluecode.fpvdrone.gui.screen.*; 8 | import com.gluecode.fpvdrone.input.ControllerReader; 9 | import com.gluecode.fpvdrone.util.SettingsLoader; 10 | import net.minecraft.client.Minecraft; 11 | import net.minecraft.client.gui.screen.ControlsScreen; 12 | import net.minecraft.client.gui.screen.Screen; 13 | import net.minecraft.client.gui.screen.SettingsScreen; 14 | import net.minecraftforge.api.distmarker.Dist; 15 | import net.minecraftforge.api.distmarker.OnlyIn; 16 | import net.minecraftforge.client.event.GuiScreenEvent; 17 | import net.minecraftforge.client.event.RenderGameOverlayEvent; 18 | import net.minecraftforge.client.event.RenderHandEvent; 19 | import net.minecraftforge.eventbus.api.SubscribeEvent; 20 | import net.minecraftforge.fml.common.Mod; 21 | import net.minecraftforge.fml.common.ObfuscationReflectionHelper; 22 | 23 | import java.lang.reflect.Field; 24 | 25 | @OnlyIn(Dist.CLIENT) 26 | @Mod.EventBusSubscriber(value = Dist.CLIENT, modid = Main.MOD_ID) 27 | public class GuiEvents { 28 | public static final Field gameSettings = ObfuscationReflectionHelper.findField( 29 | SettingsScreen.class, 30 | "field_228183_b_" 31 | ); 32 | 33 | @SubscribeEvent 34 | public static void onOpenGui(GuiScreenEvent.InitGuiEvent.Post event) { 35 | if (event.getGui() instanceof ControlsScreen) { 36 | ControlsScreen screen = (ControlsScreen) event.getGui(); 37 | 38 | int screenWidth = screen.width; 39 | 40 | event.addWidget(new FpvSettingsButton((screenWidth / 2) + 41 | 5 + 42 | 150 + 43 | 4, 18, 44 | button -> GuiEvents.enterFpvSettings(screen) 45 | )); 46 | } 47 | } 48 | 49 | @SubscribeEvent 50 | public static void onRenderHand(RenderHandEvent event) { 51 | if (ControllerReader.getArm()) { 52 | event.setCanceled(true); 53 | } 54 | } 55 | 56 | @SubscribeEvent 57 | public static void onRenderGameOverlay(RenderGameOverlayEvent event) { 58 | if (ControllerReader.getArm()) { 59 | if ( 60 | event.getType() == RenderGameOverlayEvent.ElementType.HOTBAR 61 | // event.getType() == RenderGameOverlayEvent.ElementType.EXPERIENCE 62 | // event.getType() == RenderGameOverlayEvent.ElementType.FOOD 63 | ) { 64 | event.setCanceled(true); 65 | } else if (event.getType() == 66 | RenderGameOverlayEvent.ElementType.CROSSHAIRS && 67 | !CameraManager.getShowCrosshairs()) { 68 | event.setCanceled(true); 69 | } 70 | } 71 | } 72 | 73 | public static void enterFpvSettings(ControlsScreen screen) { 74 | if (SettingsLoader.firstTimeSetup) { 75 | openWelcomeScreen(screen); 76 | } else { 77 | openFpvSettingsScreen(screen); 78 | } 79 | } 80 | 81 | public static void openWelcomeScreen(Screen screen) { 82 | Minecraft.getInstance().setScreen(new WelcomeScreen( 83 | screen 84 | )); 85 | } 86 | 87 | public static void openDecideControllerScreen(WelcomeScreen screen) { 88 | Minecraft.getInstance().setScreen(new DecideControllerScreen( 89 | screen 90 | )); 91 | } 92 | 93 | public static void openChooseControllerScreen(DecideControllerScreen screen) { 94 | Minecraft.getInstance().setScreen(new ChooseControllerScreen( 95 | screen 96 | )); 97 | } 98 | 99 | public static void openCalibrateControllerStickScreen(ChooseControllerScreen screen) { 100 | Minecraft.getInstance().setScreen(new CalibrateControllerStickScreen( 101 | screen 102 | )); 103 | } 104 | 105 | public static void openCalibrateControllerArmScreen(CalibrateControllerStickScreen screen) { 106 | Minecraft.getInstance().setScreen(new CalibrateControllerArmScreen( 107 | screen 108 | )); 109 | } 110 | 111 | public static void openCompleteSwitchScreen(Screen screen) { 112 | Minecraft.getInstance().setScreen(new CompleteScreen( 113 | screen, 114 | CompleteScreen.Mode.SWITCH 115 | )); 116 | } 117 | 118 | public static void openCompleteKeyScreen(Screen screen) { 119 | Minecraft.getInstance().setScreen(new CompleteScreen( 120 | screen, 121 | CompleteScreen.Mode.KEY 122 | )); 123 | } 124 | 125 | public static void openCalibrateKeyboardScreen(DecideControllerScreen screen) { 126 | Minecraft.getInstance().setScreen(new CalibrateKeyboardScreen( 127 | screen 128 | )); 129 | } 130 | 131 | public static void openFpvSettingsScreen(Screen screen) { 132 | openModelSettingsScreen(screen); 133 | // Minecraft.getInstance().setScreen(new MainSettingsScreen( 134 | // screen 135 | // )); 136 | } 137 | 138 | public static void openControllerChoicesScreen(Screen screen) { 139 | Minecraft.getInstance().setScreen(new ControllerChoicesScreen( 140 | screen 141 | )); 142 | } 143 | 144 | public static void openChannelMappingScreen(ModelSettingsScreen screen) { 145 | Minecraft.getInstance() 146 | .setScreen(new ChannelMappingScreen(screen)); 147 | } 148 | 149 | public static void openRatesScreen(ModelSettingsScreen screen) { 150 | Minecraft.getInstance().setScreen(new RatesScreen( 151 | screen 152 | )); 153 | } 154 | 155 | public static void openDroneBuildScreen(ModelSettingsScreen screen) { 156 | Minecraft.getInstance().setScreen(new DroneBuildScreen( 157 | screen 158 | )); 159 | } 160 | 161 | public static void openModelChoicesScreen(Screen screen) { 162 | Minecraft.getInstance().setScreen(new ModelChoicesScreen( 163 | screen 164 | )); 165 | } 166 | 167 | public static void openModelSettingsScreen(Screen screen) { 168 | Minecraft.getInstance().setScreen(new ModelSettingsScreen( 169 | screen 170 | )); 171 | } 172 | 173 | public static void openMiscSettingsScreen(ModelSettingsScreen screen) { 174 | Minecraft.getInstance().setScreen(new OtherSettingsScreen( 175 | screen 176 | )); 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/gui/entry/BooleanEntry.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.gui.entry; 2 | 3 | import com.gluecode.fpvdrone.gui.list.FPVList; 4 | import com.gluecode.fpvdrone.util.SettingsLoader; 5 | import com.google.common.collect.ImmutableList; 6 | import com.mojang.blaze3d.vertex.PoseStack; 7 | import net.minecraft.client.gui.FontRenderer; 8 | import net.minecraft.client.gui.IGuiEventListener; 9 | import net.minecraft.client.gui.widget.Widget; 10 | import net.minecraft.client.gui.widget.button.Button; 11 | import net.minecraft.client.resources.I18n; 12 | import net.minecraft.util.text.StringTextComponent; 13 | 14 | import javax.annotation.Nullable; 15 | import java.util.List; 16 | import java.util.function.Consumer; 17 | import java.util.function.Supplier; 18 | 19 | public class BooleanEntry extends FPVEntry { 20 | private Supplier getValue; 21 | private Consumer setNewValue; 22 | private Runnable setDefaultValue; 23 | private Supplier getDefaultValue; 24 | private Button changeButton; 25 | private Button resetButton; 26 | 27 | public BooleanEntry( 28 | FPVList list, 29 | String name, 30 | Supplier getValue, 31 | Consumer setNewValue, 32 | Supplier getDefaultValue, 33 | Runnable setDefaultValue 34 | ) { 35 | super(list, name); 36 | this.name = name; 37 | this.getValue = getValue; 38 | this.setNewValue = setNewValue; 39 | this.getDefaultValue = getDefaultValue; 40 | this.setDefaultValue = setDefaultValue; 41 | this.changeButton = new Button( 42 | 0, 43 | 0, 44 | 70, 45 | 20, 46 | new StringTextComponent(name), 47 | this::handleChangePress 48 | ); 49 | this.resetButton = new Button( 50 | 0, 51 | 0, 52 | 50, 53 | 20, 54 | new StringTextComponent(I18n.get("controls.reset")), 55 | this::handleResetPress 56 | ); 57 | this.editMode = false; 58 | } 59 | 60 | @Override 61 | public boolean isLetterAcceptable(String letter) { 62 | return false; 63 | } 64 | 65 | @Override 66 | public void betterRender( 67 | PoseStack matrixStack, 68 | FontRenderer fontRenderer, 69 | int rowIndex, 70 | int rowTop, 71 | int rowLeft, 72 | int rowWidth, 73 | int rowHeight, 74 | int mouseX, 75 | int mouseY, 76 | boolean isMouseOver, 77 | float partialTicks 78 | ) { 79 | fontRenderer.draw( 80 | matrixStack, 81 | this.name, 82 | rowLeft, 83 | (float) (rowTop + 6), 84 | 16777215 85 | ); 86 | 87 | int right = rowLeft + rowWidth; 88 | int resetWidth = this.resetButton.getWidth(); 89 | int changeWidth = this.changeButton.getWidth(); 90 | 91 | this.resetButton.x = right - resetWidth; 92 | this.resetButton.y = rowTop; 93 | this.resetButton.active = this.getDefaultValue.get() != 94 | this.getValue.get(); 95 | this.resetButton.render(matrixStack, mouseX, mouseY, partialTicks); 96 | this.changeButton.x = right - resetWidth - 1 - changeWidth; 97 | this.changeButton.y = rowTop; 98 | 99 | this.changeButton.setMessage( 100 | new StringTextComponent("" + this.getValue.get()) 101 | ); 102 | 103 | this.changeButton.render(matrixStack, mouseX, mouseY, partialTicks); 104 | } 105 | 106 | @Override 107 | public List children() { 108 | return ImmutableList.of(this.changeButton, this.resetButton); 109 | } 110 | 111 | public void handleChangePress(@Nullable Widget button) { 112 | super.handleChangePress(button); 113 | 114 | setNewValue.accept(!getValue.get()); 115 | SettingsLoader.save(); 116 | } 117 | 118 | public void handleResetPress(Button button) { 119 | this.setDefaultValue.run(); 120 | SettingsLoader.save(); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/gui/entry/CategoryEntry.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.gui.entry; 2 | 3 | import com.gluecode.fpvdrone.gui.list.FPVList; 4 | import com.gluecode.fpvdrone.input.ControllerReader; 5 | import com.mojang.blaze3d.vertex.PoseStack; 6 | import net.minecraft.client.gui.FontRenderer; 7 | import net.minecraft.client.gui.IGuiEventListener; 8 | 9 | import java.util.Collections; 10 | import java.util.List; 11 | import java.util.function.Supplier; 12 | 13 | public class CategoryEntry extends FPVEntry { 14 | private Supplier title; 15 | private int color = 16777215; 16 | 17 | public CategoryEntry(FPVList list, Supplier title) { 18 | super(list, ""); 19 | this.title = title; 20 | } 21 | 22 | public CategoryEntry(FPVList list, Supplier title, int color) { 23 | super(list, ""); 24 | this.title = title; 25 | this.color = color; 26 | } 27 | 28 | @Override 29 | public boolean isLetterAcceptable(String letter) { 30 | return false; 31 | } 32 | 33 | @Override 34 | public void betterRender( 35 | PoseStack matrixStack, 36 | FontRenderer fontRenderer, 37 | int rowIndex, 38 | int rowTop, 39 | int rowLeft, 40 | int rowWidth, 41 | int rowHeight, 42 | int mouseX, 43 | int mouseY, 44 | boolean isMouseOver, 45 | float partialTicks 46 | ) { 47 | String label; 48 | String name = this.title.get(); 49 | if (name.startsWith("Axis Mapping")) { 50 | label = "Axis Mapping (CH 1 - " + 51 | ControllerReader.getAxisLength() + 52 | ")"; 53 | } else if (name.startsWith("Switch Mapping")) { 54 | label = 55 | "Switch Mapping (CH " + 56 | (ControllerReader.getAxisLength() + 1) + 57 | " - " + 58 | (ControllerReader.getAxisLength() + 59 | ControllerReader.getButtonLength()) + 60 | ")"; 61 | } else { 62 | label = name; 63 | } 64 | 65 | int labelWidth = fontRenderer.width(label); 66 | 67 | fontRenderer.draw( 68 | matrixStack, 69 | label, 70 | (float) (rowLeft + rowWidth / 2 - labelWidth / 2), 71 | (float) (rowTop + 6), 72 | this.color 73 | ); 74 | } 75 | 76 | @Override 77 | public List children() { 78 | return Collections.emptyList(); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/gui/entry/DoubleButtonEntry.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.gui.entry; 2 | 3 | import com.gluecode.fpvdrone.gui.list.FPVList; 4 | import com.google.common.collect.ImmutableList; 5 | import com.mojang.blaze3d.vertex.PoseStack; 6 | import net.minecraft.client.gui.FontRenderer; 7 | import net.minecraft.client.gui.IGuiEventListener; 8 | import net.minecraft.client.gui.widget.button.Button; 9 | import net.minecraft.client.resources.I18n; 10 | import net.minecraft.util.text.StringTextComponent; 11 | 12 | import javax.annotation.Nullable; 13 | import java.util.List; 14 | import java.util.function.Supplier; 15 | 16 | public class DoubleButtonEntry extends FPVEntry { 17 | private Supplier getName; 18 | private Button changeButton; 19 | private Button configureButton; 20 | private Supplier disabled; 21 | 22 | public DoubleButtonEntry( 23 | FPVList list, 24 | Supplier getName, 25 | String leftLabel, 26 | Runnable onLeft, 27 | String rightLabel, 28 | Runnable onRight, 29 | Supplier disabled 30 | ) { 31 | super(list, ""); 32 | this.disabled = disabled; 33 | this.getName = getName; 34 | this.configureButton = new Button( 35 | 0, 36 | 0, 37 | 80, 38 | 20, 39 | new StringTextComponent(leftLabel), 40 | (@Nullable Button button) -> { 41 | onLeft.run(); 42 | } 43 | ); 44 | this.changeButton = new Button( 45 | 0, 46 | 0, 47 | 80, 48 | 20, 49 | new StringTextComponent(rightLabel), 50 | (@Nullable Button button) -> { 51 | onRight.run(); 52 | } 53 | ); 54 | } 55 | 56 | @Override 57 | public boolean isLetterAcceptable(String letter) { 58 | return false; 59 | } 60 | 61 | @Override 62 | public void betterRender( 63 | PoseStack matrixStack, 64 | FontRenderer fontRenderer, 65 | int rowIndex, 66 | int rowTop, 67 | int rowLeft, 68 | int rowWidth, 69 | int rowHeight, 70 | int mouseX, 71 | int mouseY, 72 | boolean isMouseOver, 73 | float partialTicks 74 | ) { 75 | fontRenderer.draw( 76 | matrixStack, 77 | this.getName.get(), 78 | rowLeft, 79 | (float) (rowTop + 6), 80 | 16777215 81 | ); 82 | 83 | int right = rowLeft + rowWidth; 84 | int configureWidth = this.configureButton.getWidth(); 85 | int changeWidth = this.changeButton.getWidth(); 86 | 87 | this.configureButton.x = right - changeWidth - 1 - configureWidth; 88 | this.configureButton.y = rowTop; 89 | this.configureButton.active = !this.disabled.get(); 90 | this.configureButton.render( 91 | matrixStack, 92 | mouseX, 93 | mouseY, 94 | partialTicks 95 | ); 96 | 97 | this.changeButton.x = right - changeWidth; 98 | this.changeButton.y = rowTop; 99 | this.changeButton.active = !this.disabled.get(); 100 | this.changeButton.render(matrixStack, mouseX, mouseY, partialTicks); 101 | } 102 | 103 | @Override 104 | public List children() { 105 | return ImmutableList.of(this.changeButton, this.configureButton); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/gui/entry/DoubleNavEntry.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.gui.entry; 2 | 3 | import com.gluecode.fpvdrone.gui.list.FPVList; 4 | import com.google.common.collect.ImmutableList; 5 | import com.mojang.blaze3d.vertex.PoseStack; 6 | import net.minecraft.client.gui.FontRenderer; 7 | import net.minecraft.client.gui.IGuiEventListener; 8 | import net.minecraft.client.gui.screen.Screen; 9 | import net.minecraft.client.gui.widget.button.Button; 10 | import net.minecraft.util.text.StringTextComponent; 11 | 12 | import javax.annotation.Nullable; 13 | import java.util.List; 14 | 15 | public class DoubleNavEntry extends FPVEntry { 16 | private Button leftButton; 17 | private Button rightButton; 18 | 19 | public DoubleNavEntry( 20 | FPVList list, 21 | String leftName, 22 | Runnable onLeftSelect, 23 | String rightName, 24 | Runnable onRightSelect 25 | ) { 26 | super(list, ""); 27 | this.leftButton = new Button( 28 | 0, 29 | 0, 30 | 150, 31 | 20, 32 | new StringTextComponent(leftName), 33 | (Button button) -> { 34 | onLeftSelect.run(); 35 | } 36 | ); 37 | this.rightButton = new Button( 38 | 0, 39 | 0, 40 | 150, 41 | 20, 42 | new StringTextComponent(rightName), 43 | (Button button) -> { 44 | onRightSelect.run(); 45 | } 46 | ); 47 | } 48 | 49 | @Override 50 | public boolean isLetterAcceptable(String letter) { 51 | return false; 52 | } 53 | 54 | @Override 55 | public void betterRender( 56 | PoseStack matrixStack, 57 | FontRenderer fontRenderer, 58 | int rowIndex, 59 | int rowTop, 60 | int rowLeft, 61 | int rowWidth, 62 | int rowHeight, 63 | int mouseX, 64 | int mouseY, 65 | boolean isMouseOver, 66 | float partialTicks 67 | ) { 68 | this.leftButton.x = rowLeft + rowWidth / 2 - 150 - 5; 69 | this.leftButton.y = rowTop; 70 | this.leftButton.render(matrixStack, mouseX, mouseY, partialTicks); 71 | 72 | this.rightButton.x = rowLeft + rowWidth / 2 + 5; 73 | this.rightButton.y = rowTop; 74 | this.rightButton.render(matrixStack, mouseX, mouseY, partialTicks); 75 | } 76 | 77 | @Override 78 | public List children() { 79 | return ImmutableList.of(this.leftButton, this.rightButton); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/gui/entry/FPVEntry.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.gui.entry; 2 | 3 | import com.gluecode.fpvdrone.Main; 4 | import com.gluecode.fpvdrone.gui.list.FPVList; 5 | import com.mojang.blaze3d.vertex.PoseStack; 6 | import net.minecraft.client.Minecraft; 7 | import net.minecraft.client.gui.FontRenderer; 8 | import net.minecraft.client.gui.screen.Screen; 9 | import net.minecraft.client.gui.widget.Widget; 10 | import net.minecraft.client.gui.widget.list.AbstractOptionList; 11 | import net.minecraft.client.util.InputMappings; 12 | import net.minecraftforge.api.distmarker.Dist; 13 | import net.minecraftforge.api.distmarker.OnlyIn; 14 | import org.lwjgl.glfw.GLFW; 15 | 16 | import javax.annotation.Nullable; 17 | 18 | @OnlyIn(Dist.CLIENT) 19 | public abstract class FPVEntry extends AbstractOptionList.Entry { 20 | public FPVList list; 21 | public String name; 22 | public String editValue = ""; 23 | public boolean editMode = false; 24 | 25 | public FPVEntry(FPVList list, String name) { 26 | this.list = list; 27 | this.name = name; 28 | } 29 | 30 | public void handleChangePress(@Nullable Widget button) { 31 | if (this.list.activeEntry != null && 32 | this.list.activeEntry != this && 33 | this.list.activeEntry.editMode) { 34 | // There is already an activeEntry. 35 | // simulate clicking the other button first to deactivate it. 36 | this.list.activeEntry.handleChangePress(button); 37 | } 38 | this.list.activeEntry = this; 39 | } 40 | 41 | public int maxValueLength() { 42 | return 7; 43 | } 44 | 45 | abstract public boolean isLetterAcceptable(String letter); 46 | 47 | public boolean keyPressed( 48 | int keyCode, 49 | int scanCode, 50 | int modifiers 51 | ) { 52 | InputMappings.Input input = InputMappings.getKey( 53 | keyCode, 54 | scanCode 55 | ); 56 | if (editMode) { 57 | if (input.toString().equals("key.keyboard.backspace") && this.editValue.length() > 0) { 58 | this.editValue = this.editValue.substring( 59 | 0, 60 | this.editValue.length() - 1 61 | ); 62 | } else if (this.editValue.length() < this.maxValueLength()) { 63 | String letter = GLFW.glfwGetKeyName(input.getValue(), -1); 64 | if (letter == null) { 65 | return super.keyPressed( 66 | keyCode, 67 | scanCode, 68 | modifiers 69 | ); 70 | } 71 | if (isLetterAcceptable(letter)) { 72 | this.editValue = this.editValue + letter; 73 | return true; 74 | } 75 | } 76 | return super.keyPressed( 77 | keyCode, 78 | scanCode, 79 | modifiers 80 | ); 81 | } else { 82 | return super.keyPressed( 83 | keyCode, 84 | scanCode, 85 | modifiers 86 | ); 87 | } 88 | } 89 | 90 | abstract public void betterRender( 91 | PoseStack matrixStack, 92 | FontRenderer fontRenderer, 93 | int rowIndex, 94 | int rowTop, 95 | int rowLeft, 96 | int rowWidth, 97 | int rowHeight, 98 | int mouseX, 99 | int mouseY, 100 | boolean isMouseOver, 101 | float partialTicks 102 | ); 103 | 104 | public void render( 105 | PoseStack matrixStack, 106 | int rowIndex, 107 | int rowTop, 108 | int rowLeft, 109 | int rowWidth, 110 | int rowHeight, 111 | int mouseX, 112 | int mouseY, 113 | boolean isMouseOver, 114 | float partialTicks 115 | ) { 116 | Minecraft minecraft = Minecraft.getInstance(); 117 | Screen currentScreen = minecraft.screen; 118 | if (currentScreen != null) { 119 | rowLeft = this.list.getLeftPadding(); 120 | rowWidth = currentScreen.width - this.list.getLeftPadding() - this.list.getRightPadding(); 121 | boolean showScrollBar = this.list.getMaxScroll() > 0; 122 | if (showScrollBar) { 123 | rowWidth -= 6; 124 | } 125 | } 126 | 127 | FontRenderer fontRenderer = minecraft.font; 128 | 129 | betterRender(matrixStack, fontRenderer, rowIndex, rowTop, rowLeft, rowWidth, rowHeight, mouseX, mouseY, isMouseOver, partialTicks); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/gui/entry/IntEntry.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.gui.entry; 2 | 3 | import com.gluecode.fpvdrone.Main; 4 | import com.gluecode.fpvdrone.gui.list.FPVList; 5 | import com.gluecode.fpvdrone.util.SettingsLoader; 6 | import com.google.common.collect.ImmutableList; 7 | import com.jme3.math.FastMath; 8 | import com.mojang.blaze3d.vertex.PoseStack; 9 | import net.minecraft.client.gui.FontRenderer; 10 | import net.minecraft.client.gui.IGuiEventListener; 11 | import net.minecraft.client.gui.widget.Widget; 12 | import net.minecraft.client.gui.widget.button.Button; 13 | import net.minecraft.client.resources.I18n; 14 | import net.minecraft.util.text.StringTextComponent; 15 | 16 | import javax.annotation.Nullable; 17 | import java.util.List; 18 | import java.util.function.Consumer; 19 | import java.util.function.Supplier; 20 | 21 | public class IntEntry extends FPVEntry { 22 | private Supplier getValue; 23 | private Consumer setNewValue; 24 | private Runnable setDefaultValue; 25 | private Supplier getDefaultValue; 26 | private Button changeButton; 27 | private Button resetButton; 28 | 29 | public IntEntry( 30 | FPVList list, 31 | String name, 32 | Supplier getValue, 33 | Consumer setNewValue, 34 | Supplier getDefaultValue, 35 | Runnable setDefaultValue 36 | ) { 37 | super(list, name); 38 | this.name = name; 39 | this.getValue = getValue; 40 | this.setNewValue = setNewValue; 41 | this.getDefaultValue = getDefaultValue; 42 | this.setDefaultValue = setDefaultValue; 43 | this.changeButton = new Button( 44 | 0, 45 | 0, 46 | 70, 47 | 20, 48 | new StringTextComponent(name), 49 | this::handleChangePress 50 | ); 51 | this.resetButton = new Button( 52 | 0, 53 | 0, 54 | 50, 55 | 20, 56 | new StringTextComponent(I18n.get("controls.reset")), 57 | this::handleResetPress 58 | ); 59 | this.editMode = false; 60 | } 61 | 62 | @Override 63 | public boolean isLetterAcceptable(String letter) { 64 | switch (letter) { 65 | case "0": 66 | case "1": 67 | case "2": 68 | case "3": 69 | case "4": 70 | case "5": 71 | case "6": 72 | case "7": 73 | case "8": 74 | case "9": 75 | case "-": 76 | return true; 77 | default: 78 | return false; 79 | } 80 | } 81 | 82 | @Override 83 | public void betterRender( 84 | PoseStack matrixStack, 85 | FontRenderer fontRenderer, 86 | int rowIndex, 87 | int rowTop, 88 | int rowLeft, 89 | int rowWidth, 90 | int rowHeight, 91 | int mouseX, 92 | int mouseY, 93 | boolean isMouseOver, 94 | float partialTicks 95 | ) { 96 | fontRenderer.draw( 97 | matrixStack, 98 | this.name, 99 | rowLeft, 100 | (float) (rowTop + 6), 101 | 16777215 102 | ); 103 | 104 | int right = rowLeft + rowWidth; 105 | int resetWidth = this.resetButton.getWidth(); 106 | int changeWidth = this.changeButton.getWidth(); 107 | 108 | this.resetButton.x = right - resetWidth; 109 | this.resetButton.y = rowTop; 110 | this.resetButton.active = FastMath.abs(this.getDefaultValue.get() - 111 | this.getValue.get()) > 112 | 0.001f; // todo 113 | this.changeButton.x = right - resetWidth - 1 - changeWidth; 114 | this.changeButton.y = rowTop; 115 | 116 | if (this.editMode) { 117 | this.changeButton.setMessage(new StringTextComponent("> " + 118 | editValue + 119 | "_ <")); 120 | } else { 121 | int value = this.getValue.get(); 122 | this.changeButton.setMessage(new StringTextComponent("" + 123 | value)); 124 | } 125 | 126 | if (SettingsLoader.isDefaultPreset(SettingsLoader.currentModel)) { 127 | this.resetButton.active = false; 128 | this.changeButton.active = false; 129 | } 130 | this.resetButton.render(matrixStack, mouseX, mouseY, partialTicks); 131 | this.changeButton.render(matrixStack, mouseX, mouseY, partialTicks); 132 | } 133 | 134 | @Override 135 | public List children() { 136 | return ImmutableList.of(this.changeButton, this.resetButton); 137 | } 138 | 139 | public void handleChangePress(@Nullable Widget button) { 140 | super.handleChangePress(button); 141 | 142 | this.editMode = !this.editMode; 143 | if (editMode) { 144 | this.editValue = ""; 145 | } else { 146 | if (!this.editValue.equals("")) { 147 | try { 148 | int value = Integer.parseInt(this.editValue); 149 | setNewValue.accept(value); 150 | SettingsLoader.save(); 151 | } catch (Exception e) { 152 | Main.LOGGER.info(e); 153 | } 154 | } 155 | } 156 | } 157 | 158 | public void handleResetPress(Button button) { 159 | this.editMode = false; 160 | this.setDefaultValue.run(); 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/gui/entry/SingleButtonEntry.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.gui.entry; 2 | 3 | import com.gluecode.fpvdrone.gui.list.FPVList; 4 | import com.google.common.collect.ImmutableList; 5 | import com.mojang.blaze3d.vertex.PoseStack; 6 | import net.minecraft.client.gui.FontRenderer; 7 | import net.minecraft.client.gui.IGuiEventListener; 8 | import net.minecraft.client.gui.widget.button.Button; 9 | import net.minecraft.client.resources.I18n; 10 | import net.minecraft.util.text.StringTextComponent; 11 | 12 | import javax.annotation.Nullable; 13 | import java.util.List; 14 | import java.util.function.Supplier; 15 | 16 | public class SingleButtonEntry extends FPVEntry { 17 | private Supplier getName; 18 | private Supplier getDisabled; 19 | private Button selectButton; 20 | 21 | public SingleButtonEntry( 22 | FPVList list, 23 | Supplier getName, 24 | Supplier getDisabled, 25 | Runnable onSelect 26 | ) { 27 | super(list, ""); 28 | this.getName = getName; 29 | this.getDisabled = getDisabled; 30 | this.selectButton = new Button( 31 | 0, 32 | 0, 33 | 80, 34 | 20, 35 | new StringTextComponent(I18n.get("fpvdrone.settings.choose")), 36 | (@Nullable Button button) -> { 37 | onSelect.run(); 38 | } 39 | ); 40 | } 41 | 42 | @Override 43 | public boolean isLetterAcceptable(String letter) { 44 | return false; 45 | } 46 | 47 | @Override 48 | public void betterRender( 49 | PoseStack matrixStack, 50 | FontRenderer fontRenderer, 51 | int rowIndex, 52 | int rowTop, 53 | int rowLeft, 54 | int rowWidth, 55 | int rowHeight, 56 | int mouseX, 57 | int mouseY, 58 | boolean isMouseOver, 59 | float partialTicks 60 | ) { 61 | fontRenderer.draw( 62 | matrixStack, 63 | this.getName.get(), 64 | rowLeft, 65 | (float) (rowTop + 6), 66 | 16777215 67 | ); 68 | 69 | int right = rowLeft + rowWidth; 70 | int selectWidth = this.selectButton.getWidth(); 71 | 72 | this.selectButton.x = right - selectWidth; 73 | this.selectButton.y = rowTop; 74 | this.selectButton.active = !this.getDisabled.get(); 75 | this.selectButton.render(matrixStack, mouseX, mouseY, partialTicks); 76 | } 77 | 78 | @Override 79 | public List children() { 80 | return ImmutableList.of(this.selectButton); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/gui/list/ChannelMappingList.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.gui.list; 2 | 3 | import com.gluecode.fpvdrone.gui.entry.CategoryEntry; 4 | import com.gluecode.fpvdrone.gui.entry.ChannelEntry; 5 | import com.gluecode.fpvdrone.gui.screen.ChannelMappingScreen; 6 | import com.gluecode.fpvdrone.input.ControllerConfig; 7 | import com.gluecode.fpvdrone.input.ControllerReader; 8 | import net.minecraft.client.resources.I18n; 9 | 10 | import java.util.function.*; 11 | 12 | public class ChannelMappingList extends FPVList { 13 | public ChannelMappingList(ChannelMappingScreen parentScreen) { 14 | super(parentScreen); 15 | 16 | this.addEntry(new CategoryEntry(this, () -> I18n.get( 17 | "fpvdrone.channel.category.axis"))); 18 | this.addEntry(new ChannelEntry( 19 | this, 20 | I18n.get("fpvdrone.channel.throttle"), 21 | ControllerConfig::getThrottleChannel, 22 | ControllerConfig::setThrottleChannel, 23 | ControllerConfig::getInvertThrottle, 24 | ControllerConfig::setInvertThrottle, 25 | ControllerConfig::getDefaultThrottleChannel, 26 | ControllerConfig::resetThrottleChannel 27 | )); 28 | this.addEntry(new ChannelEntry( 29 | this, 30 | I18n.get("fpvdrone.channel.roll"), 31 | ControllerConfig::getRollChannel, 32 | ControllerConfig::setRollChannel, 33 | ControllerConfig::getInvertRoll, 34 | ControllerConfig::setInvertRoll, 35 | ControllerConfig::getDefaultRollChannel, 36 | ControllerConfig::resetRollChannel 37 | )); 38 | this.addEntry(new ChannelEntry( 39 | this, 40 | I18n.get("fpvdrone.channel.pitch"), 41 | ControllerConfig::getPitchChannel, 42 | ControllerConfig::setPitchChannel, 43 | ControllerConfig::getInvertPitch, 44 | ControllerConfig::setInvertPitch, 45 | ControllerConfig::getDefaultPitchChannel, 46 | ControllerConfig::resetPitchChannel 47 | )); 48 | this.addEntry(new ChannelEntry( 49 | this, 50 | I18n.get("fpvdrone.channel.yaw"), 51 | ControllerConfig::getYawChannel, 52 | ControllerConfig::setYawChannel, 53 | ControllerConfig::getInvertYaw, 54 | ControllerConfig::setInvertYaw, 55 | ControllerConfig::getDefaultYawChannel, 56 | ControllerConfig::resetYawChannel 57 | )); 58 | this.addEntry(new ChannelEntry( 59 | this, 60 | I18n.get("fpvdrone.channel.anglepot"), 61 | ControllerConfig::getAngleChannel, 62 | ControllerConfig::setAngleChannel, 63 | ControllerConfig::getInvertAngle, 64 | ControllerConfig::setInvertAngle, 65 | ControllerConfig::getDefaultAngleChannel, 66 | ControllerConfig::resetAngleChannel 67 | )); 68 | 69 | this.addEntry(new CategoryEntry( 70 | this, 71 | () -> I18n.get("fpvdrone.channel.category.switch"))); 72 | this.addEntry(new ChannelEntry( 73 | this, 74 | I18n.get("fpvdrone.channel.arm"), 75 | getButtonChannel(ControllerConfig::getArmChannel), 76 | setButtonChannel(ControllerConfig::setArmChannel), 77 | ControllerConfig::getInvertArm, 78 | ControllerConfig::setInvertArm, 79 | getButtonChannel(ControllerConfig::getDefaultArmChannel), 80 | ControllerConfig::resetArmChannel 81 | )); 82 | this.addEntry(new ChannelEntry( 83 | this, 84 | I18n.get("fpvdrone.channel.angleswitch"), 85 | getButtonChannel(ControllerConfig::getActivateAngleChannel), 86 | setButtonChannel(ControllerConfig::setActivateAngleChannel), 87 | ControllerConfig::getInvertActivateAngle, 88 | ControllerConfig::setInvertActivateAngle, 89 | getButtonChannel(ControllerConfig::getDefaultActivateAngleChannel), 90 | ControllerConfig::resetActivateAngleChannel 91 | )); 92 | this.addEntry(new ChannelEntry( 93 | this, 94 | I18n.get("fpvdrone.channel.rightclick"), 95 | getButtonChannel(ControllerConfig::getRightClickChannel), 96 | setButtonChannel(ControllerConfig::setRightClickChannel), 97 | ControllerConfig::getInvertRightClick, 98 | ControllerConfig::setInvertRightClick, 99 | getButtonChannel(ControllerConfig::getDefaultRightClickChannel), 100 | ControllerConfig::resetRightClickChannel 101 | )); 102 | 103 | this.addEntry(new CategoryEntry(this, () -> "")); 104 | } 105 | 106 | private IntSupplier getButtonChannel(IntSupplier supplier) { 107 | return () -> supplier.getAsInt() + ControllerReader.getAxisLength(); 108 | } 109 | 110 | private IntConsumer setButtonChannel(IntConsumer consumer) { 111 | return (int channel) -> consumer.accept( 112 | channel - ControllerReader.getAxisLength() 113 | ); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/gui/list/ControllerChoicesList.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.gui.list; 2 | 3 | import com.gluecode.fpvdrone.gui.entry.SingleButtonEntry; 4 | import com.gluecode.fpvdrone.gui.screen.ControllerChoicesScreen; 5 | import com.gluecode.fpvdrone.input.ControllerReader; 6 | import com.google.common.collect.ImmutableList; 7 | import com.mojang.blaze3d.vertex.PoseStack; 8 | import net.minecraft.client.Minecraft; 9 | import net.minecraft.client.gui.IGuiEventListener; 10 | import net.minecraft.client.gui.screen.Screen; 11 | import net.minecraft.client.gui.widget.button.Button; 12 | import net.minecraft.client.gui.widget.list.AbstractOptionList; 13 | import net.minecraft.client.resources.I18n; 14 | import net.minecraft.util.text.StringTextComponent; 15 | import net.minecraftforge.api.distmarker.Dist; 16 | import net.minecraftforge.api.distmarker.OnlyIn; 17 | import org.lwjgl.glfw.GLFW; 18 | 19 | import javax.annotation.Nullable; 20 | import java.util.List; 21 | import java.util.function.Consumer; 22 | import java.util.function.Supplier; 23 | 24 | public class ControllerChoicesList extends FPVList { 25 | public ControllerChoicesList( 26 | Screen parentScreen, 27 | Consumer setId 28 | ) { 29 | super(parentScreen); 30 | 31 | for (int id = 0; id < 16; id++) { 32 | String name = ControllerReader.getControllerName(id); 33 | final int innerId = id; 34 | this.addEntry( 35 | new SingleButtonEntry(this, () -> { 36 | return ControllerReader.getControllerName(innerId); 37 | }, () -> { 38 | return !GLFW.glfwJoystickPresent(innerId); 39 | }, () -> { 40 | setId.accept(innerId); 41 | }) 42 | ); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/gui/list/FPVList.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.gui.list; 2 | 3 | import com.gluecode.fpvdrone.gui.entry.FPVEntry; 4 | import com.gluecode.fpvdrone.gui.screen.wizard.WizardConfig; 5 | import net.minecraft.client.Minecraft; 6 | import net.minecraft.client.gui.screen.Screen; 7 | import net.minecraft.client.gui.widget.list.AbstractOptionList; 8 | 9 | public class FPVList extends AbstractOptionList { 10 | public FPVEntry activeEntry = null; 11 | public Screen parentScreen; 12 | 13 | public FPVList( 14 | Screen parentScreen 15 | ) { 16 | super( 17 | Minecraft.getInstance(), 18 | parentScreen.width, // widthIn, 19 | parentScreen.height, // heightIn, 20 | WizardConfig.headerHeight, // topIn, 21 | parentScreen.height - WizardConfig.footerHeight, // bottomIn, 22 | 20 // itemHeightIn 23 | ); 24 | this.parentScreen = parentScreen; 25 | } 26 | 27 | public int getLeftPadding() { 28 | return 30; 29 | } 30 | 31 | public int getRightPadding() { 32 | return 30; 33 | } 34 | 35 | @Override 36 | protected int getScrollbarPosition() { 37 | return this.width - 6; 38 | } 39 | 40 | @Override 41 | public int getRowWidth() { 42 | return this.width; 43 | } 44 | 45 | @Override 46 | public int getRowLeft() { 47 | return 0; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/gui/list/MainSettingsList.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.gui.list; 2 | 3 | import com.gluecode.fpvdrone.gui.entry.CategoryEntry; 4 | import com.gluecode.fpvdrone.gui.entry.DoubleButtonEntry; 5 | import com.gluecode.fpvdrone.gui.entry.SingleButtonEntry; 6 | import com.gluecode.fpvdrone.gui.screen.MainSettingsScreen; 7 | import com.gluecode.fpvdrone.gui.GuiEvents; 8 | import com.gluecode.fpvdrone.input.ControllerReader; 9 | import com.gluecode.fpvdrone.util.SettingsLoader; 10 | import net.minecraft.client.resources.I18n; 11 | 12 | public class MainSettingsList extends FPVList { 13 | private int maxListLabelWidth; 14 | 15 | public MainSettingsList(MainSettingsScreen parentScreen) { 16 | // field_230708_k_ = width 17 | // field_230709_l_ = height 18 | super(parentScreen); 19 | 20 | this.addEntry(new CategoryEntry(this, () -> I18n.get( 21 | "fpvdrone.settings.controller"))); 22 | this.addEntry(new SingleButtonEntry(this, () -> { 23 | String name = ControllerReader.getControllerName(ControllerReader.getControllerId()); 24 | if (name.equalsIgnoreCase("")) { 25 | return I18n.get("fpvdrone.settings.nocontroller"); 26 | } else { 27 | return name; 28 | } 29 | }, () -> false, () -> { 30 | GuiEvents.openControllerChoicesScreen(parentScreen); 31 | })); 32 | 33 | this.addEntry(new CategoryEntry(this, () -> "")); 34 | this.addEntry(new CategoryEntry( 35 | this, 36 | () -> I18n.get("fpvdrone.settings.model") 37 | )); 38 | this.addEntry(new DoubleButtonEntry( 39 | this, 40 | () -> { 41 | String name = SettingsLoader.currentModel; 42 | return name; 43 | }, 44 | I18n.get("fpvdrone.settings.config"), 45 | () -> { 46 | GuiEvents.openModelSettingsScreen(parentScreen); 47 | }, 48 | I18n.get("fpvdrone.settings.choose"), 49 | () -> { 50 | GuiEvents.openModelChoicesScreen(parentScreen); 51 | }, 52 | () -> false 53 | )); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/gui/list/ModelChoiceList.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.gui.list; 2 | 3 | import com.gluecode.fpvdrone.gui.entry.CategoryEntry; 4 | import com.gluecode.fpvdrone.gui.entry.DoubleButtonEntry; 5 | import com.gluecode.fpvdrone.gui.screen.ModelChoicesScreen; 6 | import com.gluecode.fpvdrone.util.SettingsLoader; 7 | import net.minecraft.client.resources.I18n; 8 | 9 | import java.util.*; 10 | 11 | public class ModelChoiceList extends FPVList { 12 | public ModelChoiceList( 13 | ModelChoicesScreen parentScreen 14 | ) { 15 | // field_230708_k_ = width 16 | super(parentScreen); 17 | 18 | this.addEntry(new CategoryEntry(this, () -> I18n.get("fpvdrone.model.choose"))); 19 | 20 | this.addEntry(new DoubleButtonEntry( 21 | this, 22 | () -> SettingsLoader.defaultModelName, 23 | I18n.get("fpvdrone.settings.select"), 24 | () -> { 25 | SettingsLoader.loadModel(SettingsLoader.defaultModelName); 26 | SettingsLoader.save(); 27 | }, 28 | I18n.get("fpvdrone.settings.delete"), 29 | () -> { 30 | SettingsLoader.models.remove(SettingsLoader.defaultModelName); 31 | if (SettingsLoader.currentModel.equals(SettingsLoader.defaultModelName)) { 32 | SettingsLoader.loadModel(SettingsLoader.defaultModelName); 33 | } 34 | SettingsLoader.save(); 35 | }, 36 | () -> SettingsLoader.currentModel.equals(SettingsLoader.defaultModelName) 37 | )); 38 | this.addEntry(new DoubleButtonEntry( 39 | this, 40 | () -> SettingsLoader.defaultWhoop, 41 | I18n.get("fpvdrone.settings.select"), 42 | () -> { 43 | SettingsLoader.loadModel(SettingsLoader.defaultWhoop); 44 | SettingsLoader.save(); 45 | }, 46 | I18n.get("fpvdrone.settings.delete"), 47 | () -> { 48 | SettingsLoader.models.remove(SettingsLoader.defaultWhoop); 49 | if (SettingsLoader.currentModel.equals(SettingsLoader.defaultWhoop)) { 50 | SettingsLoader.loadModel(SettingsLoader.defaultWhoop); 51 | } 52 | SettingsLoader.save(); 53 | }, 54 | () -> SettingsLoader.currentModel.equals(SettingsLoader.defaultWhoop) 55 | )); 56 | 57 | List keys = new ArrayList(SettingsLoader.models.keySet()); 58 | Collections.sort(keys); 59 | for (Object key : keys) { 60 | String presetName = (String) key; 61 | 62 | if (presetName.equals(SettingsLoader.defaultModelName)) continue; 63 | if (presetName.equals(SettingsLoader.defaultWhoop)) continue; 64 | 65 | this.addEntry(new DoubleButtonEntry( 66 | this, 67 | () -> presetName, 68 | I18n.get("fpvdrone.settings.select"), 69 | () -> { 70 | SettingsLoader.loadModel(presetName); 71 | SettingsLoader.save(); 72 | }, 73 | I18n.get("fpvdrone.settings.delete"), 74 | () -> { 75 | SettingsLoader.models.remove(presetName); 76 | if (SettingsLoader.currentModel.equals(presetName)) { 77 | SettingsLoader.loadModel(presetName); 78 | } 79 | SettingsLoader.save(); 80 | }, 81 | () -> SettingsLoader.currentModel.equals(presetName) 82 | )); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/gui/list/ModelSettingsList.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.gui.list; 2 | 3 | import com.gluecode.fpvdrone.gui.GuiEvents; 4 | import com.gluecode.fpvdrone.gui.entry.CategoryEntry; 5 | import com.gluecode.fpvdrone.gui.entry.DoubleNavEntry; 6 | import com.gluecode.fpvdrone.gui.entry.SingleButtonEntry; 7 | import com.gluecode.fpvdrone.gui.screen.ModelSettingsScreen; 8 | import com.gluecode.fpvdrone.input.ControllerReader; 9 | import com.gluecode.fpvdrone.util.SettingsLoader; 10 | import net.minecraft.client.resources.I18n; 11 | 12 | public class ModelSettingsList extends FPVList { 13 | public ModelSettingsList(ModelSettingsScreen parentScreen) { 14 | // field_230708_k_ = width 15 | // field_230709_l_ = height 16 | super(parentScreen); 17 | 18 | this.addEntry(new CategoryEntry( 19 | this, 20 | () -> I18n.get("fpvdrone.settings.model") 21 | )); 22 | this.addEntry(new SingleButtonEntry( 23 | this, 24 | () -> SettingsLoader.currentModel, 25 | () -> false, 26 | () -> { 27 | GuiEvents.openModelChoicesScreen(this.parentScreen); 28 | } 29 | )); 30 | this.addEntry(new CategoryEntry(this, () -> "")); 31 | 32 | this.addEntry(new CategoryEntry(this, () -> I18n.get( 33 | "fpvdrone.settings.controller"))); 34 | this.addEntry(new SingleButtonEntry(this, () -> { 35 | String name = ControllerReader.getControllerName(ControllerReader.getControllerId()); 36 | if (name.equalsIgnoreCase("")) { 37 | return I18n.get("fpvdrone.settings.nocontroller"); 38 | } else { 39 | return name; 40 | } 41 | }, () -> false, () -> { 42 | GuiEvents.openControllerChoicesScreen(this.parentScreen); 43 | })); 44 | this.addEntry(new CategoryEntry(this, () -> "")); 45 | 46 | this.addEntry(new DoubleNavEntry( 47 | this, 48 | I18n.get("fpvdrone.settings.channel"), 49 | () -> { 50 | GuiEvents.openChannelMappingScreen(parentScreen); 51 | }, 52 | I18n.get("fpvdrone.settings.rates"), 53 | () -> { 54 | GuiEvents.openRatesScreen(parentScreen); 55 | } 56 | )); 57 | this.addEntry(new DoubleNavEntry( 58 | this, 59 | I18n.get("fpvdrone.settings.build"), 60 | () -> { 61 | GuiEvents.openDroneBuildScreen(parentScreen); 62 | }, 63 | I18n.get("fpvdrone.settings.other"), 64 | () -> { 65 | GuiEvents.openMiscSettingsScreen(parentScreen); 66 | } 67 | )); 68 | this.addEntry(new CategoryEntry(this, () -> "")); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/gui/list/OtherSettingsList.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.gui.list; 2 | 3 | import com.gluecode.fpvdrone.render.CameraManager; 4 | import com.gluecode.fpvdrone.entity.DroneBuild; 5 | import com.gluecode.fpvdrone.gui.entry.BooleanEntry; 6 | import com.gluecode.fpvdrone.gui.entry.CategoryEntry; 7 | import com.gluecode.fpvdrone.gui.entry.FloatEntry; 8 | import com.gluecode.fpvdrone.gui.screen.OtherSettingsScreen; 9 | import com.gluecode.fpvdrone.util.SettingsLoader; 10 | import net.minecraft.client.resources.I18n; 11 | 12 | public class OtherSettingsList extends FPVList { 13 | public OtherSettingsList(OtherSettingsScreen parentScreen) { 14 | // field_230708_k_ = width 15 | // field_230709_l_ = height 16 | super(parentScreen); 17 | 18 | this.addEntry(new CategoryEntry(this, () -> I18n.get("fpvdrone.settings.other"))); 19 | this.addEntry(new BooleanEntry( 20 | this, 21 | I18n.get("fpvdrone.other.crosshair"), 22 | CameraManager::getShowCrosshairs, 23 | CameraManager::setShowCrosshairs, 24 | CameraManager::getDefaultShowCrosshairs, 25 | CameraManager::resetShowCrosshairs 26 | )); 27 | this.addEntry(new BooleanEntry( 28 | this, 29 | I18n.get("fpvdrone.other.showBlockOutline"), 30 | CameraManager::getShowBlockOutline, 31 | CameraManager::setShowBlockOutline, 32 | CameraManager::getDefaultShowBlockOutline, 33 | CameraManager::resetShowBlockOutline 34 | )); 35 | this.addEntry(new BooleanEntry( 36 | this, 37 | I18n.get("fpvdrone.other.showStickOverlay"), 38 | CameraManager::getShowStickOverlay, 39 | CameraManager::setShowStickOverlay, 40 | CameraManager::getDefaultShowStickOverlay, 41 | CameraManager::resetShowStickOverlay 42 | )); 43 | this.addEntry(new BooleanEntry( 44 | this, 45 | I18n.get("fpvdrone.other.flightMode3d"), 46 | DroneBuild::getFlightMode3d, 47 | DroneBuild::setFlightMode3d, 48 | DroneBuild::getDefaultFlightMode3d, 49 | DroneBuild::resetFlightMode3d 50 | )); 51 | this.addEntry(new FloatEntry( 52 | this, 53 | I18n.get("fpvdrone.other.angle"), 54 | DroneBuild::getSwitchlessAngle, 55 | DroneBuild::setSwitchlessAngle, 56 | DroneBuild::getDefaultSwitchlessAngle, 57 | DroneBuild::resetSwitchlessAngle, 58 | false, 59 | () -> 0f, 60 | () -> 0f, 61 | () -> false 62 | )); 63 | 64 | this.addEntry(new CategoryEntry(this, () -> "")); 65 | this.addEntry(new CategoryEntry(this, () -> I18n.get("fpvdrone.settings.other"))); 66 | this.addEntry(new FloatEntry( 67 | this, 68 | I18n.get("fpvdrone.other.fov"), 69 | () -> SettingsLoader.currentFov, 70 | (fov) -> { 71 | SettingsLoader.currentFov = fov; 72 | }, 73 | () -> SettingsLoader.defaultFov, 74 | () -> { 75 | SettingsLoader.currentFov = SettingsLoader.defaultFov; 76 | }, 77 | true, 78 | () -> 30f, 79 | () -> 150f, 80 | () -> false 81 | )); 82 | this.addEntry(new BooleanEntry( 83 | this, 84 | I18n.get("fpvdrone.other.fisheye"), 85 | () -> SettingsLoader.currentUseFisheye, 86 | (useFisheye) -> { 87 | SettingsLoader.currentUseFisheye = useFisheye; 88 | }, 89 | () -> SettingsLoader.defaultUseFisheye, 90 | () -> { 91 | SettingsLoader.currentUseFisheye = SettingsLoader.defaultUseFisheye; 92 | } 93 | )); 94 | // this.addEntry(new BooleanEntry( 95 | // this, 96 | // I18n.get("fpvdrone.other.realtime"), 97 | // () -> SettingsLoader.currentUseRealtimePhysics, 98 | // (useRealtimePhysics) -> { 99 | // SettingsLoader.currentUseRealtimePhysics = useRealtimePhysics; 100 | // }, 101 | // () -> SettingsLoader.defaultUseRealtimePhysics, 102 | // () -> { 103 | // SettingsLoader.currentUseRealtimePhysics = SettingsLoader.defaultUseRealtimePhysics; 104 | // } 105 | // )); 106 | this.addEntry(new BooleanEntry( 107 | this, 108 | I18n.get("fpvdrone.other.showWizard"), 109 | () -> SettingsLoader.firstTimeSetup, 110 | (value) -> { 111 | SettingsLoader.firstTimeSetup = value; 112 | }, 113 | () -> true, 114 | () -> { 115 | SettingsLoader.firstTimeSetup = true; 116 | } 117 | )); 118 | 119 | this.addEntry(new CategoryEntry(this, () -> "")); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/gui/screen/ChannelMappingScreen.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.gui.screen; 2 | 3 | import com.gluecode.fpvdrone.gui.list.ChannelMappingList; 4 | import com.gluecode.fpvdrone.gui.screen.addon.DoneFooter; 5 | import com.gluecode.fpvdrone.gui.screen.addon.ServerTitleWikiHeader; 6 | import com.mojang.blaze3d.vertex.PoseStack; 7 | import net.minecraft.client.Options; 8 | import net.minecraft.client.resources.I18n; 9 | import net.minecraft.util.text.*; 10 | 11 | public class ChannelMappingScreen extends FpvScreen { 12 | public long time; 13 | private ChannelMappingList channelMappingList; 14 | 15 | public ChannelMappingScreen( 16 | ModelSettingsScreen previousScreen 17 | ) { 18 | super( 19 | previousScreen, 20 | new ServerTitleWikiHeader(I18n.get("fpvdrone.settings.fpvsettings")), 21 | new DoneFooter() 22 | ); 23 | } 24 | 25 | @Override 26 | protected void init() { 27 | super.init(); 28 | 29 | this.channelMappingList = new ChannelMappingList(this); 30 | this.children.add(this.channelMappingList); 31 | } 32 | 33 | // func_230430_a_ = render 34 | @Override 35 | public void renderCustom( 36 | PoseStack matrixStack, 37 | int mouseX, 38 | int mouseY, 39 | float partialTicks 40 | ) { 41 | this.channelMappingList.render( 42 | matrixStack, 43 | mouseX, 44 | mouseY, 45 | partialTicks 46 | ); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/gui/screen/ControllerChoicesScreen.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.gui.screen; 2 | 3 | import com.gluecode.fpvdrone.Main; 4 | import com.gluecode.fpvdrone.gui.list.ControllerChoicesList; 5 | import com.gluecode.fpvdrone.gui.screen.addon.DoneFooter; 6 | import com.gluecode.fpvdrone.gui.screen.addon.ServerTitleWikiHeader; 7 | import com.gluecode.fpvdrone.input.ControllerReader; 8 | import com.mojang.blaze3d.vertex.PoseStack; 9 | import net.minecraft.client.Options; 10 | import net.minecraft.client.gui.screen.Screen; 11 | import net.minecraft.client.gui.screen.SettingsScreen; 12 | import net.minecraft.client.gui.widget.button.Button; 13 | import net.minecraft.client.resources.I18n; 14 | import net.minecraft.util.text.StringTextComponent; 15 | 16 | import javax.annotation.Nullable; 17 | 18 | public class ControllerChoicesScreen extends FpvScreen { 19 | private ControllerChoicesList keyBindingList; 20 | 21 | public ControllerChoicesScreen( 22 | Screen previousScreen 23 | ) { 24 | super( 25 | previousScreen, 26 | new ServerTitleWikiHeader(I18n.get("fpvdrone.device.choose")), 27 | new DoneFooter() 28 | ); 29 | } 30 | 31 | public void handleIdSet(int id) { 32 | ControllerReader.setControllerId(id); 33 | if (this.footer != null) { 34 | DoneFooter footer = (DoneFooter) this.footer; 35 | footer.handleDone(this); 36 | } 37 | } 38 | 39 | @Override 40 | protected void init() { 41 | super.init(); 42 | this.keyBindingList = new ControllerChoicesList( 43 | this, 44 | this::handleIdSet 45 | ); 46 | this.children.add(this.keyBindingList); 47 | } 48 | 49 | @Override 50 | public void renderCustom( 51 | PoseStack matrixStack, 52 | int mouseX, 53 | int mouseY, 54 | float partialTicks 55 | ) { 56 | this.keyBindingList.render(matrixStack, mouseX, mouseY, partialTicks); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/gui/screen/DroneBuildScreen.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.gui.screen; 2 | 3 | import com.gluecode.fpvdrone.gui.list.DroneBuildList; 4 | import com.gluecode.fpvdrone.gui.screen.addon.DoneFooter; 5 | import com.gluecode.fpvdrone.gui.screen.addon.ServerTitleWikiHeader; 6 | import com.mojang.blaze3d.vertex.PoseStack; 7 | import net.minecraft.client.Options; 8 | import net.minecraft.client.gui.screen.Screen; 9 | import net.minecraft.client.resources.I18n; 10 | import net.minecraft.util.text.*; 11 | 12 | public class DroneBuildScreen extends FpvScreen { 13 | private DroneBuildList droneBuildList; 14 | 15 | public DroneBuildScreen( 16 | Screen previousScreen 17 | ) { 18 | super( 19 | previousScreen, 20 | new ServerTitleWikiHeader(I18n.get("fpvdrone.settings.fpvsettings")), 21 | new DoneFooter() 22 | ); 23 | } 24 | 25 | // func_231160_c_ = init 26 | @Override 27 | protected void init() { 28 | super.init(); 29 | this.droneBuildList = new DroneBuildList(this); 30 | this.children.add(this.droneBuildList); 31 | } 32 | 33 | // func_230430_a_ = render 34 | @Override 35 | public void renderCustom( 36 | PoseStack matrixStack, 37 | int mouseX, 38 | int mouseY, 39 | float partialTicks 40 | ) { 41 | this.droneBuildList.render(matrixStack, mouseX, mouseY, partialTicks); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/gui/screen/EmptyListScreen.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.gui.screen; 2 | 3 | import com.gluecode.fpvdrone.gui.list.FPVList; 4 | import com.gluecode.fpvdrone.gui.screen.addon.ScreenAddon; 5 | import com.mojang.blaze3d.vertex.PoseStack; 6 | import net.minecraft.client.gui.screen.Screen; 7 | 8 | import javax.annotation.Nullable; 9 | 10 | public class EmptyListScreen extends FpvScreen { 11 | FPVList list; 12 | 13 | public EmptyListScreen( 14 | Screen previousScreen, 15 | @Nullable ScreenAddon header, 16 | @Nullable ScreenAddon footer 17 | ) { 18 | super( 19 | previousScreen, 20 | header, 21 | footer 22 | ); 23 | } 24 | 25 | @Override 26 | protected void init() { 27 | super.init(); 28 | this.list = new FPVList(this); 29 | 30 | // Empty list must not be added as a child. 31 | // Children will capture mouse events. 32 | // Adding the list as a child will cause any buttons rendered on top to 33 | // to not receive mouse events. 34 | // this.children.add(this.list); 35 | } 36 | 37 | @Override 38 | public void renderCustom( 39 | PoseStack matrixStack, 40 | int mouseX, 41 | int mouseY, 42 | float partialTicks 43 | ) { 44 | this.list.render(matrixStack, mouseX, mouseY, partialTicks); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/gui/screen/FpvScreen.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.gui.screen; 2 | 3 | import com.gluecode.fpvdrone.gui.screen.addon.ScreenAddon; 4 | import com.mojang.blaze3d.vertex.PoseStack; 5 | import net.minecraft.client.Options; 6 | import net.minecraft.client.Minecraft; 7 | import net.minecraft.client.gui.FontRenderer; 8 | import net.minecraft.client.gui.screen.Screen; 9 | import net.minecraft.client.gui.screen.SettingsScreen; 10 | import net.minecraft.client.gui.widget.Widget; 11 | import net.minecraft.util.text.StringTextComponent; 12 | 13 | import javax.annotation.Nullable; 14 | 15 | public abstract class FpvScreen extends SettingsScreen { 16 | @Nullable 17 | public Screen previousScreen; 18 | @Nullable 19 | public ScreenAddon header; 20 | @Nullable 21 | public ScreenAddon footer; 22 | 23 | public FpvScreen( 24 | Screen previousScreen, 25 | @Nullable ScreenAddon header, 26 | @Nullable ScreenAddon footer 27 | ) { 28 | super( 29 | previousScreen, 30 | Minecraft.getInstance().options, 31 | new StringTextComponent("") 32 | ); 33 | this.previousScreen = previousScreen; 34 | this.header = header; 35 | this.footer = footer; 36 | } 37 | 38 | @Override 39 | protected void init() { 40 | if (this.header != null) { 41 | this.header.init(this); 42 | } 43 | if (this.footer != null) { 44 | this.footer.init(this); 45 | } 46 | } 47 | 48 | @Override 49 | public T addButton(T button) { // Access widener 50 | return super.addButton(button); 51 | } 52 | 53 | abstract public void renderCustom( 54 | PoseStack matrixStack, 55 | int mouseX, 56 | int mouseY, 57 | float partialTicks 58 | ); 59 | 60 | @Override 61 | public void render( 62 | PoseStack matrixStack, 63 | int mouseX, 64 | int mouseY, 65 | float partialTicks 66 | ) { 67 | this.renderBackground(matrixStack); 68 | this.renderCustom(matrixStack, mouseX, mouseY, partialTicks); 69 | 70 | super.render(matrixStack, mouseX, mouseY, partialTicks); 71 | 72 | // These need to render after super.render because of text that might be drawn on top of "x" button. 73 | if (this.header != null) { 74 | this.header.render(this, matrixStack, mouseX, mouseY, partialTicks); 75 | } 76 | if (this.footer != null) { 77 | this.footer.render(this, matrixStack, mouseX, mouseY, partialTicks); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/gui/screen/MainSettingsScreen.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.gui.screen; 2 | 3 | import com.gluecode.fpvdrone.gui.list.MainSettingsList; 4 | import com.gluecode.fpvdrone.gui.screen.addon.DoneFooter; 5 | import com.gluecode.fpvdrone.gui.screen.addon.ServerTitleWikiHeader; 6 | import com.mojang.blaze3d.vertex.PoseStack; 7 | import net.minecraft.client.Options; 8 | import net.minecraft.client.gui.screen.Screen; 9 | import net.minecraft.client.resources.I18n; 10 | import net.minecraft.util.text.*; 11 | 12 | public class MainSettingsScreen extends FpvScreen { 13 | private MainSettingsList list; 14 | 15 | public MainSettingsScreen( 16 | Screen previousScreen 17 | ) { 18 | super( 19 | previousScreen, 20 | new ServerTitleWikiHeader(I18n.get("fpvdrone.settings.fpvsettings")), 21 | new DoneFooter() 22 | ); 23 | } 24 | 25 | // func_231160_c_ = init 26 | @Override 27 | protected void init() { 28 | super.init(); 29 | this.list = new MainSettingsList(this); 30 | this.children.add(this.list); 31 | } 32 | 33 | @Override 34 | public void renderCustom( 35 | PoseStack matrixStack, 36 | int mouseX, 37 | int mouseY, 38 | float partialTicks 39 | ) { 40 | this.list.render(matrixStack, mouseX, mouseY, partialTicks); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/gui/screen/ModelChoicesScreen.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.gui.screen; 2 | 3 | import com.gluecode.fpvdrone.gui.list.ModelChoiceList; 4 | import com.gluecode.fpvdrone.gui.screen.addon.DoneFooter; 5 | import com.gluecode.fpvdrone.gui.screen.addon.ServerTitleWikiHeader; 6 | import com.gluecode.fpvdrone.gui.screen.wizard.WizardConfig; 7 | import com.gluecode.fpvdrone.util.SettingsLoader; 8 | import com.mojang.blaze3d.vertex.PoseStack; 9 | import net.minecraft.client.gui.screen.Screen; 10 | import net.minecraft.client.gui.widget.Widget; 11 | import net.minecraft.client.gui.widget.button.Button; 12 | import net.minecraft.client.resources.I18n; 13 | import net.minecraft.client.util.InputMappings; 14 | import net.minecraft.util.text.StringTextComponent; 15 | import net.minecraftforge.client.event.GuiScreenEvent; 16 | import net.minecraftforge.common.MinecraftForge; 17 | import net.minecraftforge.eventbus.api.SubscribeEvent; 18 | 19 | import javax.annotation.Nullable; 20 | 21 | public class ModelChoicesScreen extends FpvScreen { 22 | private ModelChoiceList list = null; 23 | private String initName = I18n.get("fpvdrone.model.new"); 24 | private String editValue = initName; 25 | private boolean editMode = false; 26 | // private Button createButton; 27 | private Button nameButton; 28 | 29 | public ModelChoicesScreen( 30 | Screen previousScreen 31 | ) { 32 | super( 33 | previousScreen, 34 | new ServerTitleWikiHeader(I18n.get("fpvdrone.model.active")), 35 | new DoneFooter() 36 | ); 37 | MinecraftForge.EVENT_BUS.register(this); 38 | } 39 | 40 | @SubscribeEvent 41 | public void charTyped(GuiScreenEvent.KeyboardCharTypedEvent.Pre event) { 42 | if (editMode) { 43 | if (this.editValue.length() < 24) { 44 | this.editValue = this.editValue + event.getCodePoint(); 45 | } 46 | } 47 | } 48 | 49 | @Override 50 | protected void init() { 51 | super.init(); 52 | this.generateList(); 53 | 54 | this.nameButton = this.addButton(new Button( 55 | WizardConfig.left, 56 | this.height - 20 - WizardConfig.footerBottom, 57 | WizardConfig.wideButtonWidth * 2, 58 | 20, 59 | new StringTextComponent(this.editValue), 60 | this::handleNewPress 61 | )); 62 | } 63 | 64 | public void generateList() { 65 | if (this.list != null) { 66 | this.children.remove(this.list); 67 | } 68 | 69 | this.list = new ModelChoiceList(this); 70 | this.children.add(this.list); 71 | } 72 | 73 | public boolean keyPressed( 74 | int p_keyPressed_1_, 75 | int p_keyPressed_2_, 76 | int p_keyPressed_3_ 77 | ) { 78 | InputMappings.Input input = InputMappings.getKey( 79 | p_keyPressed_1_, 80 | p_keyPressed_2_ 81 | ); 82 | if (editMode) { 83 | if (input.toString().equals("key.keyboard.backspace")) { 84 | if (this.editValue.length() > 0) { 85 | this.editValue = this.editValue.substring( 86 | 0, 87 | this.editValue.length() - 1 88 | ); 89 | } 90 | return true; 91 | } else if (input.toString().equals("key.keyboard.space")) { 92 | return true; 93 | } 94 | } 95 | return super.keyPressed( 96 | p_keyPressed_1_, 97 | p_keyPressed_2_, 98 | p_keyPressed_3_ 99 | ); 100 | } 101 | 102 | public void handleNewPress(@Nullable Widget button) { 103 | this.editMode = !this.editMode; 104 | if (editMode) { 105 | this.editValue = ""; 106 | } else { 107 | String attemptName = this.editValue.trim(); 108 | if (!attemptName.equals("")) { 109 | Object existing = SettingsLoader.models.get(attemptName); 110 | if (existing == null) { 111 | SettingsLoader.loadModel(SettingsLoader.defaultModelName); 112 | SettingsLoader.currentModel = attemptName; 113 | SettingsLoader.save(); 114 | this.generateList(); 115 | } 116 | } 117 | this.editValue = initName; 118 | } 119 | } 120 | 121 | @Override 122 | public void renderCustom( 123 | PoseStack matrixStack, 124 | int mouseX, 125 | int mouseY, 126 | float partialTicks 127 | ) { 128 | this.list.render(matrixStack, mouseX, mouseY, partialTicks); 129 | this.nameButton.setMessage( 130 | new StringTextComponent(this.editMode ? "> " + 131 | this.editValue + 132 | "_ <" : this.editValue)); 133 | this.nameButton.render(matrixStack, mouseX, mouseY, partialTicks); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/gui/screen/ModelSettingsScreen.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.gui.screen; 2 | 3 | import com.gluecode.fpvdrone.gui.list.ModelSettingsList; 4 | import com.gluecode.fpvdrone.gui.screen.addon.DoneFooter; 5 | import com.gluecode.fpvdrone.gui.screen.addon.ServerTitleWikiHeader; 6 | import com.gluecode.fpvdrone.gui.screen.addon.WizardDoneFooter; 7 | import com.mojang.blaze3d.vertex.PoseStack; 8 | import net.minecraft.client.Options; 9 | import net.minecraft.client.gui.screen.Screen; 10 | import net.minecraft.client.resources.I18n; 11 | import net.minecraft.util.text.StringTextComponent; 12 | 13 | public class ModelSettingsScreen extends FpvScreen { 14 | private ModelSettingsList list; 15 | 16 | public ModelSettingsScreen( 17 | Screen previousScreen 18 | ) { 19 | super( 20 | previousScreen, 21 | new ServerTitleWikiHeader(I18n.get("fpvdrone.settings.fpvsettings")), 22 | new WizardDoneFooter() 23 | ); 24 | } 25 | 26 | // func_231160_c_ = init 27 | @Override 28 | protected void init() { 29 | super.init(); 30 | this.list = new ModelSettingsList(this); 31 | this.children.add(this.list); 32 | } 33 | 34 | // func_230430_a_ = render 35 | @Override 36 | public void renderCustom( 37 | PoseStack matrixStack, 38 | int mouseX, 39 | int mouseY, 40 | float partialTicks 41 | ) { 42 | this.list.render(matrixStack, mouseX, mouseY, partialTicks); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/gui/screen/OtherSettingsScreen.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.gui.screen; 2 | 3 | import com.gluecode.fpvdrone.gui.list.OtherSettingsList; 4 | import com.gluecode.fpvdrone.gui.screen.addon.DoneFooter; 5 | import com.gluecode.fpvdrone.gui.screen.addon.ServerTitleWikiHeader; 6 | import com.mojang.blaze3d.vertex.PoseStack; 7 | import net.minecraft.client.Options; 8 | import net.minecraft.client.gui.screen.Screen; 9 | import net.minecraft.client.resources.I18n; 10 | import net.minecraft.util.text.*; 11 | 12 | public class OtherSettingsScreen extends FpvScreen { 13 | private OtherSettingsList otherSettingsList; 14 | 15 | public OtherSettingsScreen( 16 | Screen previousScreen 17 | ) { 18 | super( 19 | previousScreen, 20 | new ServerTitleWikiHeader(I18n.get("fpvdrone.settings.fpvsettings")), 21 | new DoneFooter() 22 | ); 23 | } 24 | 25 | @Override 26 | protected void init() { 27 | super.init(); 28 | 29 | this.otherSettingsList = new OtherSettingsList(this); 30 | this.children.add(this.otherSettingsList); 31 | } 32 | 33 | // func_230430_a_ = render 34 | @Override 35 | public void renderCustom( 36 | PoseStack matrixStack, 37 | int mouseX, 38 | int mouseY, 39 | float partialTicks 40 | ) { 41 | this.otherSettingsList.render( 42 | matrixStack, 43 | mouseX, 44 | mouseY, 45 | partialTicks 46 | ); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/gui/screen/RatesScreen.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.gui.screen; 2 | 3 | import com.gluecode.fpvdrone.gui.screen.addon.DoneFooter; 4 | import com.gluecode.fpvdrone.gui.screen.addon.ServerTitleWikiHeader; 5 | import com.gluecode.fpvdrone.gui.screen.wizard.WizardConfig; 6 | import com.gluecode.fpvdrone.gui.widget.RateChart; 7 | import com.gluecode.fpvdrone.gui.list.RatesList; 8 | import com.gluecode.fpvdrone.util.SettingsLoader; 9 | import com.mojang.blaze3d.vertex.PoseStack; 10 | import net.minecraft.client.Minecraft; 11 | import net.minecraft.client.gui.screen.Screen; 12 | import net.minecraft.client.gui.widget.button.Button; 13 | import net.minecraft.client.resources.I18n; 14 | import net.minecraft.util.text.*; 15 | 16 | public class RatesScreen extends FpvScreen { 17 | private RatesList ratesList; 18 | private RateChart rateChart; 19 | 20 | public RatesScreen( 21 | Screen previousScreen 22 | ) { 23 | super( 24 | previousScreen, 25 | new ServerTitleWikiHeader(I18n.get("fpvdrone.settings.fpvsettings")), 26 | new DoneFooter() 27 | ); 28 | } 29 | 30 | // func_231160_c_ = init 31 | @Override 32 | protected void init() { 33 | super.init(); 34 | 35 | this.ratesList = new RatesList(this); 36 | this.children.add(this.ratesList); 37 | 38 | int chartSize = lastScreen.height - 32 - 43 - 12 * 2; 39 | this.rateChart = new RateChart(30, 43 + 12, chartSize, chartSize); 40 | this.children.add(this.rateChart); 41 | 42 | this.addButton(new Button( 43 | WizardConfig.left, 44 | this.height - 20 - WizardConfig.footerBottom, 45 | WizardConfig.wideButtonWidth, 46 | 20, 47 | new StringTextComponent(I18n.get("fpvdrone.settings.reset.xbox")), 48 | this::handleXboxReset 49 | )); 50 | this.addButton(new Button( 51 | WizardConfig.left + WizardConfig.wideButtonWidth + WizardConfig.doubleButtonSpacing, 52 | this.height - 20 - WizardConfig.footerBottom, 53 | WizardConfig.wideButtonWidth, 54 | 20, 55 | new StringTextComponent(I18n.get("fpvdrone.settings.reset.radio")), 56 | this::handleRadioReset 57 | )); 58 | } 59 | 60 | private void handleXboxReset(Button button) { 61 | SettingsLoader.resetXboxRates(); 62 | } 63 | 64 | private void handleRadioReset(Button button) { 65 | SettingsLoader.resetRadioRates(); 66 | } 67 | 68 | @Override 69 | public void renderCustom( 70 | PoseStack matrixStack, 71 | int mouseX, 72 | int mouseY, 73 | float partialTicks 74 | ) { 75 | this.ratesList.render(matrixStack, mouseX, mouseY, partialTicks); 76 | this.rateChart.render(matrixStack, mouseX, mouseY, partialTicks); 77 | Minecraft minecraft = Minecraft.getInstance(); 78 | String chartLabel = I18n.get("fpvdrone.rates.sticks"); 79 | int labelWidth = minecraft.font.width(chartLabel); 80 | int chartSize = lastScreen.height - 32 - 43 - 12 * 2; 81 | minecraft.font.draw( 82 | matrixStack, 83 | I18n.get("fpvdrone.rates.sticks"), 84 | (float) (30 + chartSize / 2 - labelWidth / 2), 85 | 60, 86 | 16777215 87 | ); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/gui/screen/addon/BackFooter.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.gui.screen.addon; 2 | 3 | import com.gluecode.fpvdrone.Main; 4 | import com.gluecode.fpvdrone.gui.screen.FpvScreen; 5 | import com.gluecode.fpvdrone.gui.screen.wizard.WizardConfig; 6 | import com.mojang.blaze3d.vertex.PoseStack; 7 | import net.minecraft.client.gui.widget.button.Button; 8 | import net.minecraft.client.resources.I18n; 9 | import net.minecraft.util.text.StringTextComponent; 10 | 11 | public class BackFooter extends ScreenAddon { 12 | public void handleDone(FpvScreen screen) { 13 | screen.getMinecraft().setScreen(screen.previousScreen); 14 | } 15 | 16 | @Override 17 | public void init(FpvScreen screen) { 18 | screen.addButton(new Button( 19 | WizardConfig.left, 20 | screen.height - 20 - WizardConfig.footerBottom, 21 | WizardConfig.wideButtonWidth, 22 | 20, 23 | new StringTextComponent(I18n.get("gui.back")), 24 | (Button button) -> this.handleDone(screen) 25 | )); 26 | } 27 | 28 | @Override 29 | public void render( 30 | FpvScreen screen, PoseStack matrixStack, int mouseX, int mouseY, float partialTicks 31 | ) { 32 | 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/gui/screen/addon/BackHelpFooter.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.gui.screen.addon; 2 | 3 | import com.gluecode.fpvdrone.gui.screen.FpvScreen; 4 | import com.gluecode.fpvdrone.gui.screen.wizard.WizardConfig; 5 | import com.mojang.blaze3d.vertex.PoseStack; 6 | import net.minecraft.client.gui.screen.ConfirmOpenLinkScreen; 7 | import net.minecraft.client.gui.screen.Screen; 8 | import net.minecraft.client.gui.widget.button.Button; 9 | import net.minecraft.client.resources.I18n; 10 | import net.minecraft.util.Util; 11 | import net.minecraft.util.text.StringTextComponent; 12 | 13 | public class BackHelpFooter extends ScreenAddon { 14 | 15 | public void handleBack(FpvScreen screen) { 16 | screen.getMinecraft().setScreen(screen.previousScreen); 17 | } 18 | 19 | public void handleDiscord(Screen screen) { 20 | screen.getMinecraft().setScreen(new ConfirmOpenLinkScreen((p_244739_1_) -> { 21 | if (p_244739_1_) { 22 | Util.getPlatform().openUri( 23 | "https://discord.gg/WJfhXuz"); 24 | } 25 | screen.getMinecraft().setScreen(screen); 26 | }, "https://discord.gg/WJfhXuz", true)); 27 | } 28 | 29 | public void handleWiki(Screen screen) { 30 | screen.getMinecraft().setScreen(new ConfirmOpenLinkScreen((p_244739_1_) -> { 31 | if (p_244739_1_) { 32 | Util.getPlatform().openUri( 33 | "https://minecraftfpv.com/wiki"); 34 | } 35 | screen.getMinecraft().setScreen(screen); 36 | }, "https://minecraftfpv.com/wiki", true)); 37 | } 38 | 39 | @Override 40 | public void init(FpvScreen screen) { 41 | int width = 310; 42 | int nButtons = 3; 43 | int padding = 8; 44 | int btnWidth = (width - (nButtons - 1) * padding) / nButtons; 45 | 46 | screen.addButton(new Button( 47 | WizardConfig.left, 48 | screen.height - 20 - WizardConfig.footerBottom, 49 | WizardConfig.wideButtonWidth, 50 | 20, 51 | new StringTextComponent(I18n.get("gui.back")), 52 | (Button button) -> this.handleBack(screen) 53 | )); 54 | 55 | screen.addButton(new Button( 56 | screen.width - 2 * WizardConfig.shortButtonWidth - 2 * WizardConfig.right, 57 | screen.height - 20 - WizardConfig.footerBottom, 58 | WizardConfig.shortButtonWidth, 59 | 20, 60 | new StringTextComponent("Discord"), 61 | (Button button) -> this.handleDiscord(screen) 62 | )); 63 | 64 | screen.addButton(new Button( 65 | screen.width - WizardConfig.shortButtonWidth - WizardConfig.right, 66 | screen.height - 20 - WizardConfig.footerBottom, 67 | WizardConfig.shortButtonWidth, 68 | 20, 69 | new StringTextComponent(I18n.get("fpvdrone.settings.wiki")), 70 | (Button button) -> this.handleWiki(screen) 71 | )); 72 | } 73 | 74 | @Override 75 | public void render( 76 | FpvScreen screen, PoseStack matrixStack, int mouseX, int mouseY, float partialTicks 77 | ) { 78 | 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/gui/screen/addon/BackProceedFooter.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.gui.screen.addon; 2 | 3 | import com.gluecode.fpvdrone.gui.screen.FpvScreen; 4 | import com.gluecode.fpvdrone.gui.screen.wizard.WizardConfig; 5 | import com.mojang.blaze3d.vertex.PoseStack; 6 | import net.minecraft.client.gui.widget.button.Button; 7 | import net.minecraft.client.resources.I18n; 8 | import net.minecraft.util.text.StringTextComponent; 9 | 10 | import java.util.function.Supplier; 11 | 12 | public class BackProceedFooter extends ScreenAddon { 13 | private Button proceedButton; 14 | private Runnable onBack; 15 | private Runnable onProceed; 16 | private Supplier proceedLabel; 17 | private Supplier isProceedVisible; 18 | 19 | public BackProceedFooter() { 20 | 21 | } 22 | 23 | public void completeConstructor(Runnable onProceed, Supplier proceedLabel, Supplier isProceedVisible) { 24 | this.onProceed = onProceed; 25 | this.proceedLabel = proceedLabel; 26 | this.isProceedVisible = isProceedVisible; 27 | } 28 | 29 | public void overrideOnBack(Runnable onBack) { 30 | this.onBack = onBack; 31 | } 32 | 33 | public void handleBack(FpvScreen screen) { 34 | if (this.onBack != null) { 35 | this.onBack.run(); 36 | return; 37 | } 38 | screen.getMinecraft().setScreen(screen.previousScreen); 39 | } 40 | 41 | @Override 42 | public void init(FpvScreen screen) { 43 | screen.addButton(new Button( 44 | WizardConfig.left, 45 | screen.height - 20 - WizardConfig.footerBottom, 46 | WizardConfig.wideButtonWidth, 47 | 20, 48 | new StringTextComponent(I18n.get("gui.back")), 49 | (Button button) -> this.handleBack(screen) 50 | )); 51 | 52 | this.proceedButton = new Button( 53 | screen.width - WizardConfig.wideButtonWidth - WizardConfig.right, 54 | screen.height - 20 - WizardConfig.footerBottom, 55 | WizardConfig.wideButtonWidth, 56 | 20, 57 | new StringTextComponent(I18n.get("gui.proceed")), 58 | (Button button) -> { 59 | this.onProceed.run(); 60 | } 61 | ); 62 | screen.addButton(this.proceedButton); 63 | } 64 | 65 | @Override 66 | public void render( 67 | FpvScreen screen, PoseStack matrixStack, int mouseX, int mouseY, float partialTicks 68 | ) { 69 | if (this.isProceedVisible != null) { 70 | if (!this.isProceedVisible.get()) { 71 | this.proceedButton.visible = false; 72 | } else { 73 | this.proceedButton.visible = true; 74 | if (this.proceedLabel != null) { 75 | this.proceedButton.setMessage(new StringTextComponent(this.proceedLabel.get())); 76 | } 77 | } 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/gui/screen/addon/DoneFooter.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.gui.screen.addon; 2 | 3 | import com.gluecode.fpvdrone.gui.screen.FpvScreen; 4 | import com.gluecode.fpvdrone.gui.screen.wizard.WizardConfig; 5 | import com.mojang.blaze3d.vertex.PoseStack; 6 | import net.minecraft.client.gui.widget.button.Button; 7 | import net.minecraft.client.resources.I18n; 8 | import net.minecraft.util.text.StringTextComponent; 9 | 10 | public class DoneFooter extends ScreenAddon { 11 | private Runnable onDone; 12 | 13 | public void overrideDone(Runnable onDone) { 14 | this.onDone = onDone; 15 | } 16 | 17 | public void handleDone(FpvScreen screen) { 18 | if (this.onDone != null) { 19 | this.onDone.run(); 20 | return; 21 | } 22 | if (screen.getMinecraft() != null) { 23 | screen.getMinecraft().setScreen(screen.previousScreen); 24 | } 25 | } 26 | 27 | @Override 28 | public void init(FpvScreen screen) { 29 | screen.addButton(new Button( 30 | screen.width - WizardConfig.wideButtonWidth - WizardConfig.right, 31 | screen.height - 20 - WizardConfig.footerBottom, 32 | WizardConfig.wideButtonWidth, 33 | 20, 34 | new StringTextComponent(I18n.get("gui.done")), 35 | (Button button) -> this.handleDone(screen) 36 | )); 37 | } 38 | 39 | @Override 40 | public void render( 41 | FpvScreen screen, PoseStack matrixStack, int mouseX, int mouseY, float partialTicks 42 | ) { 43 | 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/gui/screen/addon/ScreenAddon.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.gui.screen.addon; 2 | 3 | import com.gluecode.fpvdrone.gui.screen.FpvScreen; 4 | import com.mojang.blaze3d.vertex.PoseStack; 5 | 6 | public abstract class ScreenAddon { 7 | public abstract void init(FpvScreen screen); 8 | 9 | public abstract void render( 10 | FpvScreen screen, 11 | PoseStack matrixStack, 12 | int mouseX, 13 | int mouseY, 14 | float partialTicks 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/gui/screen/addon/ServerTitleWikiHeader.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.gui.screen.addon; 2 | 3 | import com.gluecode.fpvdrone.gui.screen.FpvScreen; 4 | import com.gluecode.fpvdrone.gui.screen.wizard.WizardConfig; 5 | import com.mojang.blaze3d.vertex.PoseStack; 6 | import net.minecraft.client.gui.AbstractGui; 7 | import net.minecraft.client.gui.screen.ConfirmOpenLinkScreen; 8 | import net.minecraft.client.gui.widget.button.Button; 9 | import net.minecraft.client.resources.I18n; 10 | import net.minecraft.util.Util; 11 | import net.minecraft.util.text.StringTextComponent; 12 | 13 | public class ServerTitleWikiHeader extends ScreenAddon { 14 | String title; 15 | 16 | public ServerTitleWikiHeader(String title) { 17 | this.title = title; 18 | } 19 | 20 | @Override 21 | public void init(FpvScreen screen) { 22 | screen.addButton(new Button( 23 | WizardConfig.left, 24 | WizardConfig.headerTop, 25 | WizardConfig.shortButtonWidth, 26 | 20, 27 | new StringTextComponent(I18n.get("fpvdrone.settings.server")), 28 | (p_244738_1_) -> { 29 | screen.getMinecraft().setScreen(new ConfirmOpenLinkScreen((p_244739_1_) -> { 30 | if (p_244739_1_) { 31 | Util.getPlatform().openUri( 32 | "https://minecraftfpv.com/wiki/joiningServer"); 33 | } 34 | screen.getMinecraft().setScreen(screen); 35 | }, "https://minecraftfpv.com/wiki/joiningServer", true)); 36 | } 37 | )); 38 | 39 | screen.addButton(new Button( 40 | screen.width - WizardConfig.right - WizardConfig.shortButtonWidth, 41 | WizardConfig.headerTop, 42 | WizardConfig.shortButtonWidth, 43 | 20, 44 | new StringTextComponent(I18n.get("fpvdrone.settings.wiki")), 45 | (p_244738_1_) -> { 46 | screen.getMinecraft().setScreen(new ConfirmOpenLinkScreen((p_244739_1_) -> { 47 | if (p_244739_1_) { 48 | Util.getPlatform() 49 | .openUri("https://minecraftfpv.com/wiki"); 50 | } 51 | 52 | screen.getMinecraft().setScreen(screen); 53 | }, "https://minecraftfpv.com/wiki", true)); 54 | } 55 | )); 56 | } 57 | 58 | @Override 59 | public void render( 60 | FpvScreen screen, PoseStack matrixStack, int mouseX, int mouseY, float partialTicks 61 | ) { 62 | AbstractGui.drawCenteredString(matrixStack, screen.getMinecraft().font, this.title, 63 | screen.width / 2, 64 | WizardConfig.headerTop + 20 / 2 - screen.getMinecraft().font.lineHeight / 2, 65 | 0xFFFFFF 66 | ); 67 | // AbstractGui.drawCenteredString( 68 | // matrixStack, 69 | // screen.getMinecraft().font, 70 | // InputHandler.getControllerName(InputHandler.getControllerId()) 71 | // .equalsIgnoreCase("") ? I18n.get( 72 | // "fpvdrone.settings.nocontroller") : 73 | // InputHandler.getControllerName(InputHandler.getControllerId()), 74 | // screen.width / 2, 75 | // 26, 76 | // 0xFFFFFF 77 | // ); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/gui/screen/addon/WizardDoneFooter.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.gui.screen.addon; 2 | 3 | import com.gluecode.fpvdrone.gui.GuiEvents; 4 | import com.gluecode.fpvdrone.gui.screen.FpvScreen; 5 | import com.gluecode.fpvdrone.gui.screen.wizard.WizardConfig; 6 | import com.mojang.blaze3d.vertex.PoseStack; 7 | import net.minecraft.client.gui.widget.button.Button; 8 | import net.minecraft.client.resources.I18n; 9 | import net.minecraft.util.text.StringTextComponent; 10 | 11 | public class WizardDoneFooter extends ScreenAddon { 12 | private Runnable onDone; 13 | 14 | public void overrideDone(Runnable onDone) { 15 | this.onDone = onDone; 16 | } 17 | 18 | public void handleDone(FpvScreen screen) { 19 | if (this.onDone != null) { 20 | this.onDone.run(); 21 | return; 22 | } 23 | if (screen.getMinecraft() != null) { 24 | screen.getMinecraft().setScreen(screen.previousScreen); 25 | } 26 | } 27 | 28 | public void handleOpenWizard(FpvScreen screen) { 29 | GuiEvents.openWelcomeScreen(screen); 30 | } 31 | 32 | @Override 33 | public void init(FpvScreen screen) { 34 | screen.addButton(new Button( 35 | WizardConfig.left, 36 | screen.height - 20 - WizardConfig.footerBottom, 37 | WizardConfig.wideButtonWidth * 2, 38 | 20, 39 | new StringTextComponent(I18n.get("fpvdrone.wizard")), 40 | (Button button) -> this.handleOpenWizard(screen) 41 | )); 42 | 43 | screen.addButton(new Button( 44 | screen.width - WizardConfig.wideButtonWidth - WizardConfig.right, 45 | screen.height - 20 - WizardConfig.footerBottom, 46 | WizardConfig.wideButtonWidth, 47 | 20, 48 | new StringTextComponent(I18n.get("gui.done")), 49 | (Button button) -> this.handleDone(screen) 50 | )); 51 | } 52 | 53 | @Override 54 | public void render( 55 | FpvScreen screen, PoseStack matrixStack, int mouseX, int mouseY, float partialTicks 56 | ) { 57 | 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/gui/screen/addon/WizardHeader.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.gui.screen.addon; 2 | 3 | import com.gluecode.fpvdrone.Main; 4 | import com.gluecode.fpvdrone.gui.GuiEvents; 5 | import com.gluecode.fpvdrone.gui.screen.FpvScreen; 6 | import com.gluecode.fpvdrone.gui.screen.wizard.HelpQAScreen; 7 | import com.gluecode.fpvdrone.gui.screen.wizard.HelpScreen; 8 | import com.gluecode.fpvdrone.gui.screen.wizard.WelcomeScreen; 9 | import com.gluecode.fpvdrone.gui.screen.wizard.WizardConfig; 10 | import com.mojang.blaze3d.vertex.PoseStack; 11 | import net.minecraft.client.Minecraft; 12 | import net.minecraft.client.gui.AbstractGui; 13 | import net.minecraft.client.gui.screen.ConfirmOpenLinkScreen; 14 | import net.minecraft.client.gui.widget.button.Button; 15 | import net.minecraft.client.resources.I18n; 16 | import net.minecraft.util.Util; 17 | import net.minecraft.util.text.ITextComponent; 18 | import net.minecraft.util.text.StringTextComponent; 19 | 20 | import javax.annotation.Nullable; 21 | import java.util.*; 22 | 23 | public class WizardHeader extends ScreenAddon { 24 | @Nullable 25 | public String title; 26 | public boolean showHelpButton; 27 | @Nullable 28 | public Runnable overrideOnDone; 29 | 30 | public LinkedHashMap qa = null; 31 | 32 | public WizardHeader(@Nullable String title, boolean showHelpButton) { 33 | this.title = title; 34 | this.showHelpButton = showHelpButton; 35 | } 36 | 37 | public void handleDone() { 38 | if (this.overrideOnDone != null) { 39 | this.overrideOnDone.run(); 40 | return; 41 | } 42 | if (WelcomeScreen.currentWelcomeScreen != null) { 43 | WelcomeScreen.currentWelcomeScreen.getMinecraft() 44 | .setScreen(WelcomeScreen.currentWelcomeScreen); 45 | } 46 | } 47 | 48 | public void setOverrideOnDone(@Nullable Runnable overrideOnDone) { 49 | this.overrideOnDone = overrideOnDone; 50 | } 51 | 52 | public void addHelpQA(String question, ITextComponent answer) { 53 | if (qa == null) { 54 | qa = new LinkedHashMap<>(); 55 | } 56 | qa.put(question, answer); 57 | } 58 | 59 | @Override 60 | public void init(FpvScreen screen) { 61 | screen.addButton(new Button( 62 | WizardConfig.left, 63 | WizardConfig.headerTop, 64 | 20, 65 | 20, 66 | new StringTextComponent(""), 67 | (Button button) -> this.handleDone() 68 | )); 69 | 70 | if (this.showHelpButton) { 71 | screen.addButton(new Button( 72 | screen.width - WizardConfig.right - WizardConfig.shortButtonWidth, 73 | WizardConfig.headerTop, 74 | WizardConfig.shortButtonWidth, 75 | 20, 76 | new StringTextComponent(I18n.get("fpvdrone.wizard.header.help")), 77 | (Button button) -> { 78 | if (qa == null) { 79 | screen.getMinecraft().setScreen(new HelpScreen(screen)); 80 | } else { 81 | screen.getMinecraft().setScreen(new HelpQAScreen(screen, qa)); 82 | } 83 | } 84 | )); 85 | } 86 | } 87 | 88 | @Override 89 | public void render( 90 | FpvScreen screen, 91 | PoseStack matrixStack, 92 | int mouseX, 93 | int mouseY, 94 | float partialTicks 95 | ) { 96 | if (this.title != null) { 97 | AbstractGui.drawCenteredString(matrixStack, 98 | screen.getMinecraft().font, 99 | this.title, 100 | screen.width / 2, 101 | WizardConfig.headerTop + 20 / 2 - screen.getMinecraft().font.lineHeight / 2, 102 | 0xFFFFFF 103 | ); 104 | } 105 | 106 | Minecraft minecraft = Minecraft.getInstance(); 107 | int width = minecraft.font.width("x"); 108 | 109 | minecraft.font.draw( 110 | matrixStack, 111 | "x", 112 | WizardConfig.left + 10 - width / 2f + 1, 113 | WizardConfig.headerTop + 10 - minecraft.font.lineHeight / 2f - 1 + 1, 114 | 0x464646 115 | ); 116 | 117 | minecraft.font.draw( 118 | matrixStack, 119 | "x", 120 | WizardConfig.left + 10 - width / 2f, 121 | WizardConfig.headerTop + 10 - minecraft.font.lineHeight / 2f - 1, 122 | 0xFFFFFF 123 | ); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/gui/screen/wizard/ChooseControllerScreen.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.gui.screen.wizard; 2 | 3 | import com.gluecode.fpvdrone.gui.GuiEvents; 4 | import com.gluecode.fpvdrone.gui.list.ControllerChoicesList; 5 | import com.gluecode.fpvdrone.gui.list.FPVList; 6 | import com.gluecode.fpvdrone.gui.screen.FpvScreen; 7 | import com.gluecode.fpvdrone.gui.screen.addon.BackFooter; 8 | import com.gluecode.fpvdrone.gui.screen.addon.WizardHeader; 9 | import com.gluecode.fpvdrone.input.ControllerReader; 10 | import com.mojang.blaze3d.vertex.PoseStack; 11 | import net.minecraft.client.gui.screen.Screen; 12 | import net.minecraft.client.resources.I18n; 13 | import net.minecraft.util.text.Color; 14 | import net.minecraft.util.text.StringTextComponent; 15 | import net.minecraft.util.text.Style; 16 | import net.minecraft.util.text.TextFormatting; 17 | import net.minecraft.util.text.event.ClickEvent; 18 | 19 | public class ChooseControllerScreen extends FpvScreen { 20 | private FPVList list; 21 | 22 | public ChooseControllerScreen( 23 | Screen previousScreen 24 | ) { 25 | super( 26 | previousScreen, new WizardHeader(I18n.get("fpvdrone.wizard.chooseController.title"), true), new BackFooter() 27 | ); 28 | 29 | WizardHeader header = (WizardHeader) this.header; 30 | if (header != null) { 31 | header.addHelpQA(I18n.get("fpvdrone.wizard.chooseController.q1"), new StringTextComponent(I18n.get("fpvdrone.wizard.chooseController.a1"))); 32 | 33 | StringTextComponent a2 = new StringTextComponent(I18n.get("fpvdrone.wizard.chooseController.a2") + " "); 34 | StringTextComponent a2url = new StringTextComponent( 35 | "How?"); 36 | Style style = a2url.getStyle() 37 | .withClickEvent(new ClickEvent( 38 | ClickEvent.Action.OPEN_URL, 39 | "https://minecraftfpv.com/wiki/controllerVerify" 40 | )) 41 | .withColor(Color.fromLegacyFormat(TextFormatting.BLUE)) 42 | .setUnderlined(true); 43 | a2url.setStyle(style); 44 | a2.append(a2url); 45 | header.addHelpQA(I18n.get("fpvdrone.wizard.chooseController.q2"), a2); 46 | } 47 | } 48 | 49 | public void handleIdSet(int id) { 50 | ControllerReader.setControllerId(id); 51 | GuiEvents.openCalibrateControllerStickScreen(this); 52 | } 53 | 54 | @Override 55 | protected void init() { 56 | super.init(); 57 | this.list = new ControllerChoicesList( 58 | this, 59 | this::handleIdSet 60 | ); 61 | this.children.add(this.list); 62 | } 63 | 64 | @Override 65 | public void renderCustom( 66 | PoseStack matrixStack, 67 | int mouseX, 68 | int mouseY, 69 | float partialTicks 70 | ) { 71 | this.list.render(matrixStack, mouseX, mouseY, partialTicks); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/gui/screen/wizard/CompleteScreen.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.gui.screen.wizard; 2 | 3 | import com.gluecode.fpvdrone.gui.GuiEvents; 4 | import com.gluecode.fpvdrone.gui.screen.EmptyListScreen; 5 | import com.gluecode.fpvdrone.gui.screen.addon.DoneFooter; 6 | import com.gluecode.fpvdrone.util.SettingsLoader; 7 | import com.mojang.blaze3d.vertex.PoseStack; 8 | import net.minecraft.client.Minecraft; 9 | import net.minecraft.client.gui.screen.Screen; 10 | import net.minecraft.client.gui.widget.button.Button; 11 | import net.minecraft.client.resources.I18n; 12 | import net.minecraft.util.text.StringTextComponent; 13 | 14 | public class CompleteScreen extends EmptyListScreen { 15 | public enum Mode { 16 | SWITCH, 17 | KEY 18 | } 19 | 20 | private Mode mode; 21 | 22 | public CompleteScreen( 23 | Screen previousScreen, 24 | Mode mode 25 | ) { 26 | super(previousScreen, null, new DoneFooter()); 27 | 28 | this.mode = mode; 29 | 30 | DoneFooter footer = (DoneFooter) this.footer; 31 | if (footer != null) { 32 | footer.overrideDone(this::handleDone); 33 | } 34 | } 35 | 36 | public void handleDone() { 37 | SettingsLoader.firstTimeSetup = false; 38 | SettingsLoader.save(); 39 | if (WelcomeScreen.currentWelcomeScreen != null) { 40 | WelcomeScreen.currentWelcomeScreen.getMinecraft() 41 | .setScreen(WelcomeScreen.currentWelcomeScreen.previousScreen); 42 | } 43 | } 44 | 45 | public void handleMore(Button button) { 46 | SettingsLoader.firstTimeSetup = false; 47 | SettingsLoader.save(); 48 | if (WelcomeScreen.currentWelcomeScreen != null) { 49 | GuiEvents.openFpvSettingsScreen(WelcomeScreen.currentWelcomeScreen.previousScreen); 50 | } 51 | } 52 | 53 | @Override 54 | protected void init() { 55 | super.init(); 56 | 57 | this.addButton(new Button( 58 | this.width / 2 - WizardConfig.wideButtonWidth / 2, 59 | WizardConfig.headerHeight + WizardConfig.contentTop + WizardConfig.titleSpacing + 40 + WizardConfig.doubleButtonSpacing + 2, 60 | WizardConfig.wideButtonWidth, 61 | 20, 62 | new StringTextComponent(I18n.get("fpvdrone.wizard.complete.more")), 63 | this::handleMore 64 | )); 65 | } 66 | 67 | @Override 68 | public void renderCustom( 69 | PoseStack matrixStack, 70 | int mouseX, 71 | int mouseY, 72 | float partialTicks 73 | ) { 74 | super.renderCustom(matrixStack, mouseX, mouseY, partialTicks); 75 | 76 | float lineHeight = WizardConfig.lineHeight; 77 | 78 | Minecraft minecraft = Minecraft.getInstance(); 79 | String ready = I18n.get("fpvdrone.wizard.complete.ready"); 80 | int readyWidth = minecraft.font.width(ready); 81 | 82 | String goBackSwitch = this.mode == Mode.SWITCH ? I18n.get("fpvdrone.wizard.complete.goBackSwitch") : I18n.get("fpvdrone.wizard.complete.goBackKey"); 83 | String switchTop = this.safeSplitGet(goBackSwitch, "\n", 0); 84 | String switchBottom = this.safeSplitGet(goBackSwitch, "\n", 1); 85 | 86 | int switchTopWidth = minecraft.font.width(switchTop); 87 | int switchBottonWidth = minecraft.font.width(switchBottom); 88 | 89 | minecraft.font.draw( 90 | matrixStack, 91 | ready, 92 | this.width / 2f - readyWidth / 2f, 93 | WizardConfig.headerHeight + WizardConfig.contentTop, 94 | 0xFFFFFF 95 | ); 96 | 97 | minecraft.font.draw( 98 | matrixStack, 99 | switchTop, 100 | this.width / 2f - switchTopWidth / 2f, 101 | WizardConfig.headerHeight + WizardConfig.contentTop + 2 * minecraft.font.lineHeight * lineHeight, 102 | 0xFFFFFF 103 | ); 104 | 105 | minecraft.font.draw( 106 | matrixStack, 107 | switchBottom, 108 | this.width / 2f - switchBottonWidth / 2f, 109 | WizardConfig.headerHeight + WizardConfig.contentTop + 3 * minecraft.font.lineHeight * lineHeight, 110 | 0xFFFFFF 111 | ); 112 | } 113 | 114 | public String safeSplitGet(String str, String regex, int index) { 115 | if (index < 0) { 116 | return ""; 117 | } 118 | 119 | String[] split = str.split(regex); 120 | 121 | if (index <= split.length) { 122 | return split[index]; 123 | } else { 124 | return ""; 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/gui/screen/wizard/DecideControllerScreen.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.gui.screen.wizard; 2 | 3 | import com.gluecode.fpvdrone.gui.GuiEvents; 4 | import com.gluecode.fpvdrone.gui.screen.EmptyListScreen; 5 | import com.gluecode.fpvdrone.gui.screen.addon.WizardHeader; 6 | import com.mojang.blaze3d.vertex.PoseStack; 7 | import net.minecraft.client.Minecraft; 8 | import net.minecraft.client.gui.screen.Screen; 9 | import net.minecraft.client.gui.widget.button.Button; 10 | import net.minecraft.client.resources.I18n; 11 | import net.minecraft.util.text.StringTextComponent; 12 | 13 | public class DecideControllerScreen extends EmptyListScreen { 14 | 15 | public DecideControllerScreen( 16 | Screen previousScreen 17 | ) { 18 | super(previousScreen, new WizardHeader(null, false), null); 19 | } 20 | 21 | @Override 22 | protected void init() { 23 | super.init(); 24 | 25 | this.addButton(new Button( 26 | this.width / 2 - WizardConfig.wideButtonWidth / 2, 27 | WizardConfig.headerHeight + WizardConfig.contentTop + WizardConfig.titleSpacing, 28 | WizardConfig.wideButtonWidth, 29 | 20, 30 | new StringTextComponent(I18n.get("gui.yes")), 31 | this::handleYes 32 | )); 33 | 34 | this.addButton(new Button( 35 | this.width / 2 - WizardConfig.wideButtonWidth / 2, 36 | WizardConfig.headerHeight + WizardConfig.contentTop + WizardConfig.titleSpacing + 20 + WizardConfig.doubleButtonSpacing, 37 | WizardConfig.wideButtonWidth, 38 | 20, 39 | new StringTextComponent(I18n.get("gui.no")), 40 | this::handleNo 41 | )); 42 | } 43 | 44 | public void handleYes(Button button) { 45 | GuiEvents.openChooseControllerScreen(this); 46 | } 47 | 48 | public void handleNo(Button button) { 49 | GuiEvents.openCalibrateKeyboardScreen(this); 50 | } 51 | 52 | @Override 53 | public void renderCustom( 54 | PoseStack matrixStack, 55 | int mouseX, 56 | int mouseY, 57 | float partialTicks 58 | ) { 59 | super.renderCustom(matrixStack, mouseX, mouseY, partialTicks); 60 | 61 | Minecraft minecraft = Minecraft.getInstance(); 62 | String welcomeString = I18n.get("fpvdrone.wizard.decideController.title"); 63 | int welcomeWidth = minecraft.font.width(welcomeString); 64 | minecraft.font.draw( 65 | matrixStack, 66 | welcomeString, 67 | this.width / 2f - welcomeWidth / 2f, 68 | WizardConfig.headerHeight + WizardConfig.contentTop, 69 | 0xFFFFFF 70 | ); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/gui/screen/wizard/HelpQAScreen.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.gui.screen.wizard; 2 | 3 | import com.gluecode.fpvdrone.gui.screen.EmptyListScreen; 4 | import com.gluecode.fpvdrone.gui.screen.addon.BackHelpFooter; 5 | import com.gluecode.fpvdrone.gui.screen.addon.WizardHeader; 6 | import com.mojang.blaze3d.vertex.PoseStack; 7 | import net.minecraft.client.Minecraft; 8 | import net.minecraft.client.gui.IGuiEventListener; 9 | import net.minecraft.client.gui.screen.ConfirmOpenLinkScreen; 10 | import net.minecraft.client.gui.screen.Screen; 11 | import net.minecraft.util.Util; 12 | import net.minecraft.util.text.ITextComponent; 13 | import net.minecraft.util.text.StringTextComponent; 14 | import net.minecraft.util.text.Style; 15 | import net.minecraft.util.text.event.ClickEvent; 16 | 17 | import javax.annotation.Nullable; 18 | import java.util.LinkedHashMap; 19 | 20 | public class HelpQAScreen extends EmptyListScreen { 21 | private LinkedHashMap qa; 22 | 23 | public HelpQAScreen( 24 | Screen previousScreen, 25 | LinkedHashMap qa 26 | ) { 27 | super(previousScreen, null, new BackHelpFooter()); 28 | this.qa = qa; 29 | } 30 | 31 | @Override 32 | protected void init() { 33 | super.init(); 34 | } 35 | 36 | @Override 37 | public void renderCustom( 38 | PoseStack matrixStack, 39 | int mouseX, 40 | int mouseY, 41 | float partialTicks 42 | ) { 43 | super.renderCustom(matrixStack, mouseX, mouseY, partialTicks); 44 | 45 | Minecraft minecraft = Minecraft.getInstance(); 46 | float lineMult = WizardConfig.lineHeight; 47 | int lineHeight = (int) (lineMult * minecraft.font.lineHeight); 48 | 49 | String[] questions = this.qa.keySet().toArray(new String[0]); 50 | for (int i = 0; i < questions.length; i++) { 51 | int y = 3 * lineHeight * i; 52 | String q = questions[i]; 53 | ITextComponent a = this.qa.get(q); 54 | StringTextComponent answer = new StringTextComponent("A: "); 55 | answer.append(a); 56 | 57 | minecraft.font.draw( 58 | matrixStack, 59 | "Q: " + q, 60 | 3 * WizardConfig.left, 61 | WizardConfig.headerHeight + WizardConfig.contentTop + y, 62 | 0xFFFFFF 63 | ); 64 | 65 | minecraft.font.draw( 66 | matrixStack, 67 | answer, 68 | 4 * WizardConfig.left, 69 | WizardConfig.headerHeight + WizardConfig.contentTop + y + lineHeight, 70 | 0xFFFFFF 71 | ); 72 | } 73 | } 74 | 75 | public @Nullable 76 | Style getStyleAt(double mouseX, double mouseY) { 77 | // Find if any links were clicked. 78 | String[] questions = this.qa.keySet().toArray(new String[0]); 79 | 80 | Minecraft minecraft = Minecraft.getInstance(); 81 | float lineMult = WizardConfig.lineHeight; 82 | int lineHeight = (int) (lineMult * minecraft.font.lineHeight); 83 | 84 | for (int i = 0; i < questions.length; i++) { 85 | int y = 3 * lineHeight * i; 86 | String q = questions[i]; 87 | ITextComponent a = this.qa.get(q); 88 | StringTextComponent answer = new StringTextComponent("A: "); 89 | answer.append(a); 90 | 91 | int answerY = WizardConfig.headerHeight + 92 | WizardConfig.contentTop + 93 | y + 94 | lineHeight; 95 | 96 | if (answerY <= mouseY && mouseY <= answerY + minecraft.font.lineHeight) { 97 | // This answer is on the same line as the mouseClick. 98 | // Do not worry about line wrapping. It will not be supported. 99 | 100 | int answerX = 4 * WizardConfig.left; 101 | Style style = minecraft.font.getSplitter().componentStyleAtWidth( 102 | answer, 103 | (int) (mouseX - answerX) 104 | ); 105 | 106 | if (style != null) { 107 | return style; 108 | } 109 | } 110 | } 111 | return null; 112 | } 113 | 114 | @Override 115 | public boolean mouseClicked(double mouseX, double mouseY, int p_231044_5_) { 116 | Style style = this.getStyleAt(mouseX, mouseY); 117 | 118 | if ( 119 | style != null && 120 | style.getClickEvent() != null && 121 | style.getClickEvent().getAction().equals(ClickEvent.Action.OPEN_URL) 122 | ) { 123 | String url = style.getClickEvent().getValue(); 124 | this.handleLinkClick(url); 125 | return true; 126 | } 127 | 128 | return super.mouseClicked(mouseX, mouseY, p_231044_5_); 129 | } 130 | 131 | public void handleLinkClick(String url) { 132 | this.getMinecraft().setScreen(new ConfirmOpenLinkScreen((p_244739_1_) -> { 133 | if (p_244739_1_) { 134 | Util.getPlatform().openUri( 135 | url); 136 | } 137 | this.getMinecraft().setScreen(this); 138 | }, url, true)); 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/gui/screen/wizard/HelpScreen.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.gui.screen.wizard; 2 | 3 | import com.gluecode.fpvdrone.gui.screen.EmptyListScreen; 4 | import com.gluecode.fpvdrone.gui.screen.addon.BackFooter; 5 | import com.gluecode.fpvdrone.gui.screen.addon.WizardHeader; 6 | import com.mojang.blaze3d.vertex.PoseStack; 7 | import net.minecraft.client.Minecraft; 8 | import net.minecraft.client.gui.screen.ConfirmOpenLinkScreen; 9 | import net.minecraft.client.gui.screen.Screen; 10 | import net.minecraft.client.gui.widget.button.Button; 11 | import net.minecraft.client.resources.I18n; 12 | import net.minecraft.util.Util; 13 | import net.minecraft.util.text.StringTextComponent; 14 | 15 | public class HelpScreen extends EmptyListScreen { 16 | 17 | public HelpScreen( 18 | Screen previousScreen 19 | ) { 20 | super(previousScreen, null, new BackFooter()); 21 | } 22 | 23 | public void handleDiscord(Screen screen) { 24 | screen.getMinecraft().setScreen(new ConfirmOpenLinkScreen((p_244739_1_) -> { 25 | if (p_244739_1_) { 26 | Util.getPlatform().openUri( 27 | "https://discord.gg/WJfhXuz"); 28 | } 29 | screen.getMinecraft().setScreen(screen); 30 | }, "https://discord.gg/WJfhXuz", true)); 31 | } 32 | 33 | public void handleWiki(Screen screen) { 34 | screen.getMinecraft().setScreen(new ConfirmOpenLinkScreen((p_244739_1_) -> { 35 | if (p_244739_1_) { 36 | Util.getPlatform().openUri( 37 | "https://minecraftfpv.com/wiki"); 38 | } 39 | screen.getMinecraft().setScreen(screen); 40 | }, "https://minecraftfpv.com/wiki", true)); 41 | } 42 | 43 | @Override 44 | protected void init() { 45 | super.init(); 46 | 47 | this.addButton(new Button( 48 | this.width / 2 - WizardConfig.shortButtonWidth / 2, 49 | WizardConfig.headerHeight + WizardConfig.contentTop + WizardConfig.titleSpacing, 50 | WizardConfig.shortButtonWidth, 51 | 20, 52 | new StringTextComponent("Discord"), 53 | (button) -> this.handleDiscord(this) 54 | )); 55 | 56 | this.addButton(new Button( 57 | this.width / 2 - WizardConfig.shortButtonWidth / 2, 58 | WizardConfig.headerHeight + WizardConfig.contentTop + WizardConfig.titleSpacing + 20 + WizardConfig.doubleButtonSpacing, 59 | WizardConfig.shortButtonWidth, 60 | 20, 61 | new StringTextComponent(I18n.get("fpvdrone.settings.wiki")), 62 | (button) -> this.handleWiki(this) 63 | )); 64 | } 65 | 66 | @Override 67 | public void renderCustom( 68 | PoseStack matrixStack, 69 | int mouseX, 70 | int mouseY, 71 | float partialTicks 72 | ) { 73 | super.renderCustom(matrixStack, mouseX, mouseY, partialTicks); 74 | 75 | Minecraft minecraft = Minecraft.getInstance(); 76 | String title = I18n.get("fpvdrone.wizard.help.title"); 77 | int welcomeWidth = minecraft.font.width(title); 78 | minecraft.font.draw( 79 | matrixStack, 80 | title, 81 | this.width / 2f - welcomeWidth / 2f, 82 | WizardConfig.headerHeight + WizardConfig.contentTop, 83 | 0xFFFFFF 84 | ); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/gui/screen/wizard/WelcomeScreen.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.gui.screen.wizard; 2 | 3 | import com.gluecode.fpvdrone.gui.GuiEvents; 4 | import com.gluecode.fpvdrone.gui.screen.EmptyListScreen; 5 | import com.gluecode.fpvdrone.gui.screen.FpvScreen; 6 | import com.gluecode.fpvdrone.gui.screen.addon.WizardHeader; 7 | import com.gluecode.fpvdrone.util.SettingsLoader; 8 | import com.mojang.blaze3d.vertex.PoseStack; 9 | import net.minecraft.client.Minecraft; 10 | import net.minecraft.client.gui.screen.Screen; 11 | import net.minecraft.client.gui.widget.button.Button; 12 | import net.minecraft.client.resources.I18n; 13 | import net.minecraft.util.text.StringTextComponent; 14 | 15 | import javax.annotation.Nullable; 16 | 17 | public class WelcomeScreen extends EmptyListScreen { 18 | // currentWelcomeScreen is set whenever a welcomeScreen is instantiated. 19 | // it is used to make the "close" button functional. 20 | @Nullable 21 | public static WelcomeScreen currentWelcomeScreen; 22 | 23 | public WelcomeScreen( 24 | Screen previousScreen 25 | ) { 26 | super(previousScreen, new WizardHeader(null, false), null); 27 | WelcomeScreen.currentWelcomeScreen = this; 28 | 29 | WizardHeader header = (WizardHeader) this.header; 30 | if (header != null) { 31 | header.setOverrideOnDone(this::handleDone); 32 | } 33 | } 34 | 35 | public void handleDone() { 36 | this.getMinecraft().setScreen(this.previousScreen); 37 | } 38 | 39 | @Override 40 | protected void init() { 41 | super.init(); 42 | 43 | this.addButton(new Button( 44 | this.width / 2 - WizardConfig.wideButtonWidth / 2, 45 | WizardConfig.headerHeight + WizardConfig.contentTop + WizardConfig.titleSpacing, 46 | WizardConfig.wideButtonWidth, 47 | 20, 48 | new StringTextComponent(I18n.get("fpvdrone.wizard.welcome.new")), 49 | this::handleNew 50 | )); 51 | 52 | this.addButton(new Button( 53 | this.width / 2 - WizardConfig.wideButtonWidth / 2, 54 | WizardConfig.headerHeight + WizardConfig.contentTop + WizardConfig.titleSpacing + 20 + WizardConfig.doubleButtonSpacing, 55 | WizardConfig.wideButtonWidth, 56 | 20, 57 | new StringTextComponent(I18n.get("fpvdrone.wizard.welcome.returning")), 58 | this::handleReturning 59 | )); 60 | } 61 | 62 | public void handleNew(Button button) { 63 | GuiEvents.openDecideControllerScreen(this); 64 | } 65 | 66 | public void handleReturning(Button button) { 67 | SettingsLoader.firstTimeSetup = false; 68 | SettingsLoader.save(); 69 | 70 | if (this.previousScreen instanceof FpvScreen) { 71 | // Entered via "Calibration Wizard" button. 72 | // The previous screen is where we are going to. 73 | this.handleDone(); 74 | } else { 75 | // Entered as first timer. 76 | GuiEvents.openFpvSettingsScreen(this.previousScreen); 77 | } 78 | } 79 | 80 | @Override 81 | public void renderCustom( 82 | PoseStack matrixStack, 83 | int mouseX, 84 | int mouseY, 85 | float partialTicks 86 | ) { 87 | super.renderCustom(matrixStack, mouseX, mouseY, partialTicks); 88 | 89 | Minecraft minecraft = Minecraft.getInstance(); 90 | String welcomeString = I18n.get("fpvdrone.wizard.welcome.title"); 91 | int welcomeWidth = minecraft.font.width(welcomeString); 92 | minecraft.font.draw( 93 | matrixStack, 94 | welcomeString, 95 | this.width / 2f - welcomeWidth / 2f, 96 | WizardConfig.headerHeight + WizardConfig.contentTop, 97 | 0xFFFFFF 98 | ); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/gui/screen/wizard/WizardConfig.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.gui.screen.wizard; 2 | 3 | public class WizardConfig { 4 | public static final int headerTop = 12; 5 | public static final int left = 12; 6 | public static final int right = 12; 7 | public static final int footerBottom = 6; 8 | 9 | // These values should not be changed in order to look the same as vanilla: 10 | public static final int headerHeight = 43; 11 | public static final int footerHeight = 32; 12 | 13 | public static final int contentTop = 33; 14 | public static final int titleSpacing = 36; 15 | public static final int doubleButtonSpacing = 12; 16 | public static final int wideButtonWidth = 100; 17 | public static final int shortButtonWidth = 60; 18 | 19 | public static final float lineHeight = 1.6f; 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/gui/widget/FpvSettingsButton.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.gui.widget; 2 | 3 | import net.minecraft.client.gui.widget.button.Button; 4 | import net.minecraft.client.resources.I18n; 5 | import net.minecraft.util.text.StringTextComponent; 6 | 7 | public class FpvSettingsButton extends Button { 8 | public FpvSettingsButton(int x, int y, IPressable onPress) { 9 | super( 10 | x, 11 | y, 12 | 30, 13 | 20, 14 | new StringTextComponent(I18n.get("fpvdrone.settings.fpv")), 15 | onPress 16 | ); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/gui/widget/RateChart.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.gui.widget; 2 | 3 | import com.gluecode.fpvdrone.input.ControllerConfig; 4 | import com.gluecode.fpvdrone.input.ControllerReader; 5 | import com.gluecode.fpvdrone.render.StickOverlayRenderer; 6 | import com.gluecode.fpvdrone.util.Transforms; 7 | import com.mojang.blaze3d.vertex.PoseStack; 8 | import net.minecraft.client.Minecraft; 9 | import net.minecraft.client.gui.widget.Widget; 10 | import net.minecraft.client.renderer.BufferBuilder; 11 | import net.minecraft.client.renderer.Tessellator; 12 | import net.minecraft.client.renderer.vertex.DefaultVertexFormats; 13 | import net.minecraft.util.math.vector.Matrix4f; 14 | import net.minecraft.util.text.StringTextComponent; 15 | import org.lwjgl.opengl.GL11; 16 | 17 | public class RateChart extends Widget { 18 | private boolean showYaw; 19 | private boolean showPitch; 20 | private boolean showRoll; 21 | 22 | public RateChart( 23 | int x, 24 | int y, 25 | int width, 26 | int height 27 | ) { 28 | super(x, y, width, height, new StringTextComponent("Rates")); 29 | this.showYaw = true; 30 | this.showPitch = true; 31 | this.showRoll = true; 32 | this.active = false; 33 | } 34 | 35 | public void render(PoseStack matrixStack, int mouseX, int mouseY, float partialTicks) { 36 | Tessellator tessellator = Tessellator.getInstance(); 37 | BufferBuilder buffer = tessellator.getBuilder(); 38 | buffer.begin(GL11.GL_LINES, DefaultVertexFormats.POSITION_COLOR); 39 | 40 | matrixStack.pushPose(); 41 | matrixStack.translate(this.x, this.y + this.height, 0); 42 | matrixStack.scale(this.width, -this.height, 1); 43 | 44 | Matrix4f matrix = matrixStack.last().pose(); 45 | 46 | // Render curve: 47 | float yawMax = 0; 48 | float pitchMax = 0; 49 | float rollMax = 0; 50 | if (this.showYaw) { 51 | float crate = ControllerConfig.getYawRate(); 52 | float csuper = ControllerConfig.getYawSuper(); 53 | float cexpo = ControllerConfig.getYawExpo(); 54 | yawMax = Transforms.bfRate( 55 | 1, 56 | crate, 57 | csuper, 58 | cexpo 59 | ); 60 | } 61 | if (this.showPitch) { 62 | float crate = ControllerConfig.getPitchRate(); 63 | float csuper = ControllerConfig.getPitchSuper(); 64 | float cexpo = ControllerConfig.getPitchExpo(); 65 | pitchMax = Transforms.bfRate( 66 | 1, 67 | crate, 68 | csuper, 69 | cexpo 70 | ); 71 | } 72 | if (this.showRoll) { 73 | float crate = ControllerConfig.getRollRate(); 74 | float csuper = ControllerConfig.getRollSuper(); 75 | float cexpo = ControllerConfig.getRollExpo(); 76 | rollMax = Transforms.bfRate( 77 | 1, 78 | crate, 79 | csuper, 80 | cexpo 81 | ); 82 | } 83 | float max = Math.max(yawMax, Math.max(pitchMax, rollMax)); 84 | 85 | if (this.showYaw) { 86 | float setpoint = ControllerReader.getYaw(); 87 | float crate = ControllerConfig.getYawRate(); 88 | float csuper = ControllerConfig.getYawSuper(); 89 | float cexpo = ControllerConfig.getYawExpo(); 90 | this.renderCurve(matrix, buffer, setpoint, crate, csuper, cexpo, max, 0xFF8040); 91 | } 92 | if (this.showPitch) { 93 | float setpoint = ControllerReader.getPitch(); 94 | float crate = ControllerConfig.getPitchRate(); 95 | float csuper = ControllerConfig.getPitchSuper(); 96 | float cexpo = ControllerConfig.getPitchExpo(); 97 | this.renderCurve(matrix, buffer, setpoint, crate, csuper, cexpo, max, 0x40FF80); 98 | } 99 | if (this.showRoll) { 100 | float setpoint = ControllerReader.getRoll(); 101 | float crate = ControllerConfig.getRollRate(); 102 | float csuper = ControllerConfig.getRollSuper(); 103 | float cexpo = ControllerConfig.getRollExpo(); 104 | this.renderCurve(matrix, buffer, setpoint, crate, csuper, cexpo, max, 0x4080FF); 105 | } 106 | 107 | // Render border 108 | buffer.vertex(matrix, 0, 0, 0).color(1f, 1f, 1f, 1f).endVertex(); 109 | buffer.vertex(matrix, 0, 1, 0).color(1f, 1f, 1f, 1f).endVertex(); 110 | buffer.vertex(matrix, 0, 0, 0).color(1f, 1f, 1f, 1f).endVertex(); 111 | buffer.vertex(matrix, 1, 0, 0).color(1f, 1f, 1f, 1f).endVertex(); 112 | buffer.vertex(matrix, 1, 0, 0).color(1f, 1f, 1f, 1f).endVertex(); 113 | buffer.vertex(matrix, 1, 1, 0).color(1f, 1f, 1f, 1f).endVertex(); 114 | buffer.vertex(matrix, 0, 1, 0).color(1f, 1f, 1f, 1f).endVertex(); 115 | buffer.vertex(matrix, 1, 1, 0).color(1f, 1f, 1f, 1f).endVertex(); 116 | 117 | matrixStack.popPose(); 118 | 119 | StickOverlayRenderer.applyLineMode(); 120 | tessellator.end(); 121 | StickOverlayRenderer.cleanLineMode(); 122 | 123 | // super.render(matrixStack, mouseX, mouseY, partialTicks); 124 | } 125 | 126 | protected void renderBg(PoseStack matrixStack, Minecraft minecraft, int mouseX, int mouseY) { 127 | } 128 | 129 | private void renderCurve(Matrix4f matrix, BufferBuilder buffer, float setpoint, float crate, float csuper, float cexpo, float max, int color) { 130 | int red = color >> 16; 131 | int green = (color & 0x00FF00) >> 8; 132 | int blue = color & 0x0000FF; 133 | 134 | float r = red / 255f; 135 | float g = green / 255f; 136 | float b = blue / 255f; 137 | 138 | // Render setpoint: 139 | float sx = Math.max(Math.abs(setpoint), 0); 140 | float sy = Transforms.bfRate(sx, crate, csuper, cexpo) / max; 141 | buffer.vertex(matrix, sx, 0, 0).color(r, g, b, 1f).endVertex(); 142 | buffer.vertex(matrix, sx, sy, 0).color(r, g, b, 1f).endVertex(); 143 | buffer.vertex(matrix, sx, sy, 0).color(r, g, b, 1f).endVertex(); 144 | buffer.vertex(matrix, 1, sy, 0).color(r, g, b, 1f).endVertex(); 145 | 146 | int n = 100; 147 | for (int i = 1; i < n; i++) { 148 | float pxprev = (i - 1f) / (n - 1f); 149 | float px = 1f * i / (n - 1f); 150 | 151 | float pyprev = Transforms.bfRate(pxprev, crate, csuper, cexpo) / max; 152 | float py = Transforms.bfRate(px, crate, csuper, cexpo) / max; 153 | 154 | buffer.vertex(matrix, pxprev, pyprev, 0).color(r, g, b, 1f).endVertex(); 155 | buffer.vertex(matrix, px, py, 0).color(r, g, b, 1f).endVertex(); 156 | } 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/gui/widget/SnappySlider.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.gui.widget; 2 | 3 | import com.jme3.math.FastMath; 4 | import net.minecraft.client.gui.widget.AbstractSlider; 5 | import net.minecraft.client.gui.widget.Widget; 6 | import net.minecraft.util.text.StringTextComponent; 7 | 8 | import java.text.DecimalFormat; 9 | import java.util.function.Consumer; 10 | import java.util.function.Supplier; 11 | 12 | 13 | // todo: use abstract slider 14 | public class SnappySlider extends AbstractSlider { 15 | private Consumer onClick; 16 | private Consumer setNewValue; 17 | private Supplier getMin; 18 | private Supplier getMax; 19 | 20 | /* 21 | * slider value is from 0 to 1 22 | * */ 23 | public SnappySlider( 24 | int xIn, 25 | int yIn, 26 | int width, 27 | int height, 28 | float lerpValue, 29 | Supplier getMin, 30 | Supplier getMax, 31 | Consumer setNewValue, 32 | Consumer onClick 33 | ) { 34 | super(xIn, yIn, width, height, new StringTextComponent(""), lerpValue); 35 | this.getMin = getMin; 36 | this.getMax = getMax; 37 | this.setNewValue = setNewValue; 38 | this.onClick = onClick; 39 | 40 | float min = this.getMin.get(); 41 | float max = this.getMax.get(); 42 | float value = lerpValue * max + (1f - lerpValue) * min; 43 | this.setText(value); 44 | } 45 | 46 | public void setValue(float value) { 47 | float min = this.getMin.get(); 48 | float max = this.getMax.get(); 49 | value = FastMath.clamp(value, min, max); 50 | float diff = value - min; 51 | float range = max - min; 52 | float ratio = diff / range; 53 | 54 | double original = this.value; 55 | this.value = ratio; 56 | if (original != ratio) { 57 | this.applyValue(); 58 | } 59 | this.updateMessage(); 60 | 61 | this.setText(value); 62 | } 63 | 64 | public void setText(float value) { 65 | DecimalFormat df = new DecimalFormat(); 66 | df.setMaximumFractionDigits(2); 67 | String str = df.format(value); 68 | // this.setMessage(new StringTextComponent(Integer.toString((int) FastMath.floor(value)))); 69 | this.setMessage(new StringTextComponent(str)); 70 | } 71 | 72 | 73 | /* 74 | * Looks like this function is called when a new value is set. 75 | * */ 76 | @Override 77 | protected void applyValue() { 78 | float lerpValue = (float) this.value; 79 | float min = this.getMin.get(); 80 | float max = this.getMax.get(); 81 | float value = lerpValue * max + (1f - lerpValue) * min; 82 | this.setNewValue.accept(value); 83 | } 84 | 85 | 86 | /* 87 | * I think this is when the button is clicked. 88 | * */ 89 | @Override 90 | public void onRelease(double mouseX, double mouseY) { 91 | super.onRelease(mouseX, mouseY); 92 | this.onClick.accept(this); 93 | } 94 | 95 | /* 96 | * Not sure what this does. OptionSlider uses it to set a value which looks like the label. And also has something to do with the narrator. 97 | * */ 98 | @Override 99 | protected void updateMessage() { 100 | 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/input/ControllerEvent.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.input; 2 | 3 | import com.gluecode.fpvdrone.Main; 4 | import net.minecraftforge.api.distmarker.Dist; 5 | import net.minecraftforge.api.distmarker.OnlyIn; 6 | import net.minecraftforge.client.event.ScreenEvent; 7 | import net.minecraftforge.event.TickEvent; 8 | import net.minecraftforge.eventbus.api.SubscribeEvent; 9 | import net.minecraftforge.fml.common.Mod; 10 | import org.lwjgl.glfw.GLFW; 11 | 12 | @OnlyIn(Dist.CLIENT) 13 | @Mod.EventBusSubscriber(value = Dist.CLIENT, modid = Main.MOD_ID) 14 | public class ControllerEvent { 15 | @SubscribeEvent 16 | public static void handleGuiEvent(ScreenEvent.InitScreenEvent.Pre event) { 17 | if (ControllerReader.controllerId != -1) { 18 | Main.LOGGER.debug("REFRESH BUFFERS"); 19 | ControllerReader.axisBuffer = GLFW.glfwGetJoystickAxes(ControllerReader.controllerId); 20 | ControllerReader.buttonBuffer = GLFW.glfwGetJoystickButtons( 21 | ControllerReader.controllerId); 22 | } 23 | } 24 | 25 | @SubscribeEvent 26 | public static void handleTickEvent(TickEvent.RenderTickEvent event) { 27 | if (event.phase == TickEvent.Phase.END) { 28 | ControllerReader.poll(); 29 | KeyManager.poll(); 30 | MouseManager.poll(); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/input/JoystickCallback.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.input; 2 | 3 | import org.lwjgl.glfw.GLFWJoystickCallbackI; 4 | 5 | public class JoystickCallback implements GLFWJoystickCallbackI { 6 | public void invoke(int jid, int event) { 7 | ControllerReader.joystickCallback(jid, event); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/input/KeyManager.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.input; 2 | 3 | import com.gluecode.fpvdrone.Main; 4 | import com.gluecode.fpvdrone.gui.GuiEvents; 5 | import net.minecraft.client.Options; 6 | import net.minecraft.client.Minecraft; 7 | import net.minecraft.client.KeyMapping; 8 | import net.minecraftforge.api.distmarker.Dist; 9 | import net.minecraftforge.api.distmarker.OnlyIn; 10 | import net.minecraftforge.client.event.InputEvent; 11 | import net.minecraftforge.eventbus.api.SubscribeEvent; 12 | import net.minecraftforge.client.ClientRegistry; 13 | import net.minecraftforge.fml.common.Mod; 14 | import org.lwjgl.glfw.GLFW; 15 | 16 | @OnlyIn(Dist.CLIENT) 17 | @Mod.EventBusSubscriber(value = Dist.CLIENT, modid = Main.MOD_ID) 18 | public class KeyManager { 19 | public static KeyMapping armKey = new KeyMapping( 20 | "key.fpvdrone.arm", 21 | GLFW.GLFW_KEY_I, 22 | "key.fpvdrone.category" 23 | ); 24 | 25 | public static KeyMapping menuKey = new KeyMapping( 26 | "key.fpvdrone.menu", 27 | GLFW.GLFW_KEY_O, 28 | "key.fpvdrone.category" 29 | ); 30 | 31 | public static void init() { 32 | ClientRegistry.registerKeyBinding(armKey); 33 | ClientRegistry.registerKeyBinding(menuKey); 34 | 35 | Options gameSettings = Minecraft.getInstance().options; 36 | 37 | KeyMapping[] nextKeyMappings = new KeyMapping[gameSettings.keyMappings.length]; 38 | for (int i = 0; i < gameSettings.keyMappings.length; i++) { 39 | if (gameSettings.keyMappings[i].equals(gameSettings.keyUp)) { 40 | nextKeyMappings[i] = new KeyMappingInterceptor(gameSettings.keyUp); 41 | gameSettings.keyUp = nextKeyMappings[i]; 42 | } else if (gameSettings.keyMappings[i].equals(gameSettings.keyLeft)) { 43 | nextKeyMappings[i] = new KeyMappingInterceptor(gameSettings.keyLeft); 44 | gameSettings.keyLeft = nextKeyMappings[i]; 45 | } else if (gameSettings.keyMappings[i].equals(gameSettings.keyDown)) { 46 | nextKeyMappings[i] = new KeyMappingInterceptor(gameSettings.keyDown); 47 | gameSettings.keyDown = nextKeyMappings[i]; 48 | } else if (gameSettings.keyMappings[i].equals(gameSettings.keyRight)) { 49 | nextKeyMappings[i] = new KeyMappingInterceptor(gameSettings.keyRight); 50 | gameSettings.keyRight = nextKeyMappings[i]; 51 | } else if (gameSettings.keyMappings[i].equals(gameSettings.keyJump)) { 52 | nextKeyMappings[i] = new KeyMappingInterceptor(gameSettings.keyJump); 53 | gameSettings.keyJump = nextKeyMappings[i]; 54 | } else { 55 | nextKeyMappings[i] = gameSettings.keyMappings[i]; 56 | } 57 | } 58 | gameSettings.keyMappings = nextKeyMappings; 59 | } 60 | 61 | public static void poll() { 62 | Options gameSettings = Minecraft.getInstance().options; 63 | if (ControllerReader.arm && 64 | ((KeyMappingInterceptor) gameSettings.keyLeft).isKeyReallyDown()) { 65 | ControllerReader.yaw += -0.5f; 66 | } 67 | if (ControllerReader.arm && 68 | ((KeyMappingInterceptor) gameSettings.keyRight).isKeyReallyDown()) { 69 | ControllerReader.yaw += 0.5f; 70 | } 71 | if (ControllerReader.arm && 72 | ((KeyMappingInterceptor) gameSettings.keyJump).isKeyReallyDown()) { 73 | ControllerReader.throttle = 1f; 74 | } else if (ControllerReader.arm && 75 | ((KeyMappingInterceptor) gameSettings.keyUp).isKeyReallyDown()) { 76 | ControllerReader.throttle = 0.5f; 77 | } 78 | if (ControllerReader.arm && 79 | ((KeyMappingInterceptor) gameSettings.keyDown).isKeyReallyDown()) { 80 | ControllerReader.throttle += -1f; 81 | } 82 | } 83 | 84 | @SubscribeEvent 85 | public static void onKeyboardEvent(InputEvent.KeyInputEvent event) { 86 | boolean isGuiOpen = Minecraft.getInstance().screen != null; 87 | if (isGuiOpen) return; 88 | 89 | int action = event.getAction(); 90 | int code = event.getKey(); 91 | 92 | if (code == armKey.getKey().getValue()) { 93 | ControllerReader.rawArm = action == GLFW.GLFW_PRESS; 94 | ControllerReader.handleArmToggle(); 95 | } else if (code == menuKey.getKey().getValue()) { 96 | Minecraft minecraft = Minecraft.getInstance(); 97 | if (minecraft.screen == null) { 98 | minecraft.pauseGame(false); 99 | GuiEvents.openFpvSettingsScreen(null); 100 | } 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/input/KeyMappingInterceptor.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.input; 2 | 3 | import com.gluecode.fpvdrone.Main; 4 | import net.minecraft.client.KeyMapping; 5 | 6 | public class KeyMappingInterceptor extends KeyMapping { 7 | protected KeyMapping interceptedKeyMapping; 8 | private boolean interceptionActive; 9 | private int interceptedPressTime; 10 | 11 | /** 12 | * Create an Interceptor based on an existing binding. 13 | * The initial interception mode is OFF. 14 | * If existingKeyMapping is already a KeyMappingInterceptor, a reinitialised copy will be created but no further effect. 15 | * 16 | * @param existingKeyMapping - the binding that will be intercepted. 17 | */ 18 | public KeyMappingInterceptor(KeyMapping existingKeyMapping) { 19 | super( 20 | existingKeyMapping.getName(), 21 | existingKeyMapping.getKey().getValue(), 22 | existingKeyMapping.getCategory() 23 | ); 24 | 25 | this.interceptionActive = false; 26 | this.isDown = false; 27 | this.clickCount = 0; 28 | this.interceptedPressTime = 0; 29 | 30 | if (existingKeyMapping instanceof KeyMappingInterceptor) { 31 | interceptedKeyMapping = ((KeyMappingInterceptor) existingKeyMapping) 32 | .getOriginalKeyMapping(); 33 | } else { 34 | interceptedKeyMapping = existingKeyMapping; 35 | } 36 | 37 | KeyMapping.resetMapping(); 38 | } 39 | 40 | public void setInterceptionActive(boolean newMode) { 41 | if (newMode && !interceptionActive) { 42 | this.interceptedPressTime = 0; 43 | } 44 | interceptionActive = newMode; 45 | } 46 | 47 | public boolean isDown() { 48 | if (interceptionActive) return false; 49 | return super.isDown(); 50 | // Main.LOGGER.info("a: " + interceptedKeyMapping.isDown); 51 | // copyKeyCodeToOriginal(); 52 | // return interceptedKeyMapping.isDown; 53 | } 54 | 55 | public boolean isKeyReallyDown() { 56 | return super.isDown(); 57 | } 58 | 59 | /** 60 | * @return returns false if interception isn't active. Otherwise, retrieves one of the clicks (true) or false if no clicks left 61 | */ 62 | public boolean retrieveClick() { 63 | copyKeyCodeToOriginal(); 64 | if (interceptionActive) { 65 | copyClickInfoFromOriginal(); 66 | 67 | if (this.interceptedPressTime == 0) { 68 | return false; 69 | } else { 70 | --this.interceptedPressTime; 71 | return true; 72 | } 73 | } else { 74 | return false; 75 | } 76 | } 77 | 78 | /** 79 | * A better name for this method would be retrieveClick. 80 | * If interception is on, resets .isDown and .clickCount to zero. 81 | * Otherwise, copies these from the intercepted KeyMapping. 82 | * 83 | * @return If interception is on, this will return false; Otherwise, it will pass on any clicks in the intercepted KeyMapping 84 | */ 85 | @Override 86 | public boolean consumeClick() { 87 | copyKeyCodeToOriginal(); 88 | copyClickInfoFromOriginal(); 89 | 90 | Main.LOGGER.info("interceptionActive: " + interceptionActive); 91 | 92 | if (interceptionActive) { 93 | this.clickCount = 0; 94 | this.isDown = false; 95 | return false; 96 | } else { 97 | if (this.clickCount == 0) { 98 | return false; 99 | } else { 100 | --this.clickCount; 101 | return true; 102 | } 103 | } 104 | } 105 | 106 | public KeyMapping getOriginalKeyMapping() { 107 | return interceptedKeyMapping; 108 | } 109 | 110 | protected void copyClickInfoFromOriginal() { 111 | this.clickCount += interceptedKeyMapping.clickCount; 112 | this.interceptedPressTime += interceptedKeyMapping.clickCount; 113 | interceptedKeyMapping.clickCount = 0; 114 | this.isDown = interceptedKeyMapping.isDown; 115 | } 116 | 117 | protected void copyKeyCodeToOriginal() { 118 | // only copy if necessary 119 | if (this.getKey() != interceptedKeyMapping.getKey()) { 120 | this.setKey(interceptedKeyMapping.getKey()); 121 | resetMapping(); 122 | } 123 | } 124 | 125 | } 126 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/input/MouseManager.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.input; 2 | 3 | import net.minecraft.client.Minecraft; 4 | import net.minecraft.client.MouseHandler; 5 | 6 | public class MouseManager { 7 | public static float mouseX = 0; 8 | public static float mouseY = 0; 9 | public static float mouseDiffX = 0; 10 | public static float mouseDiffY = 0; 11 | 12 | private static boolean guiOpen = false; 13 | 14 | public static void poll() { 15 | boolean nextGuiOpen = Minecraft.getInstance().screen != null; 16 | boolean toggleGui = nextGuiOpen != guiOpen; 17 | guiOpen = nextGuiOpen; 18 | 19 | if (!toggleGui) { 20 | MouseHandler MouseHandler = Minecraft.getInstance().mouseHandler; 21 | double nextMouseX = MouseHandler.xpos(); 22 | double nextMouseY = MouseHandler.ypos(); 23 | mouseDiffX = (float) nextMouseX - mouseX; 24 | mouseDiffY = (float) nextMouseY - mouseY; 25 | mouseX = (float) nextMouseX; 26 | mouseY = (float) nextMouseY; 27 | } else { 28 | MouseHandler MouseHandler = Minecraft.getInstance().mouseHandler; 29 | double nextMouseX = MouseHandler.xpos(); 30 | double nextMouseY = MouseHandler.ypos(); 31 | mouseDiffX = 0; 32 | mouseDiffY = 0; 33 | mouseX = (float) nextMouseX; 34 | mouseY = (float) nextMouseY; 35 | } 36 | } 37 | 38 | public static float yposDiff() { 39 | if (Minecraft.getInstance().screen != null) { 40 | return 0; 41 | } 42 | // camera angle fix is used to make yaw and roll feel equal no matter the camera angle setting. 43 | float sensitivity = (float) Minecraft.getInstance().options.sensitivity; 44 | // float cameraAngleFix = FastMath.cos(cameraAngle * rads); 45 | return mouseDiffX * sensitivity * 0.007f; 46 | } 47 | 48 | public static float getMousePitchDiff() { 49 | if (Minecraft.getInstance().screen != null) { 50 | return 0; 51 | } 52 | float sensitivity = (float) Minecraft.getInstance().options.sensitivity; 53 | return (ControllerConfig.getInvertPitch() ? -1 : 1) * mouseDiffY * sensitivity * .007f; 54 | } 55 | 56 | public static float getMouseRollDiff() { 57 | if (Minecraft.getInstance().screen != null) { 58 | return 0; 59 | } 60 | float sensitivity = (float) Minecraft.getInstance().options.sensitivity; 61 | // float cameraAngleFix = FastMath.sin(cameraAngle * rads); 62 | return mouseDiffX * sensitivity * 0.007f; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/network/Network.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.network; 2 | 3 | import com.gluecode.fpvdrone.Main; 4 | import com.gluecode.fpvdrone.entity.DroneBuild; 5 | import com.gluecode.fpvdrone.input.ControllerReader; 6 | import com.gluecode.fpvdrone.network.packet.DroneBuildPacket; 7 | import com.gluecode.fpvdrone.network.packet.DroneStatePacket; 8 | import com.gluecode.fpvdrone.network.packet.PacketHandler; 9 | import com.gluecode.fpvdrone.network.packet.SetArmPacket; 10 | import com.jme3.math.Quaternion; 11 | import net.minecraft.client.Minecraft; 12 | import net.minecraft.world.entity.player.Player; 13 | import net.minecraftforge.api.distmarker.Dist; 14 | import net.minecraftforge.api.distmarker.OnlyIn; 15 | 16 | import java.util.UUID; 17 | 18 | @OnlyIn(Dist.CLIENT) 19 | public class Network { 20 | public static void updateArmState(Player player) { 21 | // Poses are automatically synced from server->client, so we don't have to worry about other players on the client 22 | if (player == Minecraft.getInstance().player) { 23 | updateClientArmState(); 24 | } 25 | } 26 | 27 | public static void updateDroneState( 28 | Player player, 29 | Quaternion rot, 30 | float motorVel1, 31 | float motorVel2, 32 | float motorVel3, 33 | float motorVel4 34 | ) { 35 | UUID uuid = player.getUUID(); 36 | PacketHandler.sendToServer(new DroneStatePacket( 37 | uuid, 38 | rot.getX(), 39 | rot.getY(), 40 | rot.getZ(), 41 | rot.getW(), 42 | motorVel1, 43 | motorVel2, 44 | motorVel3, 45 | motorVel4, 46 | System.currentTimeMillis() 47 | )); 48 | } 49 | 50 | private static void updateClientArmState() { 51 | Player player = Minecraft.getInstance().player; 52 | if (player != null) { 53 | UUID uuid = player.getUUID(); 54 | if (ControllerReader.getArm() != Main.entityArmStates.getOrDefault( 55 | uuid, 56 | false 57 | )) { 58 | PacketHandler.sendToServer(new SetArmPacket( 59 | ControllerReader.getArm(), 60 | uuid 61 | )); 62 | 63 | if (ControllerReader.getArm()) { 64 | // When arming, the current build is re-sent to the server, 65 | // which will forward it to all users, 66 | // which will cause all users to rebuild the DroneRenderer. 67 | PacketHandler.sendToServer( 68 | new DroneBuildPacket(uuid, DroneBuild.getSelf()) 69 | ); 70 | } 71 | } 72 | Main.entityArmStates.put(uuid, ControllerReader.getArm()); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/network/VersionNotifier.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.network; 2 | 3 | import com.gluecode.fpvdrone.Main; 4 | import net.minecraftforge.api.distmarker.Dist; 5 | import net.minecraftforge.api.distmarker.OnlyIn; 6 | import net.minecraftforge.fml.ModLoadingContext; 7 | import net.minecraftforge.fml.VersionChecker; 8 | import net.minecraftforge.fml.common.Mod; 9 | import net.minecraftforge.forgespi.language.IModInfo; 10 | import org.json.simple.JSONObject; 11 | 12 | @OnlyIn(Dist.CLIENT) 13 | @Mod.EventBusSubscriber(value = Dist.CLIENT, modid = Main.MOD_ID) 14 | public class VersionNotifier { 15 | public static final String VERSION_URL = "https://minecraftfpv-assets.s3.us-east-2.amazonaws.com/version.json"; 16 | public static String versionRaw = null; 17 | public static JSONObject versionObject = null; 18 | public static String version = ""; 19 | public static VersionChecker.CheckResult result = null; 20 | public static IModInfo info; 21 | 22 | public static boolean versionNotificationSent = false; 23 | 24 | public static void init() { 25 | info = ModLoadingContext.get().getActiveContainer().getModInfo(); 26 | result = VersionChecker.getResult(info); 27 | 28 | // AsyncHttpClient client = Dsl.asyncHttpClient(); 29 | // client.prepareGet(VERSION_URL).execute(new AsyncCompletionHandler() { 30 | // 31 | // @Override 32 | // public State onBodyPartReceived(HttpResponseBodyPart bodyPart) throws Exception { 33 | // versionRaw = new String(bodyPart.getBodyPartBytes(), StandardCharsets.UTF_8); 34 | // versionObject = (JSONObject) (new JSONParser().parse(versionRaw)); 35 | // 36 | // Pattern pattern = Pattern.compile("1.15.2-recommended\": \"([^\"]*)\""); 37 | // Matcher matcher = pattern.matcher(versionRaw); 38 | // matcher.find(); 39 | // version = "1.15.2-" + matcher.group(1); 40 | // 41 | // if (version.equalsIgnoreCase(info.getVersion().toString())) { 42 | // versionNotificationSent = true; 43 | // } 44 | // 45 | // return State.CONTINUE; 46 | // } 47 | // 48 | // @Override 49 | // public String onCompleted(Response response) throws Exception { 50 | // return versionRaw; 51 | // } 52 | // }); 53 | } 54 | 55 | // @SubscribeEvent 56 | // public static void handleVersionNotification(TickEvent.PlayerTickEvent event) { 57 | // if (event.phase == TickEvent.Phase.END) { 58 | // if (!versionNotificationSent) { 59 | // versionNotificationSent = true; 60 | // event.player.sendMessage(new StringTextComponent("A newer version of the fpv-drone mod is on " + 61 | // "curseforge.")); 62 | // } 63 | // } 64 | // } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/network/packet/DroneStatePacket.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.network.packet; 2 | 3 | import com.gluecode.fpvdrone.Main; 4 | import com.gluecode.fpvdrone.network.DroneState; 5 | import net.minecraft.client.Minecraft; 6 | import net.minecraft.entity.player.PlayerEntity; 7 | import net.minecraft.network.FriendlyByteBuf; 8 | import net.minecraftforge.api.distmarker.Dist; 9 | import net.minecraftforge.api.distmarker.OnlyIn; 10 | import net.minecraftforge.fml.DistExecutor; 11 | import net.minecraftforge.network.NetworkEvent; 12 | 13 | import java.util.UUID; 14 | import java.util.function.Supplier; 15 | 16 | public class DroneStatePacket { 17 | private final long uuidMostSig; 18 | private final long uuidLeastSig; 19 | 20 | // current quaternion rotation: 21 | private final float x; 22 | private final float y; 23 | private final float z; 24 | private final float w; 25 | 26 | // motor velocities: 27 | private final float motorVel1; 28 | private final float motorVel2; 29 | private final float motorVel3; 30 | private final float motorVel4; 31 | 32 | // current stick inputs: 33 | // private final float throttle; 34 | // private final float yaw; 35 | // private final float pitch; 36 | // private final float roll; 37 | 38 | private final long timeSent; 39 | // world euler angles. 40 | // private final float droneYaw; 41 | // private final float dronePitch; 42 | // private final float droneRoll; 43 | // private final float cameraYaw; 44 | // private final float cameraPitch; 45 | // private final float cameraRoll; 46 | 47 | 48 | public DroneStatePacket( 49 | UUID uuid, 50 | float x, 51 | float y, 52 | float z, 53 | float w, 54 | float motorVel1, 55 | float motorVel2, 56 | float motorVel3, 57 | float motorVel4, 58 | long timeSent 59 | ) { 60 | this.uuidMostSig = uuid.getMostSignificantBits(); 61 | this.uuidLeastSig = uuid.getLeastSignificantBits(); 62 | this.x = x; 63 | this.y = y; 64 | this.z = z; 65 | this.w = w; 66 | this.motorVel1 = motorVel1; 67 | this.motorVel2 = motorVel2; 68 | this.motorVel3 = motorVel3; 69 | this.motorVel4 = motorVel4; 70 | this.timeSent = timeSent; 71 | } 72 | 73 | public static DroneStatePacket decode(FriendlyByteBuf buffer) { 74 | return new DroneStatePacket( 75 | new UUID( 76 | buffer.readLong(), 77 | buffer.readLong() 78 | ), 79 | buffer.readFloat(), 80 | buffer.readFloat(), 81 | buffer.readFloat(), 82 | buffer.readFloat(), 83 | buffer.readFloat(), 84 | buffer.readFloat(), 85 | buffer.readFloat(), 86 | buffer.readFloat(), 87 | buffer.readLong() 88 | ); 89 | } 90 | 91 | public static void encode(DroneStatePacket msg, FriendlyByteBuf buffer) { 92 | buffer.writeLong(msg.uuidMostSig); 93 | buffer.writeLong(msg.uuidLeastSig); 94 | buffer.writeFloat(msg.x); 95 | buffer.writeFloat(msg.y); 96 | buffer.writeFloat(msg.z); 97 | buffer.writeFloat(msg.w); 98 | buffer.writeFloat(msg.motorVel1); 99 | buffer.writeFloat(msg.motorVel2); 100 | buffer.writeFloat(msg.motorVel3); 101 | buffer.writeFloat(msg.motorVel4); 102 | buffer.writeLong(msg.timeSent); 103 | } 104 | 105 | public static void handle( 106 | DroneStatePacket msg, 107 | Supplier contextSupplier 108 | ) { 109 | contextSupplier.get().enqueueWork(() -> { 110 | DistExecutor.runWhenOn( 111 | Dist.CLIENT, 112 | () -> () -> DroneStatePacket.handleClient( 113 | msg, 114 | contextSupplier 115 | ) 116 | ); 117 | DistExecutor.runWhenOn( 118 | Dist.DEDICATED_SERVER, 119 | () -> () -> DroneStatePacket.handleServer( 120 | msg, 121 | contextSupplier 122 | ) 123 | ); 124 | contextSupplier.get().setPacketHandled(true); 125 | }); 126 | } 127 | 128 | @OnlyIn(Dist.CLIENT) 129 | private static void handleClient( 130 | DroneStatePacket msg, 131 | Supplier contextSupplier 132 | ) { 133 | UUID uuid = new UUID(msg.uuidMostSig, msg.uuidLeastSig); 134 | PlayerEntity player = Minecraft.getInstance().player; 135 | if (player == null || !uuid.equals(player.getUUID())) { 136 | // Do not set self because when self sends the packet, it will echo back. 137 | // The echoed packet is out-of-date compared to self. 138 | DroneState droneOrientation = new DroneState( 139 | msg.x, 140 | msg.y, 141 | msg.z, 142 | msg.w, 143 | msg.motorVel1, 144 | msg.motorVel2, 145 | msg.motorVel3, 146 | msg.motorVel4, 147 | msg.timeSent 148 | ); 149 | DroneState.update( 150 | uuid, 151 | droneOrientation 152 | ); 153 | } 154 | 155 | boolean isIntegratedServer = !Main.isClientSide(); 156 | if (isIntegratedServer) { 157 | PacketHandler.sendToAll(msg); 158 | } 159 | } 160 | 161 | @OnlyIn(Dist.DEDICATED_SERVER) 162 | private static void handleServer( 163 | DroneStatePacket msg, 164 | Supplier contextSupplier 165 | ) { 166 | PacketHandler.sendToAll(msg); 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/network/packet/GateIndexPacket.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.network.packet; 2 | 3 | import com.gluecode.fpvdrone.race.RaceClient; 4 | import net.minecraft.network.FriendlyByteBuf; 5 | import net.minecraftforge.api.distmarker.Dist; 6 | import net.minecraftforge.api.distmarker.OnlyIn; 7 | import net.minecraftforge.fml.DistExecutor; 8 | import net.minecraftforge.network.NetworkEvent; 9 | 10 | import java.util.UUID; 11 | import java.util.function.Supplier; 12 | 13 | public class GateIndexPacket { 14 | private int gateIndex; 15 | private UUID trackId; 16 | private UUID userId; 17 | 18 | public GateIndexPacket(int gateIndex, UUID trackId, UUID userId) { 19 | this.gateIndex = gateIndex; 20 | this.trackId = trackId; 21 | this.userId = userId; 22 | } 23 | 24 | public static GateIndexPacket decode(FriendlyByteBuf buffer) { 25 | return new GateIndexPacket( 26 | buffer.readInt(), 27 | new UUID(buffer.readLong(), buffer.readLong()), 28 | new UUID(buffer.readLong(), buffer.readLong()) 29 | ); 30 | } 31 | 32 | public static void encode(GateIndexPacket msg, FriendlyByteBuf buffer) { 33 | buffer.writeInt(msg.gateIndex); 34 | buffer.writeLong(msg.trackId.getMostSignificantBits()); 35 | buffer.writeLong(msg.trackId.getLeastSignificantBits()); 36 | buffer.writeLong(msg.userId.getMostSignificantBits()); 37 | buffer.writeLong(msg.userId.getLeastSignificantBits()); 38 | } 39 | 40 | public static void handle( 41 | GateIndexPacket msg, 42 | Supplier contextSupplier 43 | ) { 44 | contextSupplier.get().enqueueWork(() -> { 45 | DistExecutor.runWhenOn( 46 | Dist.CLIENT, 47 | () -> () -> GateIndexPacket.handleClient(msg, contextSupplier) 48 | ); 49 | contextSupplier.get().setPacketHandled(true); 50 | }); 51 | } 52 | 53 | @OnlyIn(Dist.CLIENT) 54 | private static void handleClient( 55 | GateIndexPacket msg, 56 | Supplier contextSupplier 57 | ) { 58 | RaceClient.handleGateIndexPacket( 59 | msg.gateIndex, 60 | msg.trackId, 61 | msg.userId 62 | ); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/network/packet/LapBestPacket.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.network.packet; 2 | 3 | import com.gluecode.fpvdrone.race.RaceClient; 4 | import net.minecraft.network.FriendlyByteBuf; 5 | import net.minecraftforge.api.distmarker.Dist; 6 | import net.minecraftforge.api.distmarker.OnlyIn; 7 | import net.minecraftforge.fml.DistExecutor; 8 | import net.minecraftforge.network.NetworkEvent; 9 | 10 | import java.util.UUID; 11 | import java.util.function.Supplier; 12 | 13 | public class LapBestPacket { 14 | private int millis; 15 | private UUID trackId; 16 | private UUID userId; 17 | 18 | public LapBestPacket(int millis, UUID trackId, UUID userId) { 19 | this.millis = millis; 20 | this.trackId = trackId; 21 | this.userId = userId; 22 | } 23 | 24 | public static LapBestPacket decode(FriendlyByteBuf buffer) { 25 | return new LapBestPacket( 26 | buffer.readInt(), 27 | new UUID(buffer.readLong(), buffer.readLong()), 28 | new UUID(buffer.readLong(), buffer.readLong()) 29 | ); 30 | } 31 | 32 | public static void encode(LapBestPacket msg, FriendlyByteBuf buffer) { 33 | buffer.writeInt(msg.millis); 34 | buffer.writeLong(msg.trackId.getMostSignificantBits()); 35 | buffer.writeLong(msg.trackId.getLeastSignificantBits()); 36 | buffer.writeLong(msg.userId.getMostSignificantBits()); 37 | buffer.writeLong(msg.userId.getLeastSignificantBits()); 38 | } 39 | 40 | public static void handle( 41 | LapBestPacket msg, 42 | Supplier contextSupplier 43 | ) { 44 | contextSupplier.get().enqueueWork(() -> { 45 | DistExecutor.runWhenOn( 46 | Dist.CLIENT, 47 | () -> () -> LapBestPacket.handleClient(msg, contextSupplier) 48 | ); 49 | contextSupplier.get().setPacketHandled(true); 50 | }); 51 | } 52 | 53 | @OnlyIn(Dist.CLIENT) 54 | private static void handleClient( 55 | LapBestPacket msg, 56 | Supplier contextSupplier 57 | ) { 58 | RaceClient.handleLapBestPacket(msg.millis, msg.trackId, msg.userId); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/network/packet/LapStartPacket.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.network.packet; 2 | 3 | import com.gluecode.fpvdrone.race.RaceClient; 4 | import net.minecraft.network.FriendlyByteBuf; 5 | import net.minecraftforge.api.distmarker.Dist; 6 | import net.minecraftforge.api.distmarker.OnlyIn; 7 | import net.minecraftforge.fml.DistExecutor; 8 | import net.minecraftforge.network.NetworkEvent; 9 | 10 | import java.util.UUID; 11 | import java.util.function.Supplier; 12 | 13 | public class LapStartPacket { 14 | private long startTimeMillis; 15 | private long uuidMostSig; 16 | private long uuidLeastSig; 17 | 18 | public LapStartPacket(long startTimeMillis, UUID uuid) { 19 | this.startTimeMillis = startTimeMillis; 20 | this.uuidMostSig = uuid.getMostSignificantBits(); 21 | this.uuidLeastSig = uuid.getLeastSignificantBits(); 22 | } 23 | 24 | public static LapStartPacket decode(FriendlyByteBuf buffer) { 25 | return new LapStartPacket( 26 | buffer.readLong(), 27 | new UUID(buffer.readLong(), buffer.readLong()) 28 | ); 29 | } 30 | 31 | public static void encode(LapStartPacket msg, FriendlyByteBuf buffer) { 32 | buffer.writeLong(msg.startTimeMillis); 33 | buffer.writeLong(msg.uuidMostSig); 34 | buffer.writeLong(msg.uuidLeastSig); 35 | } 36 | 37 | public static void handle( 38 | LapStartPacket msg, 39 | Supplier contextSupplier 40 | ) { 41 | contextSupplier.get().enqueueWork(() -> { 42 | DistExecutor.runWhenOn( 43 | Dist.CLIENT, 44 | () -> () -> LapStartPacket.handleClient(msg, contextSupplier) 45 | ); 46 | contextSupplier.get().setPacketHandled(true); 47 | }); 48 | } 49 | 50 | @OnlyIn(Dist.CLIENT) 51 | private static void handleClient( 52 | LapStartPacket msg, 53 | Supplier contextSupplier 54 | ) { 55 | RaceClient.handleLapStartPacket( 56 | msg.startTimeMillis, 57 | new UUID(msg.uuidMostSig, msg.uuidLeastSig) 58 | ); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/network/packet/PacketHandler.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.network.packet; 2 | 3 | import com.gluecode.fpvdrone.Main; 4 | import com.gluecode.fpvdrone.race.SerialRaceGate; 5 | import com.gluecode.fpvdrone.race.SerialRaceTrack; 6 | //import net.minecraft.entity.player.ServerPlayerEntity; 7 | import net.minecraft.resources.ResourceLocation; 8 | import net.minecraftforge.common.util.FakePlayer; 9 | import net.minecraftforge.network.NetworkDirection; 10 | import net.minecraftforge.network.NetworkRegistry; 11 | import net.minecraftforge.network.PacketDistributor; 12 | import net.minecraftforge.network.simple.SimpleChannel; 13 | 14 | public class PacketHandler { 15 | private static final String PROTOCOL_VERSION = Integer.toString(4); 16 | private static final SimpleChannel HANDLER = NetworkRegistry.ChannelBuilder 17 | .named(new ResourceLocation(Main.MOD_ID, "main_channel")) 18 | .clientAcceptedVersions((ver) -> true) 19 | .serverAcceptedVersions((ver) -> true) 20 | .networkProtocolVersion(() -> PROTOCOL_VERSION) 21 | .simpleChannel(); 22 | 23 | private static int disc = 0; 24 | 25 | public static void register() { 26 | HANDLER.registerMessage( 27 | disc++, 28 | LapStartPacket.class, 29 | LapStartPacket::encode, 30 | LapStartPacket::decode, 31 | LapStartPacket::handle 32 | ); 33 | HANDLER.registerMessage( 34 | disc++, 35 | LapBestPacket.class, 36 | LapBestPacket::encode, 37 | LapBestPacket::decode, 38 | LapBestPacket::handle 39 | ); 40 | HANDLER.registerMessage( 41 | disc++, 42 | GateIndexPacket.class, 43 | GateIndexPacket::encode, 44 | GateIndexPacket::decode, 45 | GateIndexPacket::handle 46 | ); 47 | HANDLER.registerMessage( 48 | disc++, 49 | SerialRaceTrack.class, 50 | SerialRaceTrack::encode, 51 | SerialRaceTrack::decode, 52 | SerialRaceTrack::handle 53 | ); 54 | HANDLER.registerMessage( 55 | disc++, 56 | SerialRaceGate.class, 57 | SerialRaceGate::encode, 58 | SerialRaceGate::decode, 59 | SerialRaceGate::handle 60 | ); 61 | HANDLER.registerMessage( 62 | disc++, 63 | DroneBuildPacket.class, 64 | DroneBuildPacket::encode, 65 | DroneBuildPacket::decode, 66 | DroneBuildPacket::handle 67 | ); 68 | HANDLER.registerMessage( 69 | disc++, 70 | SetArmPacket.class, 71 | SetArmPacket::encode, 72 | SetArmPacket::decode, 73 | SetArmPacket::handle 74 | ); 75 | HANDLER.registerMessage( 76 | disc++, 77 | SetRaceModePacket.class, 78 | SetRaceModePacket::encode, 79 | SetRaceModePacket::decode, 80 | SetRaceModePacket::handle 81 | ); 82 | HANDLER.registerMessage( 83 | disc++, 84 | SetBuildModePacket.class, 85 | SetBuildModePacket::encode, 86 | SetBuildModePacket::decode, 87 | SetBuildModePacket::handle 88 | ); 89 | HANDLER.registerMessage( 90 | disc++, 91 | DroneStatePacket.class, 92 | DroneStatePacket::encode, 93 | DroneStatePacket::decode, 94 | DroneStatePacket::handle 95 | ); 96 | } 97 | 98 | /** 99 | * Sends a packet to the server.
100 | * Must be called Client side. 101 | */ 102 | public static void sendToServer(Object msg) { 103 | HANDLER.sendToServer(msg); 104 | } 105 | 106 | public static void sendToAll(Object msg) { 107 | HANDLER.send(PacketDistributor.ALL.noArg(), msg); 108 | } 109 | 110 | /** 111 | * Send a packet to a specific player.
112 | * Must be called Server side. 113 | */ 114 | // public static void sendTo(Object msg, ServerPlayerEntity player) { 115 | // if (!(player instanceof FakePlayer)) { 116 | // HANDLER.sendTo( 117 | // msg, 118 | // player.connection.connection, 119 | // NetworkDirection.PLAY_TO_CLIENT 120 | // ); 121 | // } 122 | // } 123 | } 124 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/network/packet/SetArmPacket.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.network.packet; 2 | 3 | import com.gluecode.fpvdrone.Main; 4 | import net.minecraft.client.Minecraft; 5 | import net.minecraft.entity.player.PlayerEntity; 6 | import net.minecraft.network.FriendlyByteBuf; 7 | import net.minecraftforge.api.distmarker.Dist; 8 | import net.minecraftforge.api.distmarker.OnlyIn; 9 | import net.minecraftforge.fml.DistExecutor; 10 | import net.minecraftforge.network.NetworkEvent; 11 | 12 | import java.util.UUID; 13 | import java.util.function.Supplier; 14 | 15 | public class SetArmPacket { 16 | private final boolean armed; 17 | private final long uuidMostSig; 18 | private final long uuidLeastSig; 19 | 20 | public SetArmPacket(boolean isArmed, UUID uuid) { 21 | this.armed = isArmed; 22 | this.uuidMostSig = uuid.getMostSignificantBits(); 23 | this.uuidLeastSig = uuid.getLeastSignificantBits(); 24 | } 25 | 26 | public static SetArmPacket decode(FriendlyByteBuf buffer) { 27 | return new SetArmPacket( 28 | buffer.readBoolean(), 29 | new UUID(buffer.readLong(), buffer.readLong()) 30 | ); 31 | } 32 | 33 | public static void encode(SetArmPacket msg, FriendlyByteBuf buffer) { 34 | buffer.writeBoolean(msg.armed); 35 | buffer.writeLong(msg.uuidMostSig); 36 | buffer.writeLong(msg.uuidLeastSig); 37 | } 38 | 39 | public static void handle( 40 | SetArmPacket msg, 41 | Supplier contextSupplier 42 | ) { 43 | // Note: 44 | // @OnlyIn(Dist.DEDICATED_SERVER) will never run on clients 45 | // Marked code will be removed from client-side builds. 46 | 47 | // If you need something to run on the integrated server, which is included in the client-side build, 48 | // then you need to use 49 | // `boolean isIntegratedServer = !isRemote` 50 | contextSupplier.get().enqueueWork(() -> { 51 | DistExecutor.runWhenOn( 52 | Dist.CLIENT, 53 | () -> () -> SetArmPacket.handleClient(msg, contextSupplier) 54 | ); 55 | DistExecutor.runWhenOn( 56 | Dist.DEDICATED_SERVER, 57 | () -> () -> SetArmPacket.handleServer(msg, contextSupplier) 58 | ); 59 | contextSupplier.get().setPacketHandled(true); 60 | }); 61 | } 62 | 63 | @OnlyIn(Dist.CLIENT) 64 | private static void handleClient( 65 | SetArmPacket msg, 66 | Supplier contextSupplier 67 | ) { 68 | UUID uuid = new UUID(msg.uuidMostSig, msg.uuidLeastSig); 69 | PlayerEntity player = Minecraft.getInstance().player; 70 | if (player == null || !uuid.equals(player.getUUID())) { 71 | // Do not set self because when self sends the packet, it will echo back. 72 | // The echoed packet is out-of-date compared to self. 73 | Main.entityArmStates.put(uuid, msg.armed); 74 | if (!msg.armed) { 75 | Main.droneRenderers.remove(uuid); 76 | } 77 | } 78 | 79 | boolean isIntegratedServer = !Main.isClientSide(); 80 | if (isIntegratedServer) { 81 | PacketHandler.sendToAll(msg); 82 | } 83 | } 84 | 85 | @OnlyIn(Dist.DEDICATED_SERVER) 86 | private static void handleServer( 87 | SetArmPacket msg, 88 | Supplier contextSupplier 89 | ) { 90 | UUID uuid = new UUID(msg.uuidMostSig, msg.uuidLeastSig); 91 | Main.entityArmStates.put(uuid, msg.armed); 92 | if (!msg.armed) { 93 | Main.droneRenderers.remove(uuid); 94 | } 95 | 96 | PacketHandler.sendToAll(msg); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/network/packet/SetBuildModePacket.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.network.packet; 2 | 3 | import com.gluecode.fpvdrone.race.RaceClient; 4 | import net.minecraft.network.FriendlyByteBuf; 5 | import net.minecraftforge.api.distmarker.Dist; 6 | import net.minecraftforge.api.distmarker.OnlyIn; 7 | import net.minecraftforge.fml.DistExecutor; 8 | import net.minecraftforge.network.NetworkEvent; 9 | 10 | import java.util.function.Supplier; 11 | 12 | public class SetBuildModePacket { 13 | private final boolean value; 14 | 15 | public SetBuildModePacket(boolean value) { 16 | this.value = value; 17 | } 18 | 19 | public static SetBuildModePacket decode(FriendlyByteBuf buffer) { 20 | return new SetBuildModePacket(buffer.readBoolean()); 21 | } 22 | 23 | public static void encode(SetBuildModePacket msg, FriendlyByteBuf buffer) { 24 | buffer.writeBoolean(msg.value); 25 | } 26 | 27 | public static void handle( 28 | SetBuildModePacket msg, 29 | Supplier contextSupplier 30 | ) { 31 | contextSupplier.get().enqueueWork(() -> { 32 | DistExecutor.runWhenOn( 33 | Dist.CLIENT, 34 | () -> () -> SetBuildModePacket.handleClient( 35 | msg, 36 | contextSupplier 37 | ) 38 | ); 39 | contextSupplier.get().setPacketHandled(true); 40 | }); 41 | } 42 | 43 | @OnlyIn(Dist.CLIENT) 44 | private static void handleClient( 45 | SetBuildModePacket msg, 46 | Supplier contextSupplier 47 | ) { 48 | RaceClient.handleBuildModePacket(msg.value); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/network/packet/SetRaceModePacket.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.network.packet; 2 | 3 | import com.gluecode.fpvdrone.race.RaceClient; 4 | import net.minecraft.network.FriendlyByteBuf; 5 | import net.minecraftforge.api.distmarker.Dist; 6 | import net.minecraftforge.api.distmarker.OnlyIn; 7 | import net.minecraftforge.fml.DistExecutor; 8 | import net.minecraftforge.network.NetworkEvent; 9 | 10 | import java.util.UUID; 11 | import java.util.function.Supplier; 12 | 13 | public class SetRaceModePacket { 14 | private final boolean value; 15 | private final UUID trackId; 16 | private final UUID userId; 17 | 18 | public SetRaceModePacket(boolean value, UUID trackUUID, UUID userUUID) { 19 | this.value = value; 20 | this.trackId = trackUUID; 21 | this.userId = userUUID; 22 | } 23 | 24 | public static SetRaceModePacket decode(FriendlyByteBuf buffer) { 25 | return new SetRaceModePacket( 26 | buffer.readBoolean(), 27 | new UUID(buffer.readLong(), buffer.readLong()), 28 | new UUID(buffer.readLong(), buffer.readLong()) 29 | ); 30 | } 31 | 32 | public static void encode(SetRaceModePacket msg, FriendlyByteBuf buffer) { 33 | buffer.writeBoolean(msg.value); 34 | buffer.writeLong(msg.trackId.getMostSignificantBits()); 35 | buffer.writeLong(msg.trackId.getLeastSignificantBits()); 36 | buffer.writeLong(msg.userId.getMostSignificantBits()); 37 | buffer.writeLong(msg.userId.getLeastSignificantBits()); 38 | } 39 | 40 | public static void handle( 41 | SetRaceModePacket msg, 42 | Supplier contextSupplier 43 | ) { 44 | contextSupplier.get().enqueueWork(() -> { 45 | DistExecutor.runWhenOn( 46 | Dist.CLIENT, 47 | () -> () -> SetRaceModePacket.handleClient(msg, contextSupplier) 48 | ); 49 | contextSupplier.get().setPacketHandled(true); 50 | }); 51 | } 52 | 53 | @OnlyIn(Dist.CLIENT) 54 | private static void handleClient( 55 | SetRaceModePacket msg, 56 | Supplier contextSupplier 57 | ) { 58 | RaceClient.handleRaceModePacket(msg.value, msg.trackId, msg.userId); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/physics/CollisionResults.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.physics; 2 | 3 | import com.jme3.math.Vector3f; 4 | 5 | public class CollisionResults { 6 | public Vector3f velocity; 7 | public Vector3f displacement; 8 | 9 | public CollisionResults() { 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/physics/IPhysicsCore.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.physics; 2 | 3 | import com.jme3.math.Vector3f; 4 | 5 | public interface IPhysicsCore { 6 | void step(float dt); 7 | float[] getMotorVel(); 8 | float[] getMotorPos(); 9 | Vector3f getVelocity(); 10 | void setVelocity(Vector3f value); 11 | Vector3f getDroneLook(); 12 | void setDroneLook(Vector3f value); 13 | Vector3f getDroneUp(); 14 | void setDroneUp(Vector3f value); 15 | Vector3f getDroneLeft(); 16 | void setDroneLeft(Vector3f value); 17 | boolean isOverheat(); 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/physics/PhysicsCollision.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.physics; 2 | 3 | import com.jme3.math.FastMath; 4 | import com.jme3.math.Vector3f; 5 | 6 | import net.minecraft.world.phys.Vec3; 7 | 8 | public class PhysicsCollision { 9 | private static final float elastic = 0.2f; 10 | 11 | /* 12 | Given a desired displacement and an allowed displacement, figure out the final velocity of the object after colliding. 13 | * */ 14 | public static CollisionResults getCollisionResults( 15 | Vector3f displacement, 16 | Vec3 clipped, 17 | Vector3f preCollisionVelocity 18 | ) { 19 | Vector3f finalDisplacement = null; 20 | Vector3f finalVelocity = null; 21 | float desiredSpeed = preCollisionVelocity.length(); 22 | 23 | Vector3f clippedF = new Vector3f( 24 | (float) clipped.x, 25 | (float) clipped.y, 26 | (float) clipped.z 27 | ); 28 | Vector3f vClip = clippedF.subtract(displacement); 29 | float angle = displacement.normalize().angleBetween(vClip.mult(-1) 30 | .normalize()); 31 | Vector3f cProj = clippedF.project(vClip.normalize()); 32 | 33 | if (Float.isNaN(cProj.x)) { 34 | // This happens if clippedF and vClip are both 0 vectors. 35 | CollisionResults results = new CollisionResults(); 36 | results.displacement = clippedF; 37 | results.velocity = new Vector3f(0, 0, 0); 38 | return results; 39 | } 40 | 41 | Vector3f hClip = cProj.subtract(clippedF) 42 | .normalizeLocal() 43 | .mult(vClip.length() * FastMath.tan(angle)); 44 | Vector3f puncture = clippedF.add(hClip); 45 | Vector3f bounceFinal = clippedF.add(vClip); 46 | Vector3f bounceDirection = bounceFinal.subtract(puncture).normalize(); 47 | 48 | float glancing = FastMath.sin(angle); 49 | float outSpeed = desiredSpeed * 50 | (glancing * 0.65f + (1f - glancing) * elastic); 51 | if (outSpeed < 1f) { 52 | finalDisplacement = clippedF; 53 | finalVelocity = bounceDirection.mult(0); 54 | } else { 55 | finalDisplacement = bounceFinal; 56 | finalVelocity = bounceDirection.mult(outSpeed); 57 | } 58 | CollisionResults results = new CollisionResults(); 59 | results.displacement = finalDisplacement; 60 | results.velocity = finalVelocity; 61 | return results; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/physics/PhysicsConstants.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.physics; 2 | 3 | import com.jme3.math.FastMath; 4 | 5 | /* 6 | Unless otherwise stated, all constants here should be natural constants. 7 | 8 | Constants should be in SI units. 9 | 10 | If a constant isn't natural, then it should have a comment next to it 11 | explaining that it is tuned. 12 | * */ 13 | public class PhysicsConstants { 14 | public static final float rads = (float) (Math.PI / 180); 15 | public static final float degs = (float) (180 / Math.PI); 16 | 17 | public static final float toSIKv = 0.104719755f; 18 | public static final float inches = 39.3701f; // inches / meter 19 | 20 | // Densities: 21 | public static final float airDensity = 1.225F; // kg / m^3 22 | public static final float batteryDensity = 2726f; // kg / m^3 23 | public static final float polycarbonateDensity = 1220; // kg / m^3 24 | public static final float steelDensity = 8050; 25 | public static final float neodymiumDensity = 7000; 26 | public static final float motorSpecificHeatCapacity = (0.385f * 1000f + 0.466f * 1000f) / 2f; // J / (kg * K) average of copper and steel 27 | public static final float motorMassCoefficient = 5527; // kg / m^3 motor volume * coeff = motor mass 28 | public static final float carbonFiberDensity = 1550; 29 | public static final float aluminumDensity = 2700; 30 | public static final float stackDensity = 750; // Estimated since the electronic stack is mixture of materials. 31 | 32 | // Electricity: 33 | public static final float vac = FastMath.PI * 4 * FastMath.pow(10, -7); 34 | public static final float resistancePer1000Ft = 26f; 35 | public static final float tempCoeff = 0.00393f; 36 | public static final float refTemp = 293.15f; // 20 Celcius 37 | public static final float heatTransfer = 2000f; // tuned value. In reality, this changes depending on several factors, one of which is the air flow speed past the motor. 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/physics/PhysicsCoreLoader.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.physics; 2 | 3 | /* 4 | This file uses preprocessor directive using: 5 | https://github.com/manifold-systems/manifold/tree/master/manifold-deps-parent/manifold-preprocessor 6 | 7 | You will continue to see syntax errors unless you buy the $20 IntelliJ plugin, 8 | but you do not need to buy the plugin in order to compile the project. 9 | The project will still compile despite these preprocessor syntax errors. 10 | You can ignore this file. 11 | * */ 12 | 13 | public class PhysicsCoreLoader { 14 | public static void load() { 15 | //#if ADVANCED_PHYSICS 16 | // PhysicsState.setCore(new AdvancedPhysicsCore()); 17 | //#else 18 | PhysicsState.setCore(new DefaultPhysicsCore()); 19 | //#endif 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/physics/PhysicsState.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.physics; 2 | 3 | import com.gluecode.fpvdrone.Main; 4 | import com.gluecode.fpvdrone.entity.DroneBuild; 5 | import com.gluecode.fpvdrone.input.ControllerReader; 6 | import com.gluecode.fpvdrone.util.SettingsLoader; 7 | import com.jme3.math.Vector3f; 8 | import net.minecraft.client.Minecraft; 9 | import net.minecraft.world.entity.player.Player; 10 | import net.minecraft.world.phys.AABB; 11 | import net.minecraft.world.phys.Vec3; 12 | import net.minecraft.util.Mth; 13 | //import com.mojang.math.Vec3; 14 | 15 | public class PhysicsState { 16 | private static IPhysicsCore core = new DefaultPhysicsCore(); 17 | 18 | public static IPhysicsCore getCore() { 19 | return core; 20 | } 21 | 22 | public static void setCore(IPhysicsCore inCore) { 23 | if (inCore == null) return; 24 | core = inCore; 25 | } 26 | 27 | private static final Vector3f gravity = new Vector3f(0, -9.80665F, 0); 28 | public static Vector3f getGravity() { 29 | return gravity; 30 | } 31 | 32 | /* 33 | * Handles collisions and moves the player. 34 | * 35 | * Returns the velocity the player has after handling collision. 36 | * */ 37 | public static Vector3f collideAndMove(Vector3f nextVelocity, float elapsed) { 38 | if (!ControllerReader.getArm()) { 39 | return nextVelocity; 40 | } 41 | 42 | Minecraft minecraft = Minecraft.getInstance(); 43 | Player entity = minecraft.player; 44 | 45 | if (entity == null) { 46 | return nextVelocity; 47 | } 48 | 49 | Main.LOGGER.info("collide 4"); 50 | 51 | float limit = 500; // m/s 52 | float elastic = 0.2f; 53 | // float limit = 500.408f; 54 | if (nextVelocity.length() > limit) { 55 | nextVelocity.normalizeLocal().multLocal(limit); 56 | } 57 | 58 | Vector3f displacement = nextVelocity.mult(elapsed); 59 | Vec3 desired = new Vec3( 60 | displacement.x, 61 | displacement.y, 62 | displacement.z 63 | ); 64 | float desiredSpeed = nextVelocity.length(); 65 | if (entity.noPhysics) { 66 | entity.setPos(entity.getX() + desired.x, entity.getY() + desired.y, entity.getZ() + desired.z); 67 | return nextVelocity; 68 | } else { 69 | Vec3 clipped = entity.collide(desired); 70 | 71 | boolean collidedHorizontally = !Mth.equal( 72 | desired.x, 73 | clipped.x 74 | ) || !Mth.equal(desired.z, clipped.z); 75 | boolean collidedVertically = !Mth.equal( 76 | desired.y, 77 | clipped.y 78 | ); 79 | // boolean onGround = collidedVertically && desired.y < 0.0D; 80 | boolean collided = collidedHorizontally || collidedVertically; 81 | 82 | if (collided) { 83 | CollisionResults collisionResults = PhysicsCollision.getCollisionResults( 84 | displacement, 85 | clipped, 86 | nextVelocity 87 | ); 88 | 89 | entity.setPos(entity.getX() + collisionResults.displacement.x, entity.getY() + collisionResults.displacement.y, collisionResults.displacement.z); 90 | 91 | return collisionResults.velocity; 92 | } 93 | 94 | entity.setBoundingBox(entity.getBoundingBox().move(clipped)); 95 | if (SettingsLoader.currentUseRealtimePhysics) { 96 | entity.setPos(entity.getX(), entity.getY(), entity.getZ()); 97 | entity.setOldPosAndRot(); 98 | } 99 | //entity.setLocationFromBoundingbox(); 100 | AABB axisalignedbb = entity.getBoundingBox(); 101 | entity.setPosRaw((axisalignedbb.minX + axisalignedbb.maxX) / 2.0D, axisalignedbb.minY, (axisalignedbb.minZ + axisalignedbb.maxZ) / 2.0D); 102 | 103 | Vec3 clippedVelocity = clipped.scale(1f / elapsed); 104 | return new Vector3f( 105 | (float) clippedVelocity.x, 106 | (float) clippedVelocity.y, 107 | (float) clippedVelocity.z 108 | ); 109 | } 110 | } 111 | 112 | public static float getThrottle() { 113 | return ControllerReader.getThrottle(); 114 | } 115 | 116 | public static float getDroneMass() { 117 | return DroneBuild.getDroneMass(); 118 | } 119 | 120 | public static float getPropRadius() { 121 | return DroneBuild.getPropDiameter() * 0.5f; 122 | } 123 | 124 | public static int getBatteryCells() { 125 | return DroneBuild.getBatteryCells(); 126 | } 127 | 128 | public static float getKv() { 129 | return DroneBuild.getMotorKv(); 130 | } 131 | 132 | public static int getBlades() { 133 | return DroneBuild.getBlades(); 134 | } 135 | 136 | public static float getMotorMass() { 137 | return DroneBuild.getMotorMass(); 138 | } 139 | 140 | public static float getMotorWidth() { 141 | return DroneBuild.getMotorWidth(); 142 | } 143 | 144 | public static float getMotorHeight() { 145 | return DroneBuild.getMotorHeight(); 146 | } 147 | 148 | public static float getPropPitch() { 149 | return DroneBuild.getPropPitch(); 150 | } 151 | 152 | public static float getPropWidth() { 153 | return DroneBuild.getPropWidth(); 154 | } 155 | 156 | public static float getBladeMass(float propDiameter) { 157 | return DroneBuild.getBladeMass(propDiameter); 158 | } 159 | 160 | public static float getBellMass() { 161 | return DroneBuild.getBellMass(); 162 | } 163 | 164 | public static boolean getFlightMode3d() { 165 | return DroneBuild.getFlightMode3d(); 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/race/LapTime.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.race; 2 | 3 | import com.jme3.math.FastMath; 4 | 5 | import java.util.UUID; 6 | 7 | public class LapTime implements Comparable { 8 | public UUID userId; 9 | public int millis; 10 | 11 | public LapTime(UUID userId, int millis) { 12 | this.userId = userId; 13 | this.millis = millis; 14 | } 15 | 16 | @Override 17 | public int compareTo(LapTime o) { 18 | return Integer.compare(this.millis, o.millis); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/race/RaceClient.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.race; 2 | 3 | import com.gluecode.fpvdrone.Main; 4 | import com.google.common.collect.Lists; 5 | import com.google.common.collect.Maps; 6 | import net.minecraftforge.api.distmarker.Dist; 7 | import net.minecraftforge.api.distmarker.OnlyIn; 8 | 9 | import java.text.MessageFormat; 10 | import java.util.*; 11 | 12 | @OnlyIn(Dist.CLIENT) 13 | public class RaceClient { 14 | // Build mode stuff: 15 | public static boolean isBuildMode = false; 16 | public static final ArrayList builtGates = new ArrayList<>(); 17 | 18 | // These variable apply only to the current track the user is on according to userToTrack: 19 | public static HashMap currentTrackId = new HashMap<>(); 20 | public static final HashMap isRacingMode = new HashMap<>(); 21 | public static final HashMap userStartTime = new HashMap<>(); 22 | public static final HashMap userBestTime = new HashMap<>(); 23 | public static final HashMap userGateIndex = new HashMap<>(); 24 | 25 | // Users are added and removed as needed: 26 | public static final HashMap userToTrack = new HashMap<>(); 27 | public static final HashMap> trackToUsers = new HashMap<>(); 28 | 29 | // Values added will not be removed: 30 | public static final HashMap loadedTracks = new HashMap<>(); 31 | public static final HashMap> trackBestTimes = new HashMap<>(); 32 | 33 | public static void handleLapStartPacket(long startTimeMillis, UUID uuid) { 34 | // use client's time instead of server time because client may have unsynced time. 35 | userStartTime.put(uuid, System.currentTimeMillis()); 36 | } 37 | 38 | public static void handleLapBestPacket( 39 | int millis, 40 | UUID trackId, 41 | UUID userId 42 | ) { 43 | // Store all best times for the track, but also the current user's time in case the 44 | // current user isn't in the top list. 45 | userBestTime.put(userId, millis); 46 | 47 | if (trackBestTimes.get(trackId) == null) { 48 | trackBestTimes.put(trackId, new ArrayList<>()); 49 | } 50 | ArrayList list = trackBestTimes.get(trackId); 51 | 52 | // only add the time if the UUID isn't in here already: 53 | boolean found = false; 54 | 55 | for (LapTime lapTime : list) { 56 | if (lapTime.userId.equals(userId)) { 57 | // Otherwise update the existing object 58 | lapTime.millis = millis; 59 | 60 | found = true; 61 | break; 62 | } 63 | } 64 | if (!found) { 65 | list.add(new LapTime(userId, millis)); 66 | } 67 | 68 | // sort and only keep the top ten 69 | Collections.sort(list); 70 | if (list.size() > 10) { 71 | list.remove(10); 72 | } 73 | } 74 | 75 | public static void handleBuildModePacket(boolean value) { 76 | if (value != isBuildMode) { 77 | builtGates.clear(); 78 | } 79 | isBuildMode = value; 80 | } 81 | 82 | public static void addGate(SerialRaceGate gate) { 83 | builtGates.add(gate); 84 | } 85 | 86 | public static void handleRaceModePacket( 87 | boolean value, 88 | UUID trackId, 89 | UUID userId 90 | ) { 91 | if (value) { 92 | handleStartRace(trackId, userId); 93 | } else { 94 | handleStopRacing(trackId, userId); 95 | } 96 | } 97 | 98 | public static void handleStartRace(UUID trackId, UUID userId) { 99 | currentTrackId.put(userId, trackId); 100 | isRacingMode.put(userId, true); 101 | userToTrack.put(userId, trackId); 102 | userGateIndex.put(userId, 0); 103 | 104 | if (trackToUsers.get(trackId) == null) { 105 | trackToUsers.put(trackId, new HashMap<>()); 106 | } 107 | trackToUsers.get(trackId).put(userId, true); 108 | 109 | userStartTime.remove(userId); 110 | userBestTime.remove(userId); 111 | } 112 | 113 | public static void handleStopRacing(UUID trackId, UUID userId) { 114 | currentTrackId.remove(userId); 115 | isRacingMode.remove(userId); 116 | userToTrack.remove(userId); 117 | userGateIndex.remove(userId); 118 | 119 | if (trackToUsers.get(trackId) != null) { 120 | trackToUsers.get(trackId).remove(userId); 121 | } 122 | 123 | userStartTime.remove(userId); 124 | userBestTime.remove(userId); 125 | } 126 | 127 | public static void handleGateIndexPacket( 128 | int gateIndex, 129 | UUID trackId, 130 | UUID userId 131 | ) { 132 | userGateIndex.put(userId, gateIndex); 133 | } 134 | 135 | public static void loadTrack(SerialRaceTrack track) { 136 | loadedTracks.put(track.trackId, track); 137 | } 138 | 139 | public static boolean checkRacingMode(UUID userId) { 140 | Boolean isRacing = isRacingMode.get(userId); 141 | return isRacing != null && isRacing; 142 | } 143 | 144 | public static String formatTime(int millis) { 145 | int hours = millis / (1000 * 60 * 60); 146 | millis -= hours * (1000 * 60 * 60); 147 | int minutes = millis / (1000 * 60); 148 | millis -= minutes * (1000 * 60); 149 | int seconds = millis / 1000; 150 | millis -= seconds * 1000; 151 | int tens = millis / 10; 152 | String tensString = "" + tens; 153 | if (tensString.length() == 1) { 154 | tensString += "0"; 155 | } 156 | if (hours > 0) { 157 | return MessageFormat.format( 158 | "{0}:{1}:{2}:{3}", 159 | hours, 160 | minutes, 161 | seconds, 162 | tensString 163 | ); 164 | } else if (minutes > 0) { 165 | return MessageFormat.format( 166 | "{0}:{1}:{2}", 167 | minutes, 168 | seconds, 169 | tensString 170 | ); 171 | } else { 172 | return MessageFormat.format("{0}:{1}", seconds, tensString); 173 | } 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/race/SerialRaceTrack.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.race; 2 | 3 | import net.minecraft.network.FriendlyByteBuf; 4 | import net.minecraftforge.api.distmarker.Dist; 5 | import net.minecraftforge.api.distmarker.OnlyIn; 6 | import net.minecraftforge.fml.DistExecutor; 7 | import net.minecraftforge.network.NetworkEvent; 8 | 9 | import java.util.ArrayList; 10 | import java.util.UUID; 11 | import java.util.function.Supplier; 12 | 13 | public class SerialRaceTrack { 14 | public UUID trackId; 15 | public String name; 16 | public ArrayList gates; 17 | 18 | public SerialRaceTrack( 19 | UUID trackId, 20 | String name, 21 | ArrayList gates 22 | ) { 23 | this.trackId = trackId; 24 | this.name = name; 25 | this.gates = gates; 26 | } 27 | 28 | public static void encode(SerialRaceTrack msg, FriendlyByteBuf buffer) { 29 | buffer.writeLong(msg.trackId.getMostSignificantBits()); 30 | buffer.writeLong(msg.trackId.getLeastSignificantBits()); 31 | buffer.writeUtf(msg.name); 32 | SerialRaceTrack.encodeGates(msg.gates, buffer); 33 | } 34 | 35 | public static SerialRaceTrack decode(FriendlyByteBuf buffer) { 36 | return new SerialRaceTrack(new UUID( 37 | buffer.readLong(), 38 | buffer.readLong() 39 | ), buffer.readUtf(), decodeGates(buffer)); 40 | } 41 | 42 | public static void encodeGates( 43 | ArrayList gates, 44 | FriendlyByteBuf buffer 45 | ) { 46 | buffer.writeVarInt(gates.size()); 47 | 48 | for (int i = 0; i < gates.size(); i++) { 49 | SerialRaceGate gate = gates.get(i); 50 | SerialRaceGate.encode(gate, buffer); 51 | } 52 | } 53 | 54 | public static ArrayList decodeGates(FriendlyByteBuf buffer) { 55 | int size = buffer.readVarInt(); 56 | 57 | ArrayList gates = new ArrayList<>(); 58 | 59 | for (int i = 0; i < size; i++) { 60 | gates.add(SerialRaceGate.decode(buffer)); 61 | } 62 | 63 | return gates; 64 | } 65 | 66 | public int hashCode() { 67 | return this.trackId.hashCode(); 68 | } 69 | 70 | public boolean equals(Object o) { 71 | if (!(o instanceof SerialRaceTrack)) { 72 | return false; 73 | } 74 | if (this == o) { 75 | return true; 76 | } 77 | return this.trackId.equals(((SerialRaceTrack) o).trackId); 78 | } 79 | 80 | public static void handle( 81 | SerialRaceTrack msg, 82 | Supplier contextSupplier 83 | ) { 84 | contextSupplier.get().enqueueWork(() -> { 85 | DistExecutor.runWhenOn( 86 | Dist.CLIENT, 87 | () -> () -> SerialRaceTrack.handleClient(msg, contextSupplier) 88 | ); 89 | contextSupplier.get().setPacketHandled(true); 90 | }); 91 | } 92 | 93 | @OnlyIn(Dist.CLIENT) 94 | private static void handleClient( 95 | SerialRaceTrack msg, 96 | Supplier contextSupplier 97 | ) { 98 | RaceClient.loadTrack(msg); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/render/DebugRenderer.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.render; 2 | 3 | import com.gluecode.fpvdrone.Main; 4 | import net.minecraftforge.api.distmarker.Dist; 5 | import net.minecraftforge.api.distmarker.OnlyIn; 6 | import net.minecraftforge.fml.common.Mod; 7 | 8 | @OnlyIn(Dist.CLIENT) 9 | @Mod.EventBusSubscriber(value = Dist.CLIENT, modid = Main.MOD_ID) 10 | public class DebugRenderer { 11 | 12 | // @SubscribeEvent 13 | // public static void renderPhysicsDebug(RenderWorldLastEvent event) { 14 | // if (!InputHandler.showDebug()) return; 15 | // 16 | // LapRender.loadCustomShader(); 17 | // 18 | // PoseStack stack = event.getPoseStack(); 19 | // 20 | // Tessellator tessellator = Tessellator.getInstance(); 21 | // BufferBuilder buffer = tessellator.getBuilder(); 22 | // BuildModeRenderer.applyLineMode(); 23 | // 24 | // ActiveRenderInfo info = Minecraft.getInstance().gameRenderer.getMainCamera(); 25 | // 26 | // Vec3 eyePosMC = info.getPosition(); 27 | // Vector3f eyePos = new Vector3f( 28 | // (float) eyePosMC.x, 29 | // (float) eyePosMC.y, 30 | // (float) eyePosMC.z 31 | // ); 32 | // 33 | // stack.pushPose(); 34 | // stack.translate(-eyePos.x, -eyePos.y, -eyePos.z); // go to center 35 | // 36 | // for (int motorNumber = 0; motorNumber < 4; motorNumber++) { 37 | // stack.pushPose(); 38 | // 39 | // Vector3f droneWorldCenter = new Vector3f( 40 | // (float) InputHandler.position.x, 41 | // (float) InputHandler.position.y, 42 | // (float) InputHandler.position.z 43 | // ); 44 | // Vector3f motorObjectPosition = InputHandler.getMotorObjectPosition( 45 | // motorNumber); 46 | // Vector3f droneUp = InputHandler.droneUp; 47 | // 48 | // Vector3f motorWorldPosition = droneWorldCenter.add(motorObjectPosition); 49 | // 50 | // 51 | // stack.translate( 52 | // motorWorldPosition.x, 53 | // motorWorldPosition.y, 54 | // motorWorldPosition.z 55 | // ); 56 | // 57 | // Matrix4f matrix = stack.last().pose(); 58 | // 59 | // buffer.begin(GL11.GL_LINES, DefaultVertexFormats.POSITION_COLOR); 60 | // buffer.vertex(matrix, 0, 0, 0) 61 | // .color( 62 | // motorNumber == 0 ? 1f : 0, 63 | // motorNumber == 1 ? 1f : 0, 64 | // motorNumber == 2 ? 1f : 0, 65 | // 1f 66 | // ) 67 | // .endVertex(); 68 | // buffer.vertex( 69 | // matrix, 70 | // droneUp.x * 0.1f, 71 | // droneUp.y * 0.1f, 72 | // droneUp.z * 0.1f 73 | // ) 74 | // .color( 75 | // motorNumber == 0 ? 1f : 0, 76 | // motorNumber == 1 ? 1f : 0, 77 | // motorNumber == 2 ? 1f : 0, 78 | // 1f 79 | // ) 80 | // .endVertex(); 81 | // tessellator.end(); 82 | // 83 | // stack.popPose(); 84 | // } 85 | // 86 | // stack.popPose(); 87 | // 88 | // LapRender.unloadCustomShader(); 89 | // 90 | // BuildModeRenderer.cleanLineMode(); 91 | // } 92 | // 93 | // public static Vector3f getMotorObjectPosition(int motorNumber) { 94 | // // Angle direction is opposite of how it is in DroneModel. 95 | // float armAngle = motorNumber * 1f / 4f * FastMath.PI * 2f; 96 | // armAngle = armAngle + FastMath.HALF_PI + FastMath.QUARTER_PI; 97 | // float bladeLength = getPropDiameter() * 0.5f * 0.0254f; 98 | // float frameWidth = getFrameWidth() * 0.001f; 99 | // float armLength = getArmLength(bladeLength, frameWidth); 100 | // float motorPosition = armLength - armWidth / 2f * 0.001f; 101 | // Quaternion rot = (new Quaternion()); 102 | // rot.lookAt(droneLook, droneUp); 103 | // Vector3f rotated = rot.mult(new Vector3f(motorPosition, 0, 0)); 104 | // rot.fromAngleAxis(armAngle, droneUp); 105 | // return rot.mult(rotated); 106 | // } 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/render/HUD.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.render; 2 | 3 | import com.gluecode.fpvdrone.Main; 4 | import com.gluecode.fpvdrone.entity.DroneBuild; 5 | import com.gluecode.fpvdrone.input.ControllerReader; 6 | import com.gluecode.fpvdrone.physics.PhysicsState; 7 | import net.minecraftforge.api.distmarker.Dist; 8 | import net.minecraftforge.api.distmarker.OnlyIn; 9 | import net.minecraftforge.client.event.RenderGameOverlayEvent; 10 | import net.minecraftforge.eventbus.api.SubscribeEvent; 11 | import net.minecraftforge.fml.common.Mod; 12 | 13 | @OnlyIn(Dist.CLIENT) 14 | @Mod.EventBusSubscriber(value = Dist.CLIENT, modid = Main.MOD_ID) 15 | public class HUD { 16 | 17 | @SubscribeEvent 18 | public static void handleOverlayRender(RenderGameOverlayEvent.Text event) { 19 | // on screen debugging: 20 | if (ControllerReader.getCustomAngle()) { 21 | long tickTime = System.currentTimeMillis(); 22 | float elapsed = Math.toIntExact(tickTime - ControllerReader.angleChangeTime) / 1000F; 23 | if (elapsed < 3F) { 24 | event.getLeft().add("angle: " + DroneBuild.getCameraAngle()); 25 | } 26 | } 27 | 28 | if (PhysicsState.getCore().isOverheat()) { 29 | event.getLeft().add("Overheating..."); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/render/StickOverlayRenderer.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.render; 2 | 3 | import com.gluecode.fpvdrone.Main; 4 | import com.gluecode.fpvdrone.input.ControllerReader; 5 | import com.gluecode.fpvdrone.input.MouseManager; 6 | import com.mojang.blaze3d.vertex.PoseStack; 7 | import com.mojang.blaze3d.systems.RenderSystem; 8 | import net.minecraft.client.MainWindow; 9 | import net.minecraft.client.Minecraft; 10 | import net.minecraft.client.renderer.BufferBuilder; 11 | import net.minecraft.client.renderer.Tessellator; 12 | import net.minecraft.client.renderer.vertex.DefaultVertexFormats; 13 | import net.minecraft.util.math.vector.Matrix4f; 14 | import net.minecraftforge.api.distmarker.Dist; 15 | import net.minecraftforge.api.distmarker.OnlyIn; 16 | import net.minecraftforge.client.event.DrawHighlightEvent; 17 | import net.minecraftforge.client.event.RenderGameOverlayEvent; 18 | import net.minecraftforge.eventbus.api.SubscribeEvent; 19 | import net.minecraftforge.fml.common.Mod; 20 | import org.lwjgl.opengl.GL11; 21 | 22 | @OnlyIn(Dist.CLIENT) 23 | @Mod.EventBusSubscriber(value = Dist.CLIENT, modid = Main.MOD_ID) 24 | public class StickOverlayRenderer { 25 | @SubscribeEvent 26 | public static void handleBlockOutlineRendering(DrawHighlightEvent event) { 27 | if (ControllerReader.getArm() && !CameraManager.getShowBlockOutline()) event.setCanceled(true); 28 | } 29 | 30 | @SubscribeEvent 31 | public static void handleStickOverlayRendering(RenderGameOverlayEvent.Pre event) { 32 | if (!ControllerReader.getArm()) return; 33 | if (!CameraManager.getShowStickOverlay()) return; 34 | 35 | float mouseYawDiff = MouseManager.yposDiff(); 36 | float mousePitchDiff = MouseManager.getMousePitchDiff(); 37 | float mouseRollDiff = MouseManager.getMouseRollDiff(); 38 | 39 | MainWindow mainWindow = Minecraft.getInstance().getWindow(); 40 | int scaledWidth = mainWindow.getGuiScaledWidth(); 41 | int scaledHeight = mainWindow.getGuiScaledHeight(); 42 | 43 | PoseStack stack = event.getPoseStack(); 44 | stack.pushPose(); 45 | stack.translate( 46 | (float) (scaledWidth / 2), 47 | (float) (scaledHeight / 2), 48 | 0 49 | ); 50 | stack.scale(1, -1, 1); 51 | 52 | Tessellator tessellator = Tessellator.getInstance(); 53 | BufferBuilder buffer = tessellator.getBuilder(); 54 | buffer.begin(GL11.GL_LINES, DefaultVertexFormats.POSITION_COLOR); 55 | 56 | if (Minecraft.getInstance().player.isCreative() || 57 | Minecraft.getInstance().player.isSpectator()) { 58 | stack.translate(0, -scaledHeight / 2 + 20, 0); 59 | } else { 60 | stack.translate(0, -scaledHeight / 2 + 60, 0); 61 | } 62 | 63 | /* 64 | * float mousePitchDiff = getMousePitchDiff(); 65 | float mouseRollDiff = getMouseRollDiff(); 66 | * */ 67 | 68 | stack.pushPose(); 69 | stack.translate(15f, 0, 0); 70 | renderAxis( 71 | stack, 72 | buffer, 73 | ControllerReader.getRoll() + MouseManager.getMouseRollDiff(), 74 | ControllerReader.getPitch() + MouseManager.getMousePitchDiff() 75 | ); 76 | stack.popPose(); 77 | 78 | stack.pushPose(); 79 | stack.translate(-15f, 0, 0); 80 | renderAxis(stack, buffer, ControllerReader.getYaw(), ControllerReader.getThrottle()); 81 | stack.popPose(); 82 | 83 | applyLineMode(); 84 | tessellator.end(); 85 | cleanLineMode(); 86 | stack.popPose(); 87 | } 88 | 89 | public static void renderAxis( 90 | PoseStack stack, 91 | BufferBuilder buffer, 92 | float xValue, 93 | float yValue 94 | ) { 95 | stack.pushPose(); 96 | float size = 10f; 97 | 98 | stack.pushPose(); 99 | stack.scale(size, size, 1); 100 | Matrix4f matrix = stack.last().pose(); 101 | buffer.vertex(matrix, 0, -1, 0).color(1f, 1f, 1f, 1f).endVertex(); 102 | buffer.vertex(matrix, 0, 1, 0).color(1f, 1f, 1f, 1f).endVertex(); 103 | buffer.vertex(matrix, -1, 0, 0).color(1f, 1f, 1f, 1f).endVertex(); 104 | buffer.vertex(matrix, 1, 0, 0).color(1f, 1f, 1f, 1f).endVertex(); 105 | stack.popPose(); 106 | 107 | stack.pushPose(); 108 | matrix = stack.last().pose(); 109 | stack.translate(xValue * size, yValue * size, 0); 110 | buffer.vertex(matrix, 1, 1, 0).color(1f, 1f, 1f, 1f).endVertex(); 111 | buffer.vertex(matrix, 1, -1, 0).color(1f, 1f, 1f, 1f).endVertex(); 112 | buffer.vertex(matrix, 1, -1, 0).color(1f, 1f, 1f, 1f).endVertex(); 113 | buffer.vertex(matrix, -1, -1, 0).color(1f, 1f, 1f, 1f).endVertex(); 114 | buffer.vertex(matrix, -1, -1, 0).color(1f, 1f, 1f, 1f).endVertex(); 115 | buffer.vertex(matrix, -1, 1, 0).color(1f, 1f, 1f, 1f).endVertex(); 116 | buffer.vertex(matrix, -1, 1, 0).color(1f, 1f, 1f, 1f).endVertex(); 117 | buffer.vertex(matrix, 1, 1, 0).color(1f, 1f, 1f, 1f).endVertex(); 118 | stack.popPose(); 119 | 120 | stack.popPose(); 121 | } 122 | 123 | public static void applyLineMode() { 124 | RenderSystem.disableTexture(); 125 | RenderSystem.depthMask(false); 126 | GL11.glLineWidth(2.0F); 127 | } 128 | 129 | public static void cleanLineMode() { 130 | GL11.glLineWidth(1.0F); 131 | RenderSystem.depthMask(true); 132 | RenderSystem.enableTexture(); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/render/shader/ShaderObject.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.render.shader; 2 | 3 | import com.gluecode.fpvdrone.Main; 4 | import com.mojang.blaze3d.platform.GlStateManager; 5 | import net.minecraft.client.Minecraft; 6 | import net.minecraft.resources.IResource; 7 | import net.minecraft.resources.IResourceManager; 8 | import net.minecraft.resources.ResourceLocation; 9 | import org.apache.commons.io.IOUtils; 10 | import org.lwjgl.opengl.GL20; 11 | 12 | import java.nio.charset.StandardCharsets; 13 | 14 | public class ShaderObject { 15 | public int shader; 16 | public int type; 17 | 18 | public ShaderObject(String glslCode, int type) { 19 | this.type = type; 20 | this.shader = GlStateManager.glCreateShader(type); 21 | GL20.glShaderSource(this.shader, glslCode); 22 | GL20.glCompileShader(this.shader); 23 | int[] res = new int[1]; 24 | GL20.glGetShaderiv(this.shader, GL20.GL_COMPILE_STATUS, res); 25 | if (res[0] == GL20.GL_FALSE) { 26 | Main.LOGGER.error("Failed to compile shader."); 27 | String infoLog = GL20.glGetShaderInfoLog( 28 | this.shader, 29 | GL20.glGetShaderi(this.shader, GL20.GL_INFO_LOG_LENGTH) 30 | ); 31 | Main.LOGGER.error(infoLog); 32 | } else { 33 | Main.LOGGER.info("Shader compiled successfully."); 34 | } 35 | } 36 | 37 | public static ShaderObject createShader(int type, String filename) { 38 | ResourceLocation resourceLocation = new ResourceLocation( 39 | Main.MOD_ID, 40 | filename 41 | ); 42 | IResourceManager resourceManager = Minecraft.getInstance() 43 | .getResourceManager(); 44 | String contents = null; 45 | try { 46 | IResource resource = resourceManager.getResource(resourceLocation); 47 | contents = IOUtils.toString( 48 | resource.getInputStream(), 49 | StandardCharsets.UTF_8 50 | ); 51 | } catch (Exception e) { 52 | Main.LOGGER.error(e); 53 | } 54 | if (contents == null) return null; 55 | 56 | return new ShaderObject(contents, type); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/render/shader/ShaderProgram.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.render.shader; 2 | 3 | import com.gluecode.fpvdrone.Main; 4 | import org.lwjgl.opengl.GL20; 5 | 6 | public class ShaderProgram { 7 | public int program; 8 | public ShaderObject vertexShader; 9 | public ShaderObject fragmentShader; 10 | 11 | public ShaderProgram( 12 | ShaderObject vertexShader, 13 | ShaderObject fragmentShader 14 | ) { 15 | this.vertexShader = vertexShader; 16 | this.fragmentShader = fragmentShader; 17 | this.program = GL20.glCreateProgram(); 18 | GL20.glAttachShader(this.program, vertexShader.shader); 19 | GL20.glAttachShader(this.program, fragmentShader.shader); 20 | GL20.glLinkProgram(this.program); 21 | int[] res = new int[1]; 22 | GL20.glGetProgramiv(this.program, GL20.GL_LINK_STATUS, res); 23 | if (res[0] == GL20.GL_FALSE) { 24 | Main.LOGGER.error("Failed to link shader program."); 25 | } 26 | Main.LOGGER.info("Shader program linked."); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/gluecode/fpvdrone/util/Transforms.java: -------------------------------------------------------------------------------- 1 | package com.gluecode.fpvdrone.util; 2 | 3 | import com.gluecode.fpvdrone.physics.PhysicsConstants; 4 | import com.jme3.math.FastMath; 5 | import com.jme3.math.Quaternion; 6 | import com.jme3.math.Vector3f; 7 | 8 | import java.util.function.Consumer; 9 | import java.util.function.Supplier; 10 | 11 | public class Transforms { 12 | 13 | /** 14 | * @return [yaw, pitch, roll] 15 | */ 16 | public static float[] getWorldEulerAngles(Vector3f forward, Vector3f up) { 17 | float worldYaw = 0; 18 | float worldPitch = 0; 19 | float worldRoll = 0; 20 | 21 | Vector3f axis = Vector3f.NAN; 22 | 23 | if (forward.x != 0 || forward.z != 0) { 24 | // Calc yaw 25 | Vector3f forwardProj = new Vector3f(forward.x, 0, forward.z); 26 | Quaternion worldYawRot = (new Quaternion()); 27 | worldYawRot.lookAt(forwardProj, Vector3f.UNIT_Y); 28 | worldYaw = worldYawRot.toAngleAxis(axis); 29 | worldYaw = worldYaw * axis.y * PhysicsConstants.degs; 30 | 31 | // Calc pitch 32 | Quaternion antiYawRot = worldYawRot.inverse(); 33 | forwardProj = antiYawRot.mult(forward); 34 | Quaternion worldPitchRot = (new Quaternion()); 35 | worldPitchRot.lookAt(forwardProj, Vector3f.UNIT_Y); 36 | worldPitch = worldPitchRot.toAngleAxis(axis); 37 | worldPitch = worldPitch * axis.x * PhysicsConstants.degs; 38 | 39 | // Calc roll 40 | Quaternion rollessRot = (new Quaternion()).fromAngles( 41 | worldPitch * 42 | PhysicsConstants.rads, 43 | worldYaw * PhysicsConstants.rads, 44 | 0 45 | ); 46 | Vector3f rollessUp = rollessRot.mult(Vector3f.UNIT_Y); 47 | Vector3f crossUps = rollessUp.cross(up).normalizeLocal(); 48 | float lookAngle = crossUps.angleBetween(forward) * PhysicsConstants.degs; 49 | float flip = FastMath.abs(lookAngle) > 90 ? -1 : 1; 50 | worldRoll = rollessUp.angleBetween(up) * PhysicsConstants.degs * flip; 51 | } else if (forward.y > 0) { 52 | // looking straight up 53 | worldYaw = 0; 54 | worldPitch = -90; 55 | 56 | Vector3f upProj = new Vector3f(up.x, 0, up.z); 57 | Quaternion rot = (new Quaternion()); 58 | rot.lookAt(upProj, Vector3f.UNIT_Y); 59 | worldRoll = rot.toAngleAxis(axis); 60 | worldRoll = worldRoll * axis.y * PhysicsConstants.degs; 61 | } else if (forward.y < 0) { 62 | // looking straight down 63 | worldYaw = 0; 64 | worldPitch = 90; 65 | 66 | Vector3f upProj = new Vector3f(up.x, 0, up.z); 67 | Quaternion rot = (new Quaternion()); 68 | rot.lookAt(upProj, Vector3f.UNIT_Y); 69 | worldRoll = rot.toAngleAxis(axis); 70 | worldRoll = worldRoll * axis.y * PhysicsConstants.degs; 71 | } 72 | 73 | // worldYaw is backwards in minecraft 74 | return new float[]{-worldYaw, worldPitch, worldRoll}; 75 | } 76 | 77 | /** 78 | * @param rcCommand Raw controller input. 79 | */ 80 | public static float bfRate( 81 | float rcCommand, 82 | float rcRate, 83 | float superRate, 84 | float expo 85 | ) { 86 | float absRcCommand = FastMath.abs(rcCommand); 87 | 88 | if (rcRate > 2.0f) { 89 | rcRate = rcRate + (14.54f * (rcRate - 2.0f)); 90 | } 91 | 92 | if (expo != 0) 93 | rcCommand = rcCommand * 94 | FastMath.pow(FastMath.abs(rcCommand), 3) * 95 | expo + rcCommand * (1.0f - expo); 96 | 97 | float angleRate = 200.0f * rcRate * rcCommand; 98 | if (superRate != 0) { 99 | float rcSuperFactor = 1.0f / (FastMath.clamp( 100 | 1.0f - 101 | absRcCommand * 102 | (superRate), 103 | 0.01f, 104 | 1.00f 105 | )); 106 | angleRate *= rcSuperFactor; 107 | } 108 | 109 | return angleRate; 110 | } 111 | 112 | public static Supplier getMillimeter(Supplier getMeter) { 113 | return () -> { 114 | return getMeter.get() * 1000f; 115 | }; 116 | } 117 | 118 | public static Consumer setMillimeter(Consumer setMeter) { 119 | return (millimeter) -> { 120 | setMeter.accept(millimeter / 1000f); 121 | }; 122 | } 123 | 124 | public static Supplier getInches(Supplier getMeter) { 125 | return () -> { 126 | return getMeter.get() * PhysicsConstants.inches; 127 | }; 128 | } 129 | 130 | public static Consumer setInches(Consumer setMeter) { 131 | return (inches) -> { 132 | setMeter.accept(inches / PhysicsConstants.inches); 133 | }; 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/accesstransformer.cfg: -------------------------------------------------------------------------------- 1 | public net.minecraft.world.entity.Entity m_20272_(Lnet/minecraft/world/phys/Vec3;)Lnet/minecraft/world/phys/Vec3; # collide 2 | public net.minecraft.world.entity.Entity f_19857_ # yRot 3 | public net.minecraft.world.entity.Entity f_19858_ # xRot 4 | public net.minecraft.client.KeyMapping f_90817_ # isDown 5 | public net.minecraft.client.KeyMapping f_90818_ # clickCount 6 | public-f net.minecraft.client.Options f_92085_ # keyUp 7 | public-f net.minecraft.client.Options f_92086_ # keyLeft 8 | public-f net.minecraft.client.Options f_92087_ # keyDown 9 | public-f net.minecraft.client.Options f_92088_ # keyRight 10 | public-f net.minecraft.client.Options f_92089_ # keyJump 11 | 12 | event.getRenderer() -------------------------------------------------------------------------------- /src/main/resources/META-INF/mods.toml: -------------------------------------------------------------------------------- 1 | # This is an example mods.toml file. It contains the data relating to the loading mods. 2 | # There are several mandatory fields (#mandatory), and many more that are optional (#optional). 3 | # The overall format is standard TOML format, v0.5.0. 4 | # Note that there are a couple of TOML lists in this file. 5 | # Find more information on toml format here: https://github.com/toml-lang/toml 6 | # The name of the mod loader type to load - for regular FML @Mod mods it should be javafml 7 | modLoader="javafml" #mandatory 8 | 9 | # A version range to match for said mod loader - for regular FML @Mod it will be the forge version 10 | loaderVersion="[39,)" #mandatory This is typically bumped every Minecraft version by Forge. See our download page for lists of versions. 11 | 12 | # The license for you mod. This is mandatory metadata and allows for easier comprehension of your redistributive properties. 13 | # Review your options at https://choosealicense.com/. All rights reserved is the default copyright stance, and is thus the default here. 14 | license="All rights reserved" 15 | 16 | # A URL to refer people to when problems occur with this mod 17 | issueTrackerURL="https://tracker.minecraftfpv.com" #optional 18 | 19 | # A list of mods - how many allowed here is determined by the individual mod loader 20 | [[mods]] #mandatory 21 | 22 | # The modid of the mod 23 | modId="fpvdrone" #mandatory 24 | 25 | # The version number of the mod - there's a few well known ${} variables useable here or just hardcode it 26 | version="1.18.1-3.1.2" #mandatory 27 | 28 | # A display name for the mod 29 | displayName="Minecraft FPV" #mandatory 30 | 31 | # A URL to query for updates for this mod. See the JSON update specification 32 | updateJSONURL="https://minecraftfpv-assets.s3.us-east-2.amazonaws.com/version.json" #optional 33 | 34 | # A URL for the "homepage" for this mod, displayed in the mod UI 35 | displayURL="https://minecraftfpv.com/" #optional 36 | 37 | # A file name (in the root of the mod JAR) containing a logo for display 38 | logoFile="fpvdrone.png" #optional 39 | 40 | # A text field displayed in the mod UI 41 | credits="" #optional 42 | 43 | # A text field displayed in the mod UI 44 | authors="gluecode" #optional 45 | 46 | # The description text for the mod (multi line!) (#mandatory) 47 | description=''' 48 | This mod introduces a new game mode which allows the player to fly around like an FPV drone. Plug in your USB 49 | compatible transmitter, and you will be able to fly FPV in Minecraft! 50 | ''' 51 | 52 | # A dependency - use the . to indicate dependency for a specific modid. Dependencies are optional. 53 | [[dependencies.fpvdrone]] #optional 54 | # the modid of the dependency 55 | modId="forge" #mandatory 56 | # Does this dependency have to exist - if not, ordering below must be specified 57 | mandatory=true #mandatory 58 | # The version range of the dependency 59 | versionRange="[33,)" #mandatory 60 | # An ordering relationship for the dependency - BEFORE or AFTER required if the relationship is not mandatory 61 | ordering="NONE" 62 | # Side this dependency is applied on - BOTH, CLIENT or SERVER 63 | side="BOTH" 64 | -------------------------------------------------------------------------------- /src/main/resources/assets/fpvdrone/shaders/barrel_fragment.glsl: -------------------------------------------------------------------------------- 1 | #version 120 2 | 3 | varying vec2 vTex; 4 | 5 | uniform sampler2D inTex; 6 | uniform float fov; // radians, vertical fov. 7 | uniform float aspect; // aspect * fov = horizontal fov. so aspect = w / h 8 | 9 | void main() { 10 | float xImageMax = 0.5; 11 | float yImageMax = 0.5; 12 | vec2 texCentered = vTex - vec2(0.5, 0.5); // goes from [-0.5, 0.5]. 0 has to be at center of screen. 13 | float imageMax = length(vec2(xImageMax, yImageMax)); 14 | float image = length(texCentered * vec2(aspect, 1)); 15 | 16 | // float tanAngle = image / imageMax * tan(fov * 0.5); 17 | // float scale = atan(tanAngle) / tanAngle; 18 | 19 | float angle = atan(image / imageMax * tan(fov * 0.5)); 20 | // float scale = 2* tan(angle/2) / tan(angle); 21 | float scale = angle / tan(angle); 22 | // float scale = sin(angle) / tan(angle); 23 | // float scale = 2 * sin(angle / 2) / tan(angle); 24 | 25 | float stretch = (fov * 0.5) / tan(fov * 0.5); 26 | 27 | // vec2 vTexEquidistant = clamp(texCentered / scale + vec2(0.5, 0.5), vec2(0, 0), vec2(1, 1)); 28 | vec2 vTexEquidistant = texCentered / scale * stretch + vec2(0.5, 0.5); // unintuitive: texcoord needs to be scaled in the opposite direction. 29 | 30 | // if ( 31 | // vTexEquidistant.x > 1 || 32 | // vTexEquidistant.x < 0 || 33 | // vTexEquidistant.y > 1 || 34 | // vTexEquidistant.y < 0 35 | // ) { 36 | // gl_FragColor = vec4(0, 0, 0, 1); 37 | // } else { 38 | // vec4 samp = texture2D(inTex, vTexEquidistant); 39 | // float s = step(1, vTexEquidistant.x) * step(1, vTexEquidistant.y); 40 | // gl_FragColor = vec4(samp.r, samp.g, samp.b, samp.a); 41 | // } 42 | 43 | vec4 samp = texture2D(inTex, vTexEquidistant); 44 | float s = step(0, vTexEquidistant.x) * step(0, vTexEquidistant.y) * step(vTexEquidistant.x, 1) * step(vTexEquidistant.y, 1); 45 | gl_FragColor = vec4(s * samp.r, s * samp.g, s * samp.b, samp.a); 46 | 47 | // float a = atan(tanAngle) / (170 * 3.14159 / 180) + 0.5; 48 | // gl_FragColor = vec4(a, 0, 0, 1); 49 | } -------------------------------------------------------------------------------- /src/main/resources/assets/fpvdrone/shaders/barrel_vertex.glsl: -------------------------------------------------------------------------------- 1 | #version 120 2 | 3 | varying vec2 vTex; 4 | 5 | void main() { 6 | gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; 7 | vTex = vec2(gl_MultiTexCoord0.x, gl_MultiTexCoord0.y); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/resources/assets/fpvdrone/shaders/build_gate_fragment.glsl: -------------------------------------------------------------------------------- 1 | #version 120 2 | 3 | varying vec4 vColor; 4 | 5 | void main() { 6 | gl_FragColor = vColor; 7 | } 8 | -------------------------------------------------------------------------------- /src/main/resources/assets/fpvdrone/shaders/build_gate_vertex.glsl: -------------------------------------------------------------------------------- 1 | #version 120 2 | 3 | varying vec4 vColor; 4 | 5 | void main() { 6 | vColor = gl_Color; 7 | gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; 8 | } 9 | -------------------------------------------------------------------------------- /src/main/resources/assets/fpvdrone/shaders/copy_fragment.glsl: -------------------------------------------------------------------------------- 1 | #version 120 2 | 3 | varying vec2 vTex; 4 | 5 | uniform sampler2D inTex; 6 | 7 | void main() { 8 | vec4 samp = texture2D(inTex, vTex); 9 | gl_FragColor = vec4(samp.r, samp.g, samp.b, samp.a); 10 | 11 | // float a = atan(tanAngle) / (170 * 3.14159 / 180) + 0.5; 12 | // gl_FragColor = vec4(1, 0, 0, 1); 13 | } -------------------------------------------------------------------------------- /src/main/resources/assets/fpvdrone/textures/entity/drone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minecraft-fpv/mfpv-client/d567eaffe3badd899ef77461ed422f9a2043702b/src/main/resources/assets/fpvdrone/textures/entity/drone.png -------------------------------------------------------------------------------- /src/main/resources/assets/fpvdrone/textures/entity/props.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minecraft-fpv/mfpv-client/d567eaffe3badd899ef77461ed422f9a2043702b/src/main/resources/assets/fpvdrone/textures/entity/props.png -------------------------------------------------------------------------------- /src/main/resources/assets/fpvdrone/textures/entity/props2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minecraft-fpv/mfpv-client/d567eaffe3badd899ef77461ed422f9a2043702b/src/main/resources/assets/fpvdrone/textures/entity/props2.png -------------------------------------------------------------------------------- /src/main/resources/assets/fpvdrone/textures/entity/props3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minecraft-fpv/mfpv-client/d567eaffe3badd899ef77461ed422f9a2043702b/src/main/resources/assets/fpvdrone/textures/entity/props3.png -------------------------------------------------------------------------------- /src/main/resources/assets/fpvdrone/textures/entity/props4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minecraft-fpv/mfpv-client/d567eaffe3badd899ef77461ed422f9a2043702b/src/main/resources/assets/fpvdrone/textures/entity/props4.png -------------------------------------------------------------------------------- /src/main/resources/assets/fpvdrone/textures/entity/props5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/minecraft-fpv/mfpv-client/d567eaffe3badd899ef77461ed422f9a2043702b/src/main/resources/assets/fpvdrone/textures/entity/props5.png -------------------------------------------------------------------------------- /src/main/resources/pack.mcmeta: -------------------------------------------------------------------------------- 1 | { 2 | "pack": { 3 | "description": "examplemod resources", 4 | "pack_format": 5, 5 | "_comment": "A pack_format of 5 requires json lang files and some texture changes from 1.15. Note: we require v5 pack meta for all mods." 6 | } 7 | } 8 | --------------------------------------------------------------------------------