diff --git a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/English.kt b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/English.kt index 763d8ccff..3c970a6d5 100644 --- a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/English.kt +++ b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/English.kt @@ -395,6 +395,7 @@ private fun misc(provider: MatteryLanguageProvider) { misc("android_station.research.low_power", "Android Station is low on power! Can't research!") misc("android_station.research.researched", "Researched!") misc("android_station.research.can_be_researched", "Ready to research!") + misc("android_station.research.can_not_afford", "Missing required items!") misc("android_station.research.can_not_be_researched", "Can't research!") misc("android_station.research.xp_cost", "Experience cost: %s levels") misc("android_station.research.item", "Requires %s x%s") diff --git a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/Russian.kt b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/Russian.kt index f8575df20..6781d647a 100644 --- a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/Russian.kt +++ b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/lang/Russian.kt @@ -403,6 +403,7 @@ private fun misc(provider: MatteryLanguageProvider) { misc("android_station.research.low_power", "Мало питания у станции андроидов! Исследования недоступны!") misc("android_station.research.researched", "Исследовано!") misc("android_station.research.can_be_researched", "Готово к исследованию!") + misc("android_station.research.can_not_afford", "Не хватает предметов!") misc("android_station.research.can_not_be_researched", "Нельзя исследовать!") misc("android_station.research.xp_cost", "Требуется %s уровней Опыта") misc("android_station.research.item", "Требует %s %s шт.") diff --git a/src/main/java/ru/dbotthepony/mc/otm/OverdriveThatMatters.java b/src/main/java/ru/dbotthepony/mc/otm/OverdriveThatMatters.java index 77e20d762..853a6188d 100644 --- a/src/main/java/ru/dbotthepony/mc/otm/OverdriveThatMatters.java +++ b/src/main/java/ru/dbotthepony/mc/otm/OverdriveThatMatters.java @@ -58,6 +58,7 @@ import ru.dbotthepony.mc.otm.matter.IMatterFunction; import ru.dbotthepony.mc.otm.matter.MatterManager; import ru.dbotthepony.mc.otm.network.*; import ru.dbotthepony.mc.otm.registry.*; +import ru.dbotthepony.mc.otm.server.MCommands; import ru.dbotthepony.mc.otm.storage.StorageStack; import ru.dbotthepony.mc.otm.triggers.KillAsAndroidTrigger; import top.theillusivec4.curios.api.CuriosApi; @@ -127,6 +128,7 @@ public final class OverdriveThatMatters { MArmorMaterials.INSTANCE.register(bus); MCriteriaTriggers.INSTANCE.register(bus); MStats.INSTANCE.register(bus); + CommandArgumentTypes.INSTANCE.register(bus); StorageStack.Companion.register(bus); MatteryChestMenu.Companion.register(bus); @@ -197,7 +199,6 @@ public final class OverdriveThatMatters { EVENT_BUS.addListener(EventPriority.NORMAL, MatteryPlayer.Companion::onPlayerSpawnPhantoms); EVENT_BUS.addListener(EventPriority.NORMAL, MatteryPlayer.Companion::onStartTracking); EVENT_BUS.addListener(EventPriority.NORMAL, MatteryPlayer.Companion::onStopTracking); - EVENT_BUS.addListener(EventPriority.NORMAL, MatteryPlayer.Companion::addCommands); EVENT_BUS.addListener(EventPriority.NORMAL, QuantumBatteryItem.Companion::tick); EVENT_BUS.addListener(EventPriority.LOWEST, PortableCondensationDriveItem.Companion::onPickupEvent); @@ -228,6 +229,8 @@ public final class OverdriveThatMatters { EVENT_BUS.addListener(EventPriority.NORMAL, DevChestBlockEntity.Companion::mappingsChanged); + EVENT_BUS.addListener(EventPriority.NORMAL, MCommands.INSTANCE::register); + if (ModList.get().isLoaded(CuriosApi.MODID)) { EVENT_BUS.addListener(EventPriority.NORMAL, CuriosCompatKt::onCuriosSlotModifiersUpdated); } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/android/AndroidResearch.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/android/AndroidResearch.kt index b1f3a113f..b9f01f739 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/android/AndroidResearch.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/android/AndroidResearch.kt @@ -173,17 +173,9 @@ class AndroidResearch(val type: AndroidResearchType, val capability: MatteryPlay return true } - val canResearch: Boolean get() { - if (!consumeResearchCost(simulate = true)) { - return false - } - - if (!isPrerequisitesResearched || (!ply.isCreative && isAnyBlockerResearched)) { - return false - } - - return true - } + val canResearch: Boolean get() = !(!isPrerequisitesResearched || (!ply.isCreative && isAnyBlockerResearched)) + val canAfford: Boolean get() = consumeResearchCost(simulate = true) + val readyToResearch: Boolean get() = canResearch && canAfford /** * Determines if this research is already blocked directly by another research. @@ -246,12 +238,12 @@ class AndroidResearch(val type: AndroidResearchType, val capability: MatteryPlay return true } - fun research(force: Boolean = false): Boolean { + fun research(force: Boolean = false, free: Boolean = false): Boolean { if (isResearched) { return false } - if (force || canResearch && consumeResearchCost(false)) { + if (force || canResearch && (free || canAfford && consumeResearchCost(false))) { onResearched() isResearched = true diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/MatteryPlayer.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/MatteryPlayer.kt index 52f63fc06..32492040e 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/capability/MatteryPlayer.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/capability/MatteryPlayer.kt @@ -8,8 +8,6 @@ import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap import net.minecraft.ChatFormatting import net.minecraft.client.model.PlayerModel import net.minecraft.client.player.AbstractClientPlayer -import net.minecraft.commands.Commands -import net.minecraft.commands.arguments.EntityArgument import net.minecraft.core.HolderLookup import net.minecraft.core.registries.BuiltInRegistries import net.minecraft.nbt.CompoundTag @@ -46,7 +44,6 @@ import net.neoforged.bus.api.Event import net.neoforged.bus.api.ICancellableEvent import net.neoforged.neoforge.common.CommonHooks import net.neoforged.neoforge.common.NeoForge -import net.neoforged.neoforge.event.RegisterCommandsEvent import net.neoforged.neoforge.event.entity.living.LivingDeathEvent import net.neoforged.neoforge.event.entity.living.LivingIncomingDamageEvent import net.neoforged.neoforge.event.entity.living.MobEffectEvent @@ -54,7 +51,6 @@ import net.neoforged.neoforge.event.entity.player.PlayerEvent import net.neoforged.neoforge.event.entity.player.PlayerSpawnPhantomsEvent import net.neoforged.neoforge.event.tick.PlayerTickEvent import net.neoforged.neoforge.network.PacketDistributor -import net.neoforged.neoforge.server.command.EnumArgument import org.apache.logging.log4j.LogManager import org.joml.Vector4f import ru.dbotthepony.kommons.collect.ListenableMap @@ -1412,76 +1408,6 @@ class MatteryPlayer(val ply: Player) { companion object { private val offhandSlotRange = IntSet.of(40) - private fun setExoPack(players: Collection, hasExoPack: Boolean): Int { - for (player in players) { - player.matteryPlayer.hasExopack = hasExoPack - player.matteryPlayer._exoPackMenu = null - } - - return players.size - } - - private fun makeAndroid(players: Collection): Int { - for (player in players) { - player.matteryPlayer.becomeAndroid() - } - - return players.size - } - - private fun makeHuman(players: Collection): Int { - for (player in players) { - player.matteryPlayer.becomeHumane() - } - - return players.size - } - - private fun setUpgrade(players: Collection, type: UpgradeType, state: Boolean): Int { - for (player in players) { - player.matteryPlayer.let { type.prop.set(it, state) } - } - - return players.size - } - - fun addCommands(event: RegisterCommandsEvent) { - event.dispatcher.register( - Commands.literal("exopack") - .requires { it.hasPermission(Commands.LEVEL_GAMEMASTERS) } - .then(Commands.literal("add") - .executes { setExoPack(listOf(it.source.playerOrException), true) } - .then(Commands.argument("targets", EntityArgument.players()).executes { setExoPack(EntityArgument.getPlayers(it, "targets"), true) }) - ) - .then(Commands.literal("remove") - .executes { setExoPack(listOf(it.source.playerOrException), false) } - .then(Commands.argument("targets", EntityArgument.players()).executes { setExoPack(EntityArgument.getPlayers(it, "targets"), false) }) - ) - .then(Commands.literal("upgrade") - .then(Commands.argument("type", EnumArgument.enumArgument(UpgradeType::class.java)) - .then(Commands.literal("add") - .executes { setUpgrade(listOf(it.source.playerOrException), it.getArgument("type", UpgradeType::class.java), true) } - .then(Commands.argument("targets", EntityArgument.players()).executes { setUpgrade(EntityArgument.getPlayers(it, "targets"), it.getArgument("type", UpgradeType::class.java), true) })) - .then(Commands.literal("remove") - .executes { setUpgrade(listOf(it.source.playerOrException), it.getArgument("type", UpgradeType::class.java), false) } - .then(Commands.argument("targets", EntityArgument.players()).executes { setUpgrade(EntityArgument.getPlayers(it, "targets"), it.getArgument("type", UpgradeType::class.java), false) }))) - ) - ) - - event.dispatcher.register( - Commands.literal("android") - .requires { it.hasPermission(Commands.LEVEL_GAMEMASTERS) } - .then(Commands.literal("on") - .executes { makeAndroid(listOf(it.source.playerOrException)) } - .then(Commands.argument("targets", EntityArgument.players()).executes { makeAndroid(EntityArgument.getPlayers(it, "targets")) }) - ) - .then(Commands.literal("off") - .executes { makeHuman(listOf(it.source.playerOrException)) } - .then(Commands.argument("targets", EntityArgument.players()).executes { makeHuman(EntityArgument.getPlayers(it, "targets")) }) - ) - ) - } - init { WitherBoss.TARGETING_CONDITIONS.selector(WitherBoss.TARGETING_CONDITIONS.selector!!.and { it.matteryPlayer?.isAndroid != true }) WitherBoss.LIVING_ENTITY_SELECTOR = WitherBoss.LIVING_ENTITY_SELECTOR.and { it.matteryPlayer?.isAndroid != true } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/tech/AndroidStationScreen.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/tech/AndroidStationScreen.kt index c17dac55c..6dae4bd61 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/tech/AndroidStationScreen.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/tech/AndroidStationScreen.kt @@ -288,7 +288,7 @@ private class AndroidResearchButton( } override val cursorType: CursorType - get() = if (node.isAnyBlockerResearchedIndirect && !(parent?.screen as AndroidStationScreen).menu.player.isCreative) CursorType.NOT_ALLOWED else if (node.canResearch && !node.isResearched) CursorType.HAND else CursorType.ARROW + get() = if (node.isAnyBlockerResearchedIndirect && !(parent?.screen as AndroidStationScreen).menu.player.isCreative) CursorType.NOT_ALLOWED else if (node.readyToResearch && !node.isResearched) CursorType.HAND else CursorType.ARROW override fun innerRender(graphics: MGUIGraphics, mouseX: Float, mouseY: Float, partialTick: Float) { val hovered = screen.hoveredResearch @@ -303,7 +303,7 @@ private class AndroidResearchButton( AndroidStationScreen.RESEARCHED } else if (node.isAnyBlockerResearchedIndirect) { AndroidStationScreen.ALREADY_BLOCKED - } else if (node.canResearch) { + } else if (node.readyToResearch) { AndroidStationScreen.CAN_BE_RESEARCHED } else { AndroidStationScreen.CAN_NOT_BE_RESEARCHED @@ -388,7 +388,7 @@ private class AndroidResearchButton( override fun mouseClickedInner(x: Double, y: Double, button: Int): Boolean { if (button == InputConstants.MOUSE_BUTTON_LEFT && minecraft.player?.isSpectator != true) { - if (node.canResearch && !node.isResearched && (parent?.screen as AndroidStationScreen).menu.energyWidget.level >= MachinesConfig.AndroidStation.ENERGY_PER_RESEARCH) { + if (node.readyToResearch && !node.isResearched && (parent?.screen as AndroidStationScreen).menu.energyWidget.level >= MachinesConfig.AndroidStation.ENERGY_PER_RESEARCH) { if (node.type.flatBlocking.isNotEmpty()) { queryUser( TranslatableComponent("otm.android_station.research.confirm", node.type.displayName), @@ -420,14 +420,16 @@ private class AndroidResearchButton( if (node.isResearched) { list.add(TranslatableComponent("otm.android_station.research.researched").withStyle(ChatFormatting.DARK_AQUA)) - } else if (node.canResearch) { - list.add(TranslatableComponent("otm.android_station.research.can_be_researched").withStyle(ChatFormatting.DARK_GREEN)) - - if (!enoughPower) { - list.add(TranslatableComponent("otm.android_station.research.low_power").withStyle(ChatFormatting.DARK_RED)) - } } else { - list.add(TranslatableComponent("otm.android_station.research.can_not_be_researched").withStyle(ChatFormatting.DARK_RED)) + if (node.canResearch) { + if (node.canAfford) { + list.add(TranslatableComponent("otm.android_station.research.can_be_researched").withStyle(ChatFormatting.DARK_GREEN)) + } else { + list.add(TranslatableComponent("otm.android_station.research.can_not_afford").withStyle(ChatFormatting.DARK_RED)) + } + } else { + list.add(TranslatableComponent("otm.android_station.research.can_not_be_researched").withStyle(ChatFormatting.DARK_RED)) + } if (!enoughPower) { list.add(TranslatableComponent("otm.android_station.research.low_power").withStyle(ChatFormatting.DARK_RED)) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/CommandArgumentTypes.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/CommandArgumentTypes.kt new file mode 100644 index 000000000..5c6e73824 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/CommandArgumentTypes.kt @@ -0,0 +1,20 @@ +package ru.dbotthepony.mc.otm.registry + +import net.minecraft.commands.synchronization.ArgumentTypeInfo +import net.minecraft.commands.synchronization.ArgumentTypeInfos +import net.minecraft.commands.synchronization.SingletonArgumentInfo +import net.minecraft.core.registries.Registries +import net.neoforged.bus.api.IEventBus +import ru.dbotthepony.mc.otm.server.command.AndroidResearchArgument + +object CommandArgumentTypes { + private val registry: MDeferredRegister> = MDeferredRegister(Registries.COMMAND_ARGUMENT_TYPE) + + val ANDROID_RESEARCH by registry.register("android_research") { + ArgumentTypeInfos.registerByClass(AndroidResearchArgument::class.java, SingletonArgumentInfo.contextFree(AndroidResearchArgument.Companion::create)) + } + + fun register(bus: IEventBus) { + registry.register(bus) + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/server/MCommands.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/server/MCommands.kt new file mode 100644 index 000000000..8b911edb9 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/server/MCommands.kt @@ -0,0 +1,11 @@ +package ru.dbotthepony.mc.otm.server + +import net.neoforged.neoforge.event.RegisterCommandsEvent +import ru.dbotthepony.mc.otm.server.command.* + +object MCommands { + fun register(event: RegisterCommandsEvent) { + ExopackCommand.register(event) + AndroidCommand.register(event) + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/server/command/AndroidCommand.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/server/command/AndroidCommand.kt new file mode 100644 index 000000000..44a14eb34 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/server/command/AndroidCommand.kt @@ -0,0 +1,125 @@ +package ru.dbotthepony.mc.otm.server.command + +import net.minecraft.commands.Commands +import net.minecraft.commands.arguments.EntityArgument +import net.minecraft.world.entity.player.Player +import net.neoforged.neoforge.event.RegisterCommandsEvent +import ru.dbotthepony.mc.otm.android.AndroidResearchManager +import ru.dbotthepony.mc.otm.android.AndroidResearchType +import ru.dbotthepony.mc.otm.capability.matteryPlayer + +object AndroidCommand { + fun register(event: RegisterCommandsEvent) { + event.dispatcher.register( + Commands.literal("android") + .requires { it.hasPermission(Commands.LEVEL_GAMEMASTERS) } + .then(Commands.literal("on") + .executes { makeAndroid(listOf(it.source.playerOrException)) } + .then(Commands.argument("targets", EntityArgument.players()).executes { makeAndroid(EntityArgument.getPlayers(it, "targets")) }) + ) + .then(Commands.literal("off") + .executes { makeHuman(listOf(it.source.playerOrException)) } + .then(Commands.argument("targets", EntityArgument.players()).executes { makeHuman(EntityArgument.getPlayers(it, "targets")) }) + ) + .then( + Commands.literal("research") + .then( + Commands.literal("grant") + .then( + Commands.argument("targets", EntityArgument.players()) + .then( + Commands.argument("research", AndroidResearchArgument.create()) + .executes { + grantResearch(EntityArgument.getPlayers(it, "targets"), AndroidResearchArgument.getResearchType(it, "research")) + } + .then( + Commands.literal("force") + .executes { + grantResearch(EntityArgument.getPlayers(it, "targets"), AndroidResearchArgument.getResearchType(it, "research"), true) + } + ) + ) + .then( + Commands.literal("*") + .executes { + val players = EntityArgument.getPlayers(it, "targets") + AndroidResearchManager.researchMap.map { a -> a.value }.forEach { a -> + grantResearch(players, a) + } + + players.size + } + .then( + Commands.literal("force") + .executes { + val players = EntityArgument.getPlayers(it, "targets") + AndroidResearchManager.researchMap.map { a -> a.value }.forEach { a -> + grantResearch(players, a, true) + } + + players.size + } + ) + ) + ) + ) + .then( + Commands.literal("revoke") + .then( + Commands.argument("targets", EntityArgument.players()) + .then( + Commands.argument("research", AndroidResearchArgument.create()) + .executes { + revokeResearch(EntityArgument.getPlayers(it, "targets"), AndroidResearchArgument.getResearchType(it, "research")) + } + ) + .then( + Commands.literal("*") + .executes { + val players = EntityArgument.getPlayers(it, "targets") + AndroidResearchManager.researchMap.map { a -> a.value }.forEach { a -> + revokeResearch(players, a) + } + + players.size + } + ) + ) + + ) + ) + ) + } + + private fun grantResearch(players: Collection, type: AndroidResearchType, force: Boolean = false): Int { + for (player in players) { + player.matteryPlayer.getResearch(type).research(force, free = true) + } + + return players.size + } + + private fun revokeResearch(players: Collection, type: AndroidResearchType): Int { + for (player in players) { + player.matteryPlayer.getResearch(type).unResearch() + } + + return players.size + } + + private fun makeAndroid(players: Collection): Int { + for (player in players) { + player.matteryPlayer.becomeAndroid() + } + + return players.size + } + + private fun makeHuman(players: Collection): Int { + for (player in players) { + player.matteryPlayer.becomeHumane() + } + + return players.size + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/server/command/AndroidResearchArgument.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/server/command/AndroidResearchArgument.kt new file mode 100644 index 000000000..16b9cebde --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/server/command/AndroidResearchArgument.kt @@ -0,0 +1,31 @@ +package ru.dbotthepony.mc.otm.server.command + +import com.mojang.brigadier.StringReader +import com.mojang.brigadier.arguments.ArgumentType +import com.mojang.brigadier.context.CommandContext +import com.mojang.brigadier.suggestion.Suggestions +import com.mojang.brigadier.suggestion.SuggestionsBuilder +import net.minecraft.commands.CommandSourceStack +import net.minecraft.commands.SharedSuggestionProvider +import net.minecraft.resources.ResourceLocation +import ru.dbotthepony.mc.otm.android.AndroidResearchManager +import ru.dbotthepony.mc.otm.android.AndroidResearchType +import java.util.concurrent.CompletableFuture + +class AndroidResearchArgument : ArgumentType { + override fun parse(reader: StringReader): AndroidResearchType { + return AndroidResearchManager[ResourceLocation.read(reader)] ?: throw NoSuchElementException() + } + + override fun listSuggestions(context: CommandContext, builder: SuggestionsBuilder): CompletableFuture { + return SharedSuggestionProvider.suggest(AndroidResearchManager.researchMap.map { it.key.toString() }, builder) + } + + companion object { + fun create(): AndroidResearchArgument = AndroidResearchArgument() + + fun getResearchType(context: CommandContext, name: String): AndroidResearchType { + return context.getArgument(name, AndroidResearchType::class.java) + } + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/server/command/ExopackCommand.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/server/command/ExopackCommand.kt new file mode 100644 index 000000000..943283326 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/server/command/ExopackCommand.kt @@ -0,0 +1,54 @@ +package ru.dbotthepony.mc.otm.server.command + +import net.minecraft.commands.Commands +import net.minecraft.commands.arguments.EntityArgument +import net.minecraft.world.entity.player.Player +import net.neoforged.neoforge.event.RegisterCommandsEvent +import net.neoforged.neoforge.server.command.EnumArgument +import ru.dbotthepony.mc.otm.capability.MatteryPlayer.UpgradeType +import ru.dbotthepony.mc.otm.capability.matteryPlayer + +object ExopackCommand { + fun register(event: RegisterCommandsEvent) { + event.dispatcher.register( + Commands.literal("exopack") + .requires { it.hasPermission(Commands.LEVEL_GAMEMASTERS) } + .then(Commands.literal("add") + .executes { setExoPack(listOf(it.source.playerOrException), true) } + .then(Commands.argument("targets", EntityArgument.players()).executes { setExoPack(EntityArgument.getPlayers(it, "targets"), true) }) + ) + .then(Commands.literal("remove") + .executes { setExoPack(listOf(it.source.playerOrException), false) } + .then(Commands.argument("targets", EntityArgument.players()).executes { setExoPack(EntityArgument.getPlayers(it, "targets"), false) }) + ) + .then(Commands.literal("upgrade") + .then(Commands.argument("type", EnumArgument.enumArgument(UpgradeType::class.java)) + .then(Commands.literal("add") + .executes { setUpgrade(listOf(it.source.playerOrException), it.getArgument("type", UpgradeType::class.java), true) } + .then(Commands.argument("targets", EntityArgument.players()).executes { setUpgrade( + EntityArgument.getPlayers(it, "targets"), it.getArgument("type", UpgradeType::class.java), true) })) + .then(Commands.literal("remove") + .executes { setUpgrade(listOf(it.source.playerOrException), it.getArgument("type", UpgradeType::class.java), false) } + .then(Commands.argument("targets", EntityArgument.players()).executes { setUpgrade( + EntityArgument.getPlayers(it, "targets"), it.getArgument("type", UpgradeType::class.java), false) }))) + ) + ) + } + + private fun setExoPack(players: Collection, hasExoPack: Boolean): Int { + for (player in players) { + player.matteryPlayer.hasExopack = hasExoPack + // player.matteryPlayer.recreateExoPackMenu() + } + + return players.size + } + + private fun setUpgrade(players: Collection, type: UpgradeType, state: Boolean): Int { + for (player in players) { + player.matteryPlayer.let { type.prop.set(it, state) } + } + + return players.size + } +}