Merge feature/flaps into flight-computer

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YVREx3uo7nz1FEhzveWTUh
This commit is contained in:
Claude committed 2026-11-11 18:37:11 +01:00
commit 782df70a69
19 files changed
+526 -14

No files matched your search

+1 -1
View File
@@ -12,7 +12,7 @@ mapping_version=2023.09.03-1.20.1
mod_id=improved_aerodynamics
mod_name=VS: Improved Aerodynamics
mod_license=MIT
mod_version=0.3.0
mod_version=0.4.0
azurelib_version=3.2.7
mod_group_id=dev.lumareth.improvedaerodynamics
mod_authors=Lumareth
@@ -150,6 +150,26 @@ public final class Config {
.comment("Flight computer (auto trim): below this airspeed (blocks per second) the computer holds its current output")
.defineInRange("trimMinSpeed", 10.0D, 0.0D, 100.0D);
public static final ForgeConfigSpec.DoubleValue FLAP_TAKEOFF_DEGREES = BUILDER
.comment("Flaps: deflection in degrees of flaps at the takeoff setting. Flaps need the improved flight model")
.defineInRange("flapTakeoffDegrees", 15.0D, 0.0D, 60.0D);
public static final ForgeConfigSpec.DoubleValue FLAP_LANDING_DEGREES = BUILDER
.comment("Flaps: deflection in degrees of flaps at the landing setting")
.defineInRange("flapLandingDegrees", 35.0D, 0.0D, 60.0D);
public static final ForgeConfigSpec.DoubleValue FLAP_SLEW_DEGREES = BUILDER
.comment("Flaps: how fast they move, in degrees per second")
.defineInRange("flapSlewDegrees", 10.0D, 1.0D, 90.0D);
public static final ForgeConfigSpec.DoubleValue FLAP_TAKEOFF_MAX_SPEED = BUILDER
.comment("Flaps: airspeed in blocks per second above which the air pushes takeoff flaps back in, fully retracted at 25% more")
.defineInRange("flapTakeoffMaxSpeed", 45.0D, 1.0D, 1000.0D);
public static final ForgeConfigSpec.DoubleValue FLAP_LANDING_MAX_SPEED = BUILDER
.comment("Flaps: the same for the landing setting")
.defineInRange("flapLandingMaxSpeed", 35.0D, 1.0D, 1000.0D);
static {
BUILDER.pop();
BUILDER.comment("Vectored afterburner nozzle block").push("nozzle");
@@ -63,6 +63,11 @@ public final class ConfigCommands {
new Option<>("exhaust_style", Config.EXHAUST_STYLE, StringArgumentType.word(), String.class, List.of("wariumvs", "improved")),
new Option<>("nozzle_yaw_mix", Config.NOZZLE_YAW_MIX, DoubleArgumentType.doubleArg(), Double.class),
new Option<>("nozzle_light", Config.NOZZLE_LIGHT, BoolArgumentType.bool(), Boolean.class),
new Option<>("flap_takeoff_degrees", Config.FLAP_TAKEOFF_DEGREES, DoubleArgumentType.doubleArg(), Double.class),
new Option<>("flap_landing_degrees", Config.FLAP_LANDING_DEGREES, DoubleArgumentType.doubleArg(), Double.class),
new Option<>("flap_slew_degrees", Config.FLAP_SLEW_DEGREES, DoubleArgumentType.doubleArg(), Double.class),
new Option<>("flap_takeoff_max_speed", Config.FLAP_TAKEOFF_MAX_SPEED, DoubleArgumentType.doubleArg(), Double.class),
new Option<>("flap_landing_max_speed", Config.FLAP_LANDING_MAX_SPEED, DoubleArgumentType.doubleArg(), Double.class),
new Option<>("lookahead_seconds", Config.LOOKAHEAD_SECONDS, DoubleArgumentType.doubleArg(), Double.class));
private ConfigCommands() {
@@ -28,6 +28,7 @@ public class ImprovedAerodynamics {
MinecraftForge.EVENT_BUS.register(ConfigCommands.class);
MinecraftForge.EVENT_BUS.register(ExhaustSync.class);
MinecraftForge.EVENT_BUS.register(dev.lumareth.improvedAerodynamics.flight.ControlMode.class);
MinecraftForge.EVENT_BUS.register(dev.lumareth.improvedAerodynamics.flight.FlapInteraction.class);
ModLoadingContext.get().registerConfig(ModConfig.Type.SERVER, Config.SPEC);
ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, Config.CLIENT_SPEC);
// Own file name: the default one is taken by Config.CLIENT_SPEC
@@ -2,6 +2,7 @@ package dev.lumareth.improvedAerodynamics.client;
import com.mojang.blaze3d.platform.InputConstants;
import dev.lumareth.improvedAerodynamics.ImprovedAerodynamics;
import dev.lumareth.improvedAerodynamics.network.FlapPacket;
import dev.lumareth.improvedAerodynamics.network.Network;
import dev.lumareth.improvedAerodynamics.network.ToggleAfterburnerPacket;
import dev.lumareth.improvedAerodynamics.network.ToggleAutoTrimPacket;
@@ -22,6 +23,12 @@ public final class KeyBindings {
public static final KeyMapping TOGGLE_AFTERBURNER = new KeyMapping("key.improved_aerodynamics.toggle_afterburner",
KeyConflictContext.IN_GAME, InputConstants.Type.KEYSYM, GLFW.GLFW_KEY_K, "key.categories.improved_aerodynamics");
public static final KeyMapping FLAPS_DOWN = new KeyMapping("key.improved_aerodynamics.flaps_down",
KeyConflictContext.IN_GAME, InputConstants.Type.KEYSYM, GLFW.GLFW_KEY_G, "key.categories.improved_aerodynamics");
public static final KeyMapping FLAPS_UP = new KeyMapping("key.improved_aerodynamics.flaps_up",
KeyConflictContext.IN_GAME, InputConstants.Type.KEYSYM, GLFW.GLFW_KEY_H, "key.categories.improved_aerodynamics");
private KeyBindings() {
}
@@ -34,6 +41,8 @@ public final class KeyBindings {
public static void onRegisterKeyMappings(RegisterKeyMappingsEvent event) {
event.register(TOGGLE_AUTO_TRIM);
event.register(TOGGLE_AFTERBURNER);
event.register(FLAPS_DOWN);
event.register(FLAPS_UP);
}
}
@@ -61,6 +70,19 @@ public final class KeyBindings {
Network.CHANNEL.sendToServer(new ToggleAfterburnerPacket());
}
}
while (FLAPS_DOWN.consumeClick()) {
sendFlaps(minecraft, true);
}
while (FLAPS_UP.consumeClick()) {
sendFlaps(minecraft, false);
}
}
private static void sendFlaps(Minecraft minecraft, boolean extend) {
if (minecraft.player != null && minecraft.player.getVehicle() != null && minecraft.getConnection() != null
&& Network.CHANNEL.isRemotePresent(minecraft.getConnection().getConnection())) {
Network.CHANNEL.sendToServer(new FlapPacket(extend));
}
}
}
}
@@ -55,6 +55,8 @@ public final class AeroState implements PhysicsBridge.Publishable {
double roll;
double trimIntegral;
double trimOutput;
// Deflection of this node's flaps in radians, moves towards what the flap lever and airspeed allow
double flapAngle;
// Total pitch input the flight computer applied last substep, pilot and trim together
double computerOutput;
// Torque (ship axes) one unit of pitch input on this node's pitch surfaces makes, measured every substep
@@ -84,6 +86,7 @@ public final class AeroState implements PhysicsBridge.Publishable {
trimOutput = data.autoTrim() ? 0.0 : trimIntegral;
savedTrim = trimIntegral;
computerOutput = pitch + trimOutput;
flapAngle = Math.toRadians(FlapSetting.of(data.flapSetting()).degrees());
}
}
@@ -241,9 +244,10 @@ public final class AeroState implements PhysicsBridge.Publishable {
NodeData node = s.node() != AeroSurface.NO_NODE ? frame.nodes.get(s.node()) : null;
NodeState state = node != null ? nodeStates.get(node.key()) : null;
double input = s.type().controllable ? input(s, node, state, ControlAxis.NONE, 0.0) : 0.0;
double flap = s.flap() && state != null ? state.flapAngle : 0.0;
if (improved) {
boolean nowStalled = ImprovedAero.evaluate(s, v, w, r, input, weight, tuning, stalled[i], out);
boolean nowStalled = ImprovedAero.evaluate(s, v, w, r, input, flap, weight, tuning, stalled[i], out);
if (commit) {
stalled[i] = nowStalled;
wingStalled |= nowStalled && s.type() == SurfaceType.WING;
@@ -263,7 +267,7 @@ public final class AeroState implements PhysicsBridge.Publishable {
double delta = 0.05;
probe.clear();
if (improved) {
ImprovedAero.evaluate(s, v, w, r, input, weight, tuning, stalled[i], probe);
ImprovedAero.evaluate(s, v, w, r, input, flap, weight, tuning, stalled[i], probe);
} else {
WariumAero.evaluate(s, v, w, r, input, weight, probe);
}
@@ -271,7 +275,7 @@ public final class AeroState implements PhysicsBridge.Publishable {
probe.clear();
double nudged = input(s, node, state, axis, delta);
if (improved) {
ImprovedAero.evaluate(s, v, w, r, nudged, weight, tuning, stalled[i], probe);
ImprovedAero.evaluate(s, v, w, r, nudged, flap, weight, tuning, stalled[i], probe);
} else {
WariumAero.evaluate(s, v, w, r, nudged, weight, probe);
}
@@ -328,10 +332,25 @@ public final class AeroState implements PhysicsBridge.Publishable {
state.pitch = moveTowards(state.pitch, effective.pitch(), step);
state.yaw = moveTowards(state.yaw, effective.yaw(), step);
state.roll = moveTowards(state.roll, effective.roll(), step);
updateFlaps(state, node, worldVelocity.length(), dt);
AutoTrim.update(state, effective, worldVelocity, altitude, rotation, inertia, w, torque, dt, improved && wingStalled);
}
}
/**
* Moves a node's flaps towards the angle its lever asks for. Above the setting's maximum speed the air pushes them
* back in, so a craft that is too fast for its flaps has them half retracted, and all the way at 25% more.
*/
private static void updateFlaps(NodeState state, NodeData node, double speed, double dt) {
FlapSetting setting = FlapSetting.of(node.flapSetting());
double target = Math.toRadians(setting.degrees());
double limit = setting.maxSpeed();
if (speed > limit) {
target *= Math.max(0.0, 1.0 - (speed - limit) / (0.25 * limit));
}
state.flapAngle = moveTowards(state.flapAngle, target, Math.toRadians(Config.FLAP_SLEW_DEGREES.get()) * dt);
}
static double moveTowards(double value, double target, double maxStep) {
if (Math.abs(target - value) <= maxStep) {
return target;
@@ -14,16 +14,17 @@ package dev.lumareth.improvedAerodynamics.flight;
* @param brakeInput air brakes only: Warium's input value, they ignore smoothing and trim
* @param aspectRatio span squared over area of the wing the block belongs to, 0 if not known yet
* @param wingGroup whether the wing the block belongs to contains WING blocks, not only control surfaces or stabilizers
* @param flap whether the block was made a flap with shift-right-click, it then also follows its node's flap setting
*/
public record AeroSurface(long key, SurfaceType type, boolean slab, double x, double y, double z, int facingX, int facingZ,
double pressure, double fluidFactor, double speedMultiplier,
long node, ControlAxis axis1, double multiplier1, ControlAxis axis2, double multiplier2,
double brakeInput, double aspectRatio, boolean wingGroup) {
double brakeInput, double aspectRatio, boolean wingGroup, boolean flap) {
public static final long NO_NODE = Long.MIN_VALUE;
AeroSurface withWingShape(double aspectRatio, boolean wingGroup) {
return new AeroSurface(key, type, slab, x, y, z, facingX, facingZ, pressure, fluidFactor, speedMultiplier,
node, axis1, multiplier1, axis2, multiplier2, brakeInput, aspectRatio, wingGroup);
node, axis1, multiplier1, axis2, multiplier2, brakeInput, aspectRatio, wingGroup, flap);
}
/** Warium's "Direction.getClockWise(Axis.Y)" of the facing, the span direction of a horizontal surface. */
@@ -105,7 +105,7 @@ final class CursorPilot {
Math.abs(node.pitch()) > MANUAL_OVERRIDE ? node.pitch() : pitch,
Math.abs(node.yaw()) > MANUAL_OVERRIDE ? node.yaw() : yaw,
Math.abs(node.roll()) > MANUAL_OVERRIDE ? node.roll() : roll,
node.throttle(), node.autoTrim(), node.storedTrim());
node.throttle(), node.autoTrim(), node.storedTrim(), node.flapSetting());
}
/** Least squares for the three inputs whose responses add up to the torque, ridge-regularized so unused axes stay at zero. */
@@ -0,0 +1,37 @@
package dev.lumareth.improvedAerodynamics.flight;
import net.mcreator.valkyrienwarium.entity.SeatEntityEntity;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.block.entity.BlockEntity;
/** Server side of the flap keys: moves the flap lever of the control node the pilot's seat is linked to. */
public final class FlapControl {
private FlapControl() {
}
/** @param step +1 for more flaps, -1 for fewer */
public static void step(ServerPlayer player, int step) {
Entity vehicle = player.getVehicle();
if (!(vehicle instanceof SeatEntityEntity)) {
player.displayClientMessage(Component.translatable("message.improved_aerodynamics.flaps.no_seat"), true);
return;
}
BlockEntity node = AutoTrimToggle.findNode(vehicle.level(), vehicle.blockPosition());
if (node == null) {
player.displayClientMessage(Component.translatable("message.improved_aerodynamics.flaps.no_node"), true);
return;
}
CompoundTag tag = node.getPersistentData();
int current = FlapSetting.of(tag.getInt(WariumCapture.FLAP_SETTING_TAG)).ordinal();
int next = Math.max(0, Math.min(FlapSetting.values().length - 1, current + (step > 0 ? 1 : -1)));
if (next != current) {
tag.putInt(WariumCapture.FLAP_SETTING_TAG, next);
node.setChanged();
}
player.displayClientMessage(Component.translatable("message.improved_aerodynamics.flaps",
Component.translatable(FlapSetting.of(next).translationKey())), true);
}
}
@@ -0,0 +1,66 @@
package dev.lumareth.improvedAerodynamics.flight;
import dev.lumareth.improvedAerodynamics.ImprovedAerodynamics;
import net.minecraft.core.BlockPos;
import net.minecraft.core.registries.Registries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.TagKey;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraftforge.event.entity.player.PlayerInteractEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
/**
* Shift-right-click on a block in the {@code improved_aerodynamics:flappable} tag (Warium's horizontal control
* surfaces) with empty hands makes it a flap, or a normal control surface again. A flap follows the flap lever of the
* control node it is linked to, on top of the pilot's input.
*/
public final class FlapInteraction {
public static final TagKey<Block> FLAPPABLE = TagKey.create(Registries.BLOCK,
new ResourceLocation(ImprovedAerodynamics.MOD_ID, "flappable"));
private FlapInteraction() {
}
@SubscribeEvent
public static void onRightClickBlock(PlayerInteractEvent.RightClickBlock event) {
Player player = event.getEntity();
if (event.getHand() != InteractionHand.MAIN_HAND || !player.isShiftKeyDown() || player.isPassenger()
|| !player.getMainHandItem().isEmpty() || !player.getOffhandItem().isEmpty()) {
return;
}
Level level = event.getLevel();
BlockPos pos = event.getPos();
BlockState state = level.getBlockState(pos);
if (!state.is(FLAPPABLE)) {
return;
}
// Handled on both sides so the block's own right-click action does not run on either
event.setCanceled(true);
event.setCancellationResult(InteractionResult.sidedSuccess(level.isClientSide));
if (level.isClientSide) {
return;
}
BlockEntity blockEntity = level.getBlockEntity(pos);
if (blockEntity == null) {
return;
}
CompoundTag tag = blockEntity.getPersistentData();
boolean flap = !tag.getBoolean(WariumCapture.FLAP_TAG);
tag.putBoolean(WariumCapture.FLAP_TAG, flap);
blockEntity.setChanged();
if (flap && !tag.contains("ControlX")) {
player.displayClientMessage(Component.translatable("message.improved_aerodynamics.flap.unlinked"), true);
} else {
player.displayClientMessage(Component.translatable(flap
? "message.improved_aerodynamics.flap.on" : "message.improved_aerodynamics.flap.off"), true);
}
}
}
@@ -0,0 +1,38 @@
package dev.lumareth.improvedAerodynamics.flight;
import dev.lumareth.improvedAerodynamics.Config;
/** Positions of a control node's flap lever. */
public enum FlapSetting {
UP,
TAKEOFF,
LANDING;
/** The setting for a stored number, UP for anything that is not one. */
public static FlapSetting of(int ordinal) {
FlapSetting[] values = values();
return ordinal >= 0 && ordinal < values.length ? values[ordinal] : UP;
}
/** Deflection in degrees the flaps move to at this setting. */
double degrees() {
return switch (this) {
case UP -> 0.0;
case TAKEOFF -> Config.FLAP_TAKEOFF_DEGREES.get();
case LANDING -> Config.FLAP_LANDING_DEGREES.get();
};
}
/** Airspeed in blocks per second above which the air starts to push the flaps back in. Infinite when they are up. */
double maxSpeed() {
return switch (this) {
case UP -> Double.POSITIVE_INFINITY;
case TAKEOFF -> Config.FLAP_TAKEOFF_MAX_SPEED.get();
case LANDING -> Config.FLAP_LANDING_MAX_SPEED.get();
};
}
public String translationKey() {
return "message.improved_aerodynamics.flaps." + name().toLowerCase(java.util.Locale.ROOT);
}
}
@@ -27,6 +27,12 @@ final class ImprovedAero {
private static final double MAX_DEFLECTION = Math.toRadians(20.0);
// Share of the deflection that acts as angle of attack, gives Warium's control authority at the design speed
private static final double FLAP_EFFECTIVENESS = 0.75;
// How much of the lift a flap adds raises the stall angle, and the parasitic drag coefficient per radian² of flap
// Share of the flap's deflection that acts as angle of attack: 35 degrees of landing flap adds about 1.1 to the
// lift coefficient, like a real plain or slotted flap
private static final double FLAP_ALPHA_FACTOR = 0.4;
private static final double FLAP_STALL_GAIN = 0.7;
private static final double FLAP_DRAG = 0.35;
// Angle of attack of a level cambered wing, where K is calibrated
private static final double REFERENCE_ANGLE = Math.toRadians(4.0);
private static final double REFERENCE_SPEED = 40.0;
@@ -62,10 +68,11 @@ final class ImprovedAero {
* @param w ship angular velocity, ship axes
* @param r block center relative to the center of mass, ship axes
* @param input control input of the surface, -1 to 1
* @param flap flap deflection in radians, 0 for surfaces that are not flaps
* @param stalled whether the surface was stalled in the previous substep
* @return whether the surface is stalled now
*/
static boolean evaluate(AeroSurface s, double[] v, double[] w, double[] r, double input, double weight,
static boolean evaluate(AeroSurface s, double[] v, double[] w, double[] r, double input, double flap, double weight,
Tuning tuning, boolean stalled, Wrench out) {
// Airflow at the block: ship velocity plus w x r
double vx = v[0] + w[1] * r[2] - w[2] * r[1];
@@ -114,6 +121,10 @@ final class ImprovedAero {
// Same sign convention as Warium: positive input pushes along n while moving against the facing
alpha += FLAP_EFFECTIVENESS * MAX_DEFLECTION * input * (u <= 0.0 ? 1.0 : -1.0);
}
// A flap bends the airfoil's camber whichever way the air comes, so it always adds lift, and it moves the stall
// up with it: a flap raises the highest lift a wing can make
double flapShift = FLAP_ALPHA_FACTOR * flap;
alpha += flapShift;
alpha = Math.max(-Math.PI / 2.0, Math.min(Math.PI / 2.0, alpha));
// Low aspect ratio wings keep their flow attached longer thanks to leading-edge vortices. Control surfaces
@@ -124,6 +135,9 @@ final class ImprovedAero {
: s.wingGroup() && s.type().controllable ? SurfaceType.WING.stallAngle : s.type().stallAngle;
double critical = Math.toRadians(baseStall) * (1.0 + tuning.vortexStallBonus() * vortexFactor) * tuning.stallAngleScale();
double absAlpha = Math.abs(alpha);
if (alpha >= 0.0) {
critical += FLAP_STALL_GAIN * flapShift;
}
// Flips only where both blends agree, so the hysteresis never makes the force jump
if (!stalled && absAlpha > critical + STALL_BLEND) {
stalled = true;
@@ -156,7 +170,7 @@ final class ImprovedAero {
double dy = -(vn * ny) / planeSpeed;
double dz = -(u * cz + vn * nz) / planeSpeed;
double liftForce = q * lift * tuning.liftScale();
double dragForce = q * drag / m;
double dragForce = q * (drag + FLAP_DRAG * flap * flap) / m;
out.addForce(r, lx * liftForce + dx * dragForce, ly * liftForce + dy * dragForce, lz * liftForce + dz * dragForce);
return stalled;
}
@@ -5,6 +5,7 @@ package dev.lumareth.improvedAerodynamics.flight;
* trim state.
*
* @param storedTrim trim saved in the node, used when the physics thread first sees the node
* @param flapSetting the node's flap lever, see {@link FlapSetting#ordinal()}
*/
public record NodeData(long key, double pitch, double yaw, double roll, double throttle, boolean autoTrim, double storedTrim) {
public record NodeData(long key, double pitch, double yaw, double roll, double throttle, boolean autoTrim, double storedTrim, int flapSetting) {
}
@@ -31,6 +31,9 @@ import org.valkyrienskies.mod.common.VSGameUtilsKt;
public final class WariumCapture {
public static final String AUTO_TRIM_TAG = "improved_aerodynamics_autotrim";
public static final String TRIM_TAG = "improved_aerodynamics_trim";
/** Block entity flag of a surface that is a flap, and the flap lever of a control node. */
public static final String FLAP_TAG = "improved_aerodynamics_flap";
public static final String FLAP_SETTING_TAG = "improved_aerodynamics_flap_setting";
private static final int TRIM_SAVE_INTERVAL = 20;
// The procedure currently running, procedures run one at a time on the server thread
@@ -77,10 +80,12 @@ public final class WariumCapture {
String key = "Null";
String key2 = "Null";
double brakeInput = 0.0;
boolean flap = false;
if (type.controllable || type == SurfaceType.AIR_BRAKE) {
BlockEntity blockEntity = level.getBlockEntity(pos);
if (blockEntity != null) {
CompoundTag tag = blockEntity.getPersistentData();
flap = type == SurfaceType.CONTROL_SURFACE && tag.getBoolean(FLAP_TAG);
key = tag.getString("Key");
key2 = type == SurfaceType.AIR_BRAKE ? "Null" : tag.getString("Key2");
BlockPos nodePos = BlockPos.containing(tag.getDouble("ControlX"), tag.getDouble("ControlY"), tag.getDouble("ControlZ"));
@@ -97,7 +102,7 @@ public final class WariumCapture {
return new AeroSurface(pos.asLong(), type, slab, pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5,
facing.getStepX(), facing.getStepZ(), pressure, fluidFactor(level), speedMultiplier,
node, ControlAxis.of(key), ControlAxis.multiplier(key), ControlAxis.of(key2), ControlAxis.multiplier(key2),
brakeInput, 0.0, false);
brakeInput, 0.0, false, flap);
}
/** Registers a node's inputs once per tick, and saves its trim to it now and then so it survives reloading. */
@@ -119,7 +124,8 @@ public final class WariumCapture {
}
}
aero.addNode(new NodeData(nodeKey, tag.getDouble("Pitch"), tag.getDouble("Yaw"), tag.getDouble("Roll"),
tag.getDouble("Throttle"), autoTrim, storedTrim));
tag.getDouble("Throttle"), autoTrim, storedTrim,
FlapSetting.of(tag.getInt(FLAP_SETTING_TAG)).ordinal()));
}
/** Raw node value for a key like "Pitch+", as Warium's air brakes read it. */
@@ -0,0 +1,33 @@
package dev.lumareth.improvedAerodynamics.network;
import dev.lumareth.improvedAerodynamics.flight.FlapControl;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.server.level.ServerPlayer;
import net.minecraftforge.fml.ModList;
import net.minecraftforge.network.NetworkEvent;
import java.util.function.Supplier;
/** Sent when a pilot moves the flap lever: more flaps (true) or fewer. */
public final class FlapPacket {
private final boolean extend;
public FlapPacket(boolean extend) {
this.extend = extend;
}
public void encode(FriendlyByteBuf buffer) {
buffer.writeBoolean(extend);
}
public static FlapPacket decode(FriendlyByteBuf buffer) {
return new FlapPacket(buffer.readBoolean());
}
public void handle(Supplier<NetworkEvent.Context> context) {
ServerPlayer player = context.get().getSender();
if (player != null && ModList.get().isLoaded("valkyrien_warium")) {
FlapControl.step(player, extend ? 1 : -1);
}
}
}
@@ -44,5 +44,11 @@ public final class Network {
.decoder(ControlModePacket::decode)
.consumerMainThread(ControlModePacket::handle)
.add();
// Higher id than the neighbours so it does not collide with packets added on other branches
CHANNEL.messageBuilder(FlapPacket.class, 10, NetworkDirection.PLAY_TO_SERVER)
.encoder(FlapPacket::encode)
.decoder(FlapPacket::decode)
.consumerMainThread(FlapPacket::handle)
.add();
}
}
@@ -27,5 +27,16 @@
"message.improved_aerodynamics.cursor_aim.on": "Cursor-Steuerung an",
"message.improved_aerodynamics.cursor_aim.off": "Cursor-Steuerung aus",
"message.improved_aerodynamics.cursor_aim.no_seat": "Setz dich in einen Warium-Steuersitz, um die Cursor-Steuerung zu nutzen",
"message.improved_aerodynamics.cursor_aim.no_server": "Dieser Server hat Improved Aerodynamics nicht, die Cursor-Steuerung braucht es"
"message.improved_aerodynamics.cursor_aim.no_server": "Dieser Server hat Improved Aerodynamics nicht, die Cursor-Steuerung braucht es",
"key.improved_aerodynamics.flaps_down": "Klappen runter (eine Stufe)",
"key.improved_aerodynamics.flaps_up": "Klappen rauf (eine Stufe)",
"message.improved_aerodynamics.flaps": "Klappen: %s",
"message.improved_aerodynamics.flaps.up": "eingefahren",
"message.improved_aerodynamics.flaps.takeoff": "Start",
"message.improved_aerodynamics.flaps.landing": "Landung",
"message.improved_aerodynamics.flaps.no_seat": "Setz dich in einen Warium-Steuersitz, um die Klappen zu nutzen",
"message.improved_aerodynamics.flaps.no_node": "Dieser Sitz ist mit keinem Vehicle Control Node verbunden",
"message.improved_aerodynamics.flap.on": "Das Ruder ist jetzt eine Klappe",
"message.improved_aerodynamics.flap.off": "Das Ruder ist wieder ein normales Ruder",
"message.improved_aerodynamics.flap.unlinked": "Das Ruder ist jetzt eine Klappe, aber noch mit keinem Vehicle Control Node verbunden"
}
@@ -27,5 +27,16 @@
"message.improved_aerodynamics.cursor_aim.on": "Cursor aim on",
"message.improved_aerodynamics.cursor_aim.off": "Cursor aim off",
"message.improved_aerodynamics.cursor_aim.no_seat": "Sit in a Warium control seat to use cursor aim",
"message.improved_aerodynamics.cursor_aim.no_server": "This server does not have Improved Aerodynamics, cursor aim needs it"
"message.improved_aerodynamics.cursor_aim.no_server": "This server does not have Improved Aerodynamics, cursor aim needs it",
"key.improved_aerodynamics.flaps_down": "Flaps down (one step)",
"key.improved_aerodynamics.flaps_up": "Flaps up (one step)",
"message.improved_aerodynamics.flaps": "Flaps: %s",
"message.improved_aerodynamics.flaps.up": "up",
"message.improved_aerodynamics.flaps.takeoff": "takeoff",
"message.improved_aerodynamics.flaps.landing": "landing",
"message.improved_aerodynamics.flaps.no_seat": "Sit in a Warium control seat to use the flaps",
"message.improved_aerodynamics.flaps.no_node": "This seat is not linked to a vehicle control node",
"message.improved_aerodynamics.flap.on": "Control surface is now a flap",
"message.improved_aerodynamics.flap.off": "Control surface is a normal control surface again",
"message.improved_aerodynamics.flap.unlinked": "Control surface is now a flap, but it is not linked to a vehicle control node yet"
}
@@ -0,0 +1,221 @@
{
"replace": false,
"values": [
{
"id": "valkyrien_warium:control_surface",
"required": false
},
{
"id": "valkyrien_warium:control_surface_offset_top",
"required": false
},
{
"id": "valkyrien_warium:control_surface_offset_bottom",
"required": false
},
{
"id": "valkyrien_warium:control_surface_dark_gray",
"required": false
},
{
"id": "valkyrien_warium:control_surface_offset_top_dark_gray",
"required": false
},
{
"id": "valkyrien_warium:control_surface_offset_bottom_dark_gray",
"required": false
},
{
"id": "valkyrien_warium:wooden_control_surface",
"required": false
},
{
"id": "valkyrien_warium:wooden_control_surface_offset_top",
"required": false
},
{
"id": "valkyrien_warium:wooden_control_surface_offset_bottom",
"required": false
},
{
"id": "valkyrien_warium:red_control_surface",
"required": false
},
{
"id": "valkyrien_warium:red_control_surface_offset_top",
"required": false
},
{
"id": "valkyrien_warium:red_control_surface_offset_bottom",
"required": false
},
{
"id": "valkyrien_warium:blue_control_surface",
"required": false
},
{
"id": "valkyrien_warium:blue_control_surface_offset_top",
"required": false
},
{
"id": "valkyrien_warium:blue_control_surface_offset_bottom",
"required": false
},
{
"id": "valkyrien_warium:brown_control_surface",
"required": false
},
{
"id": "valkyrien_warium:brown_control_surface_offset_top",
"required": false
},
{
"id": "valkyrien_warium:brown_control_surface_offset_bottom",
"required": false
},
{
"id": "valkyrien_warium:green_control_surface",
"required": false
},
{
"id": "valkyrien_warium:green_control_surface_offset_bottom",
"required": false
},
{
"id": "valkyrien_warium:cyan_control_surface",
"required": false
},
{
"id": "valkyrien_warium:cyan_control_surface_offset_top",
"required": false
},
{
"id": "valkyrien_warium:cyan_control_surface_offset_bottom",
"required": false
},
{
"id": "valkyrien_warium:orange_control_surface",
"required": false
},
{
"id": "valkyrien_warium:orange_control_surface_offset_top",
"required": false
},
{
"id": "valkyrien_warium:orange_control_surface_offset_bottom",
"required": false
},
{
"id": "valkyrien_warium:yellow_control_surface",
"required": false
},
{
"id": "valkyrien_warium:yellow_control_surface_offset_top",
"required": false
},
{
"id": "valkyrien_warium:yellow_control_surface_offset_bottom",
"required": false
},
{
"id": "valkyrien_warium:gray_control_surface",
"required": false
},
{
"id": "valkyrien_warium:gray_control_surface_offset_top",
"required": false
},
{
"id": "valkyrien_warium:gray_control_surface_offset_bottom",
"required": false
},
{
"id": "valkyrien_warium:light_gray_control_surface",
"required": false
},
{
"id": "valkyrien_warium:light_gray_control_surface_offset_top",
"required": false
},
{
"id": "valkyrien_warium:light_gray_control_surface_offset_bottom",
"required": false
},
{
"id": "valkyrien_warium:light_blue_control_surface",
"required": false
},
{
"id": "valkyrien_warium:light_blue_control_surface_offset_top",
"required": false
},
{
"id": "valkyrien_warium:light_blue_control_surface_offset_bottom",
"required": false
},
{
"id": "valkyrien_warium:pink_control_surface",
"required": false
},
{
"id": "valkyrien_warium:pink_control_surface_offset_top",
"required": false
},
{
"id": "valkyrien_warium:pink_control_surface_offset_bottom",
"required": false
},
{
"id": "valkyrien_warium:magenta_control_surface",
"required": false
},
{
"id": "valkyrien_warium:magenta_control_surface_offset_top",
"required": false
},
{
"id": "valkyrien_warium:magenta_control_surface_offset_bottom",
"required": false
},
{
"id": "valkyrien_warium:purple_control_surface",
"required": false
},
{
"id": "valkyrien_warium:purple_control_surface_offset_top",
"required": false
},
{
"id": "valkyrien_warium:purple_control_surface_offset_bottom",
"required": false
},
{
"id": "valkyrien_warium:white_control_surface",
"required": false
},
{
"id": "valkyrien_warium:white_control_surface_offset_top",
"required": false
},
{
"id": "valkyrien_warium:white_control_surface_offset_bottom",
"required": false
},
{
"id": "valkyrien_warium:black_control_surface",
"required": false
},
{
"id": "valkyrien_warium:black_control_surface_offset_top",
"required": false
},
{
"id": "valkyrien_warium:black_control_surface_offset_bottom",
"required": false
},
{
"id": "valkyrien_warium:green_control_surface_offset_top",
"required": false
}
]
}