Compare commits
1
Commits
master
...
feature/flaps
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1be7e90ec2 |
No files matched your search
+1
-1
@@ -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
|
||||
|
||||
@@ -131,6 +131,26 @@ public final class Config {
|
||||
.comment("Auto trim: below this airspeed (blocks per second) the trim holds its current value")
|
||||
.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");
|
||||
|
||||
@@ -57,6 +57,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() {
|
||||
|
||||
@@ -24,6 +24,7 @@ public class ImprovedAerodynamics {
|
||||
MinecraftForge.EVENT_BUS.register(PhysicsBridge.class);
|
||||
MinecraftForge.EVENT_BUS.register(ConfigCommands.class);
|
||||
MinecraftForge.EVENT_BUS.register(ExhaustSync.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);
|
||||
NozzleContent.register(FMLJavaModLoadingContext.get().getModEventBus());
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,6 +54,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;
|
||||
// Torque (ship axes) one unit of pitch input on this node's pitch surfaces makes, measured every substep
|
||||
final Vector3d pitchResponse = new Vector3d();
|
||||
final Vector3d pitchResponseBuilding = new Vector3d();
|
||||
@@ -67,6 +69,7 @@ public final class AeroState implements PhysicsBridge.Publishable {
|
||||
trimIntegral = data.autoTrim() ? data.storedTrim() : 0.0;
|
||||
trimOutput = trimIntegral;
|
||||
savedTrim = trimIntegral;
|
||||
flapAngle = Math.toRadians(FlapSetting.of(data.flapSetting()).degrees());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -222,9 +225,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, 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;
|
||||
@@ -239,7 +243,7 @@ public final class AeroState implements PhysicsBridge.Publishable {
|
||||
probe.clear();
|
||||
double base = 0.0;
|
||||
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);
|
||||
}
|
||||
@@ -247,7 +251,7 @@ public final class AeroState implements PhysicsBridge.Publishable {
|
||||
probe.clear();
|
||||
double nudged = input(s, node, state, 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);
|
||||
}
|
||||
@@ -296,10 +300,25 @@ public final class AeroState implements PhysicsBridge.Publishable {
|
||||
state.pitch = moveTowards(state.pitch, node.pitch(), step);
|
||||
state.yaw = moveTowards(state.yaw, node.yaw(), step);
|
||||
state.roll = moveTowards(state.roll, node.roll(), step);
|
||||
updateFlaps(state, node, worldVelocity.length(), dt);
|
||||
AutoTrim.update(state, node, worldVelocity, altitude, rotation, inertia, w, 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. */
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,5 +33,11 @@ public final class Network {
|
||||
.decoder(ExhaustPacket::decode)
|
||||
.consumerMainThread(ExhaustPacket::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();
|
||||
}
|
||||
}
|
||||
@@ -22,5 +22,16 @@
|
||||
"message.improved_aerodynamics.afterburner.off": "Nachbrenner aus",
|
||||
"message.improved_aerodynamics.afterburner.throttle": "Der Nachbrenner braucht mindestens Schub %s",
|
||||
"message.improved_aerodynamics.afterburner.no_seat": "Setz dich in einen Warium-Steuersitz, um den Nachbrenner zu nutzen",
|
||||
"message.improved_aerodynamics.afterburner.no_node": "Dieser Sitz ist mit keinem Vehicle Control Node verbunden"
|
||||
"message.improved_aerodynamics.afterburner.no_node": "Dieser Sitz ist mit keinem Vehicle Control Node verbunden",
|
||||
"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"
|
||||
}
|
||||
@@ -22,5 +22,16 @@
|
||||
"message.improved_aerodynamics.afterburner.off": "Afterburner off",
|
||||
"message.improved_aerodynamics.afterburner.throttle": "The afterburner needs a throttle of at least %s",
|
||||
"message.improved_aerodynamics.afterburner.no_seat": "Sit in a Warium control seat to use the afterburner",
|
||||
"message.improved_aerodynamics.afterburner.no_node": "This seat is not linked to a vehicle control node"
|
||||
"message.improved_aerodynamics.afterburner.no_node": "This seat is not linked to a vehicle control node",
|
||||
"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
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in new issue
Block a user