feat(flight): add aspect ratio lift and altitude-hold trim
- Derive lift slope and induced drag from each wing's aspect ratio, found by grouping connected wing blocks across short span gaps. - Give stabilizers and vertical wings their own parasitic drag from the stabilizerDrag config. - Replace designSpeed with a fixed lift constant and a liftScale multiplier. - Make auto trim hold the altitude the craft has when the pilot releases pitch, using nested altitude, climb and attitude loops. - Add /improvedaerodynamics (alias /aero) to list, read and set the main config values in game.
This commit is contained in:
1 parent
4b7fb0e1e8
commit
599ecae21f
10 files changed
+329
-48
No files matched your search
@@ -0,0 +1,91 @@
|
||||
package dev.lumareth.improvedAerodynamics;
|
||||
|
||||
import com.mojang.brigadier.arguments.ArgumentType;
|
||||
import com.mojang.brigadier.arguments.BoolArgumentType;
|
||||
import com.mojang.brigadier.arguments.DoubleArgumentType;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.tree.LiteralCommandNode;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.commands.Commands;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraftforge.common.ForgeConfigSpec;
|
||||
import net.minecraftforge.event.RegisterCommandsEvent;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* /improvedaerodynamics (or /aero) to read and change the most important server config values in game.
|
||||
* {@code /aero} lists them, {@code /aero <option>} shows one, {@code /aero <option> <value>} sets and saves it.
|
||||
* All of them are read live, so changes apply immediately.
|
||||
*/
|
||||
public final class ConfigCommands {
|
||||
private record Option<T>(String name, ForgeConfigSpec.ConfigValue<T> value, ArgumentType<T> argument, Class<T> type) {
|
||||
}
|
||||
|
||||
private static final List<Option<?>> OPTIONS = List.of(
|
||||
new Option<>("realistic_flight_model", Config.IMPROVED_FLIGHT_MODEL, BoolArgumentType.bool(), Boolean.class),
|
||||
new Option<>("smooth_force_delivery", Config.SMOOTH_FORCE_DELIVERY, BoolArgumentType.bool(), Boolean.class),
|
||||
new Option<>("physics_rate_aerodynamics", Config.PHYSICS_RATE_AERODYNAMICS, BoolArgumentType.bool(), Boolean.class),
|
||||
new Option<>("control_smoothing", Config.CONTROL_SMOOTHING, BoolArgumentType.bool(), Boolean.class),
|
||||
new Option<>("implicit_damping", Config.IMPLICIT_DAMPING, BoolArgumentType.bool(), Boolean.class),
|
||||
new Option<>("lift_scale", Config.LIFT_SCALE, DoubleArgumentType.doubleArg(), Double.class),
|
||||
new Option<>("stall_angle_scale", Config.STALL_ANGLE_SCALE, DoubleArgumentType.doubleArg(), Double.class),
|
||||
new Option<>("stabilizer_drag", Config.STABILIZER_DRAG, DoubleArgumentType.doubleArg(), Double.class),
|
||||
new Option<>("trim_authority", Config.TRIM_AUTHORITY, DoubleArgumentType.doubleArg(), Double.class),
|
||||
new Option<>("trim_min_speed", Config.TRIM_MIN_SPEED, DoubleArgumentType.doubleArg(), Double.class),
|
||||
new Option<>("air_placeholder", Config.ENABLE_AIR_PLACEHOLDER, BoolArgumentType.bool(), Boolean.class),
|
||||
new Option<>("lookahead_seconds", Config.LOOKAHEAD_SECONDS, DoubleArgumentType.doubleArg(), Double.class));
|
||||
|
||||
private ConfigCommands() {
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onRegisterCommands(RegisterCommandsEvent event) {
|
||||
LiteralArgumentBuilder<CommandSourceStack> root = Commands.literal("improvedaerodynamics")
|
||||
.requires(source -> source.hasPermission(2))
|
||||
.executes(context -> list(context.getSource()));
|
||||
for (Option<?> option : OPTIONS) {
|
||||
root.then(optionNode(option));
|
||||
}
|
||||
LiteralCommandNode<CommandSourceStack> node = event.getDispatcher().register(root);
|
||||
event.getDispatcher().register(Commands.literal("aero")
|
||||
.requires(source -> source.hasPermission(2))
|
||||
.executes(context -> list(context.getSource()))
|
||||
.redirect(node));
|
||||
}
|
||||
|
||||
private static <T> LiteralArgumentBuilder<CommandSourceStack> optionNode(Option<T> option) {
|
||||
return Commands.literal(option.name())
|
||||
.executes(context -> {
|
||||
context.getSource().sendSuccess(() -> Component.literal(option.name() + " = " + option.value().get()), false);
|
||||
return 1;
|
||||
})
|
||||
.then(Commands.argument("value", option.argument())
|
||||
.executes(context -> set(context.getSource(), option, context.getArgument("value", option.type()))));
|
||||
}
|
||||
|
||||
private static <T> int set(CommandSourceStack source, Option<T> option, T newValue) {
|
||||
ForgeConfigSpec.ValueSpec spec = Config.SPEC.getSpec().get(option.value().getPath());
|
||||
if (!spec.test(newValue)) {
|
||||
ForgeConfigSpec.Range<?> range = spec.getRange();
|
||||
source.sendFailure(Component.literal(range != null
|
||||
? option.name() + " must be between " + range.getMin() + " and " + range.getMax()
|
||||
: "Invalid value for " + option.name()));
|
||||
return 0;
|
||||
}
|
||||
option.value().set(newValue);
|
||||
option.value().save();
|
||||
source.sendSuccess(() -> Component.literal(option.name() + " set to " + newValue), true);
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int list(CommandSourceStack source) {
|
||||
StringBuilder text = new StringBuilder("Improved Aerodynamics settings:");
|
||||
for (Option<?> option : OPTIONS) {
|
||||
text.append("\n ").append(option.name()).append(" = ").append(option.value().get());
|
||||
}
|
||||
source.sendSuccess(() -> Component.literal(text.toString()), false);
|
||||
return OPTIONS.size();
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user