Add vectored afterburner nozzle with afterburner key and exhaust style

- Add vectored afterburner nozzle block with AzureLib model, deflection, throttle and afterburner state
- Drive nozzles from the vehicle control node and map pitch, roll and yaw to differential deflection
- Add afterburner toggle key and packet, linked to the control node
- Replace Warium exhaust effect entities with improved flames, particles and sounds when exhaustStyle is improved
- Add /aero seedgen to measure the air ceiling for the air placeholder
- Upgrade Warium VS to 1.1.7 and make the lift use horizontal speed
This commit is contained in:
richard committed 2026-11-11 18:00:46 +01:00
1 parent 1e659816b8
commit 70eb237d70
56 files changed
+2197 -12

No files matched your search

@@ -3,10 +3,13 @@ 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.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.tree.LiteralCommandNode;
import dev.lumareth.improvedAerodynamics.exhaust.NozzleCommands;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.commands.SharedSuggestionProvider;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer;
import net.minecraftforge.common.ForgeConfigSpec;
@@ -22,7 +25,11 @@ import java.util.List;
* 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 record Option<T>(String name, ForgeConfigSpec.ConfigValue<T> value, ArgumentType<T> argument, Class<T> type,
List<String> choices) {
Option(String name, ForgeConfigSpec.ConfigValue<T> value, ArgumentType<T> argument, Class<T> type) {
this(name, value, argument, type, List.of());
}
}
private static final List<Option<?>> OPTIONS = List.of(
@@ -44,6 +51,12 @@ public final class ConfigCommands {
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<>("nozzle_max_deflection", Config.NOZZLE_MAX_DEFLECTION, DoubleArgumentType.doubleArg(), Double.class),
new Option<>("nozzle_slew_rate", Config.NOZZLE_SLEW_RATE, DoubleArgumentType.doubleArg(), Double.class),
new Option<>("afterburner_min_throttle", Config.AFTERBURNER_MIN_THROTTLE, DoubleArgumentType.doubleArg(), Double.class),
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<>("lookahead_seconds", Config.LOOKAHEAD_SECONDS, DoubleArgumentType.doubleArg(), Double.class));
private ConfigCommands() {
@@ -66,6 +79,8 @@ public final class ConfigCommands {
for (Option<?> option : OPTIONS) {
root.then(optionNode(option));
}
root.then(SeedgenCommands.node());
root.then(NozzleCommands.node());
LiteralCommandNode<CommandSourceStack> node = event.getDispatcher().register(root);
event.getDispatcher().register(Commands.literal("aero")
.requires(ConfigCommands::mayUse)
@@ -76,10 +91,12 @@ public final class ConfigCommands {
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);
context.getSource().sendSuccess(() -> Component.literal(option.name() + " = " + option.value().get()
+ (option.choices().isEmpty() ? "" : " (" + String.join(" or ", option.choices()) + ")")), false);
return 1;
})
.then(Commands.argument("value", option.argument())
.suggests((context, builder) -> SharedSuggestionProvider.suggest(option.choices(), builder))
.executes(context -> set(context.getSource(), option, context.getArgument("value", option.type()))));
}
@@ -89,7 +106,8 @@ public final class ConfigCommands {
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()));
: option.choices().isEmpty() ? "Invalid value for " + option.name()
: option.name() + " must be " + String.join(" or ", option.choices())));
return 0;
}
option.value().set(newValue);