mirror of
https://github.com/Sollace/Unicopia.git
synced 2024-11-27 15:17:59 +01:00
Added a /cast command #102
This commit is contained in:
parent
b14e5c0d23
commit
e2ffdd43e5
9 changed files with 270 additions and 19 deletions
|
@ -2,5 +2,4 @@ package com.minelittlepony.unicopia;
|
|||
|
||||
public interface Debug {
|
||||
boolean DEBUG_SPELLBOOK_CHAPTERS = Boolean.getBoolean("unicopia.debug.spellbookChapters");
|
||||
boolean DEBUG_COMMANDS = Boolean.getBoolean("unicopia.debug.commands");
|
||||
}
|
||||
|
|
|
@ -19,6 +19,9 @@ import com.minelittlepony.unicopia.ability.magic.spell.ThrowableSpell;
|
|||
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
|
||||
import com.minelittlepony.unicopia.item.UItems;
|
||||
import com.minelittlepony.unicopia.util.RegistryUtils;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.brigadier.exceptions.DynamicCommandExceptionType;
|
||||
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NbtCompound;
|
||||
|
@ -26,15 +29,20 @@ import net.minecraft.text.Text;
|
|||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.Util;
|
||||
import net.minecraft.registry.Registry;
|
||||
import net.minecraft.registry.RegistryKey;
|
||||
import net.minecraft.registry.tag.FluidTags;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
|
||||
public final class SpellType<T extends Spell> implements Affine, SpellPredicate<T> {
|
||||
public static final Identifier EMPTY_ID = Unicopia.id("none");
|
||||
public static final SpellType<?> EMPTY_KEY = new SpellType<>(EMPTY_ID, Affinity.NEUTRAL, 0xFFFFFF, false, SpellTraits.EMPTY, t -> null);
|
||||
|
||||
public static final Registry<SpellType<?>> REGISTRY = RegistryUtils.createSimple(Unicopia.id("spells"));
|
||||
public static final RegistryKey<? extends Registry<SpellType<?>>> REGISTRY_KEY = REGISTRY.getKey();
|
||||
public static final Map<Affinity, Set<SpellType<?>>> BY_AFFINITY = new EnumMap<>(Affinity.class);
|
||||
|
||||
private static final DynamicCommandExceptionType UNKNOWN_SPELL_TYPE_EXCEPTION = new DynamicCommandExceptionType(id -> Text.translatable("spell_type.unknown", id));
|
||||
|
||||
public static final SpellType<PlaceableSpell> PLACED_SPELL = register("placed", Affinity.NEUTRAL, 0, false, SpellTraits.EMPTY, PlaceableSpell::new);
|
||||
public static final SpellType<ThrowableSpell> THROWN_SPELL = register("thrown", Affinity.NEUTRAL, 0, false, SpellTraits.EMPTY, ThrowableSpell::new);
|
||||
|
||||
|
@ -192,6 +200,11 @@ public final class SpellType<T extends Spell> implements Affine, SpellPredicate<
|
|||
return BY_AFFINITY.computeIfAbsent(affinity, a -> new LinkedHashSet<>());
|
||||
}
|
||||
|
||||
public static SpellType<?> fromArgument(CommandContext<ServerCommandSource> context, String name) throws CommandSyntaxException {
|
||||
Identifier id = context.getArgument(name, RegistryKey.class).getValue();
|
||||
return REGISTRY.getOrEmpty(id).orElseThrow(() -> UNKNOWN_SPELL_TYPE_EXCEPTION.create(id));
|
||||
}
|
||||
|
||||
public interface Factory<T extends Spell> {
|
||||
T create(CustomisedSpellType<T> type);
|
||||
}
|
||||
|
|
|
@ -268,7 +268,11 @@ public final class SpellTraits implements Iterable<Map.Entry<Trait, Float>> {
|
|||
}
|
||||
|
||||
public static Optional<SpellTraits> fromString(String traits) {
|
||||
return fromEntries(Arrays.stream(traits.split(" ")).map(a -> a.split(":")).map(pair -> {
|
||||
return fromString(traits, " ");
|
||||
}
|
||||
|
||||
public static Optional<SpellTraits> fromString(String traits, String delimiter) {
|
||||
return fromEntries(Arrays.stream(traits.split(delimiter)).map(a -> a.split(":")).map(pair -> {
|
||||
Trait key = Trait.fromName(pair[0]).orElse(null);
|
||||
if (key == null) {
|
||||
Unicopia.LOGGER.warn("Skipping unknown trait {}", pair[0]);
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
package com.minelittlepony.unicopia.command;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import com.minelittlepony.unicopia.ability.magic.Caster;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.PlaceableSpell;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.Situation;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.effect.CustomisedSpellType;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
|
||||
import com.minelittlepony.unicopia.entity.CastSpellEntity;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.arguments.FloatArgumentType;
|
||||
import com.mojang.brigadier.builder.ArgumentBuilder;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
|
||||
import net.minecraft.command.CommandRegistryAccess;
|
||||
import net.minecraft.command.argument.BlockPosArgumentType;
|
||||
import net.minecraft.command.argument.EntityArgumentType;
|
||||
import net.minecraft.command.argument.RegistryKeyArgumentType;
|
||||
import net.minecraft.command.argument.RotationArgumentType;
|
||||
import net.minecraft.server.command.CommandManager;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.util.math.Vec2f;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
|
||||
public class CastCommand {
|
||||
public static void register(CommandDispatcher<ServerCommandSource> dispatcher, CommandRegistryAccess registries) {
|
||||
dispatcher.register(
|
||||
CommandManager.literal("cast").requires(s -> s.hasPermissionLevel(2))
|
||||
.then(
|
||||
buildBranches(
|
||||
CommandManager.argument("spell", RegistryKeyArgumentType.registryKey(SpellType.REGISTRY_KEY)),
|
||||
c -> Optional.empty()
|
||||
)
|
||||
)
|
||||
.then(
|
||||
CommandManager.argument("spell", RegistryKeyArgumentType.registryKey(SpellType.REGISTRY_KEY))
|
||||
.then(buildBranches(
|
||||
CommandManager.argument("traits", TraitsArgumentType.traits()),
|
||||
CastCommand::getTraits
|
||||
))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private static ArgumentBuilder<ServerCommandSource, ?> buildBranches(ArgumentBuilder<ServerCommandSource, ?> builder,
|
||||
TraitsFunc traitsFunc) {
|
||||
return builder.then(
|
||||
CommandManager.literal("throw").then(
|
||||
CommandManager.argument("rot", RotationArgumentType.rotation()).executes(c -> thrown(c, traitsFunc, 1.5F)).then(
|
||||
CommandManager.argument("speed", FloatArgumentType.floatArg(0, 10)).executes(c -> thrown(c,
|
||||
traitsFunc,
|
||||
FloatArgumentType.getFloat(c, "speed")
|
||||
))
|
||||
)
|
||||
)
|
||||
)
|
||||
.then(
|
||||
CommandManager.literal("place").executes(c -> placed(c, traitsFunc, Optional.empty(), c.getSource().getRotation())).then(
|
||||
CommandManager.argument("loc", BlockPosArgumentType.blockPos()).executes(c -> placed(c,
|
||||
traitsFunc,
|
||||
Optional.of(BlockPosArgumentType.getBlockPos(c, "location").toCenterPos()),
|
||||
c.getSource().getRotation()
|
||||
)).then(
|
||||
CommandManager.argument("rot", RotationArgumentType.rotation()).executes(c -> placed(c,
|
||||
traitsFunc,
|
||||
Optional.of(BlockPosArgumentType.getBlockPos(c, "location").toCenterPos()),
|
||||
RotationArgumentType.getRotation(c, "rot").toAbsoluteRotation(c.getSource())
|
||||
))
|
||||
)
|
||||
)
|
||||
)
|
||||
.then(
|
||||
CommandManager.literal("apply").then(
|
||||
CommandManager.argument("targets", EntityArgumentType.entities()).executes(c -> apply(c, traitsFunc))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private static Optional<SpellTraits> getTraits(CommandContext<ServerCommandSource> source) throws CommandSyntaxException {
|
||||
return Optional.of(TraitsArgumentType.getSpellTraits(source, "traits"));
|
||||
}
|
||||
|
||||
private static CustomisedSpellType<?> getSpell(CommandContext<ServerCommandSource> source, TraitsFunc traits) throws CommandSyntaxException {
|
||||
SpellType<?> spellType = SpellType.fromArgument(source, "spell");
|
||||
return spellType.withTraits(traits.getTraits(source).orElse(spellType.getTraits()));
|
||||
}
|
||||
|
||||
private static int thrown(CommandContext<ServerCommandSource> source, TraitsFunc traits, float speed) throws CommandSyntaxException {
|
||||
ServerPlayerEntity player = source.getSource().getPlayerOrThrow();
|
||||
getSpell(source, traits).create().toThrowable().throwProjectile(Caster.of(player).orElseThrow()).ifPresent(projectile -> {
|
||||
Vec2f rotation = RotationArgumentType.getRotation(source, "rot").toAbsoluteRotation(source.getSource());
|
||||
projectile.setVelocity(player, rotation.x, rotation.y, 0, speed, 1);
|
||||
});
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static int placed(CommandContext<ServerCommandSource> source, TraitsFunc traits, Optional<Vec3d> position, Vec2f rotation) throws CommandSyntaxException {
|
||||
ServerPlayerEntity player = source.getSource().getPlayerOrThrow();
|
||||
PlaceableSpell spell = getSpell(source, traits).create().toPlaceable();
|
||||
Caster<?> caster = Caster.of(player).orElseThrow();
|
||||
|
||||
spell.setOrientation(rotation.x, rotation.y);
|
||||
spell.apply(caster);
|
||||
|
||||
position.ifPresent(pos -> {
|
||||
spell.tick(caster, Situation.BODY);
|
||||
CastSpellEntity entity = spell.getSpellEntity(caster).orElseThrow();
|
||||
entity.setPosition(pos);
|
||||
});
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static int apply(CommandContext<ServerCommandSource> source, TraitsFunc traits) throws CommandSyntaxException {
|
||||
CustomisedSpellType<?> spellType = getSpell(source, traits);
|
||||
EntityArgumentType.getEntities(source, "targets").forEach(target -> {
|
||||
Caster.of(target).ifPresent(caster -> spellType.apply(caster));
|
||||
});
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
interface TraitsFunc {
|
||||
Optional<SpellTraits> getTraits(CommandContext<ServerCommandSource> source) throws CommandSyntaxException;
|
||||
}
|
||||
}
|
|
@ -2,11 +2,11 @@ package com.minelittlepony.unicopia.command;
|
|||
|
||||
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
|
||||
|
||||
import com.minelittlepony.unicopia.Debug;
|
||||
import com.minelittlepony.unicopia.Unicopia;
|
||||
|
||||
import net.fabricmc.fabric.api.command.v2.ArgumentTypeRegistry;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.minecraft.command.argument.serialize.ConstantArgumentSerializer;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
|
||||
public class Commands {
|
||||
|
@ -17,21 +17,22 @@ public class Commands {
|
|||
EnumArgumentType.class,
|
||||
new EnumArgumentType.Serializer()
|
||||
);
|
||||
CommandRegistrationCallback.EVENT.register((dispatcher, access, environment) -> {
|
||||
ArgumentTypeRegistry.registerArgumentType(
|
||||
Unicopia.id("spell_traits"),
|
||||
TraitsArgumentType.class,
|
||||
ConstantArgumentSerializer.of(TraitsArgumentType::traits)
|
||||
);
|
||||
CommandRegistrationCallback.EVENT.register((dispatcher, registries, environment) -> {
|
||||
RacelistCommand.register(dispatcher);
|
||||
EmoteCommand.register(dispatcher);
|
||||
if (Unicopia.getConfig().enableCheats.get() || environment.dedicated) {
|
||||
SpeciesCommand.register(dispatcher, environment);
|
||||
WorldTribeCommand.register(dispatcher);
|
||||
}
|
||||
if (Unicopia.getConfig().enableCheats.get()) {
|
||||
|
||||
GravityCommand.register(dispatcher);
|
||||
DisguiseCommand.register(dispatcher, access);
|
||||
if (Debug.DEBUG_COMMANDS) {
|
||||
DisguiseCommand.register(dispatcher, registries);
|
||||
CastCommand.register(dispatcher, registries);
|
||||
TraitCommand.register(dispatcher);
|
||||
ManaCommand.register(dispatcher);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (FabricLoader.getInstance().getGameInstance() instanceof MinecraftServer server) {
|
||||
|
|
|
@ -22,7 +22,7 @@ class GravityCommand {
|
|||
static void register(CommandDispatcher<ServerCommandSource> dispatcher) {
|
||||
LiteralArgumentBuilder<ServerCommandSource> builder = CommandManager
|
||||
.literal("gravity")
|
||||
.requires(s -> s.hasPermissionLevel(4));
|
||||
.requires(s -> s.hasPermissionLevel(2));
|
||||
|
||||
builder.then(CommandManager.literal("get")
|
||||
.executes(context -> get(context.getSource(), context.getSource().getPlayer(), true))
|
||||
|
|
|
@ -13,7 +13,7 @@ import net.minecraft.text.Text;
|
|||
public class ManaCommand {
|
||||
static void register(CommandDispatcher<ServerCommandSource> dispatcher) {
|
||||
dispatcher.register(CommandManager
|
||||
.literal("mana")
|
||||
.literal("mana").requires(s -> s.hasPermissionLevel(2))
|
||||
.then(CommandManager.argument("type", EnumArgumentType.of(ManaType.class)).executes(source -> {
|
||||
var type = source.getArgument("type", ManaType.class);
|
||||
var pony = Pony.of(source.getSource().getPlayer());
|
||||
|
|
|
@ -19,7 +19,7 @@ class TraitCommand {
|
|||
static void register(CommandDispatcher<ServerCommandSource> dispatcher) {
|
||||
LiteralArgumentBuilder<ServerCommandSource> builder = CommandManager
|
||||
.literal("trait")
|
||||
.requires(s -> s.hasPermissionLevel(4));
|
||||
.requires(s -> s.hasPermissionLevel(2));
|
||||
|
||||
builder.then(CommandManager.literal("add")
|
||||
.then(CommandManager.argument("trait", EnumArgumentType.of(Trait.class))
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
package com.minelittlepony.unicopia.command;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.arguments.ArgumentType;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||
|
||||
import net.minecraft.text.Text;
|
||||
|
||||
class TraitsArgumentType implements ArgumentType<SpellTraits> {
|
||||
private static final List<String> EXAMPLES = List.of("strength:1,focus:2", "");
|
||||
public static final SimpleCommandExceptionType UNRECOGNISED_TRAIT_EXCEPTION = new SimpleCommandExceptionType(Text.translatable("argument.spell_trait.unrecognised"));
|
||||
|
||||
private TraitsArgumentType() {
|
||||
|
||||
}
|
||||
|
||||
public static TraitsArgumentType traits() {
|
||||
return new TraitsArgumentType();
|
||||
}
|
||||
|
||||
public static <S> SpellTraits getSpellTraits(CommandContext<S> context, String name) {
|
||||
return context.getArgument(name, SpellTraits.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpellTraits parse(StringReader reader) throws CommandSyntaxException {
|
||||
if (!reader.canRead()) {
|
||||
return SpellTraits.EMPTY;
|
||||
}
|
||||
|
||||
SpellTraits.Builder builder = new SpellTraits.Builder();
|
||||
while (reader.canRead() && reader.peek() != ' ') {
|
||||
Trait trait = Trait.fromName(readTraitName(reader)).orElseThrow(() -> UNRECOGNISED_TRAIT_EXCEPTION.createWithContext(reader));
|
||||
reader.expect(':');
|
||||
float value = reader.readFloat();
|
||||
builder.with(trait, value);
|
||||
if (!reader.canRead() || reader.peek() != ',') {
|
||||
break;
|
||||
}
|
||||
reader.skip();
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private String readTraitName(StringReader reader) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
while (reader.canRead() && reader.peek() != ' ' && reader.peek() != ':') {
|
||||
builder.append(reader.read());
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S> CompletableFuture<Suggestions> listSuggestions(CommandContext<S> context, SuggestionsBuilder builder) {
|
||||
String input = builder.getRemaining().toLowerCase(Locale.ROOT);
|
||||
if (input.indexOf(' ') != -1) {
|
||||
return Suggestions.empty();
|
||||
}
|
||||
|
||||
int colonIndex = input.lastIndexOf(':');
|
||||
int commaIndex = Math.max(input.lastIndexOf(','), colonIndex);
|
||||
if (commaIndex > -1) {
|
||||
builder = builder.createOffset(builder.getStart() + commaIndex + 1);
|
||||
input = input.substring(commaIndex + 1, input.length());
|
||||
|
||||
if (commaIndex == colonIndex) {
|
||||
return Suggestions.empty();
|
||||
}
|
||||
}
|
||||
|
||||
final String incomplete = input;
|
||||
System.out.println(incomplete);
|
||||
Trait.all().stream()
|
||||
.map(trait -> trait.name().toLowerCase(Locale.ROOT))
|
||||
.filter(trait -> incomplete.isBlank() || trait.startsWith(incomplete))
|
||||
.forEach(builder::suggest);
|
||||
|
||||
return builder.buildFuture();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getExamples() {
|
||||
return EXAMPLES;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SpellTraits()";
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue