diff --git a/src/main/java/me/ellieis/Sabotage/game/custom/SabotageItems.java b/src/main/java/me/ellieis/Sabotage/game/custom/SabotageItems.java index 0cecb7f..975b068 100644 --- a/src/main/java/me/ellieis/Sabotage/game/custom/SabotageItems.java +++ b/src/main/java/me/ellieis/Sabotage/game/custom/SabotageItems.java @@ -1,17 +1,31 @@ package me.ellieis.Sabotage.game.custom; +import eu.pb4.polymer.core.api.item.PolymerBlockItem; +import eu.pb4.polymer.core.api.item.PolymerItemGroupUtils; import me.ellieis.Sabotage.Sabotage; -import me.ellieis.Sabotage.game.custom.items.SabotageChestItem; +import me.ellieis.Sabotage.game.custom.items.DetectiveShears; +import net.fabricmc.fabric.api.itemgroup.v1.FabricItemGroup; import net.minecraft.item.Item; +import net.minecraft.item.ItemGroup; import net.minecraft.item.Items; import net.minecraft.registry.Registries; import net.minecraft.registry.Registry; +import net.minecraft.text.Text; public class SabotageItems { - public static final Item SABOTAGE_CHEST = new SabotageChestItem(SabotageBlocks.SABOTAGE_CHEST, new Item.Settings(), Items.CHEST); - + public static final Item SABOTAGE_CHEST = new PolymerBlockItem(SabotageBlocks.SABOTAGE_CHEST, new Item.Settings(), Items.CHEST); + public static final Item DETECTIVE_SHEARS = new DetectiveShears(new Item.Settings()); + public static final ItemGroup ITEM_GROUP = FabricItemGroup.builder() + .displayName(Text.translatable("gameType.sabotage.sabotage")) + .icon(SABOTAGE_CHEST::getDefaultStack) + .entries((context, entries) -> { + entries.add(SABOTAGE_CHEST); + }) + .build(); public static void register() { register("sabotage_chest", SABOTAGE_CHEST); + register("detective_shears", DETECTIVE_SHEARS); + PolymerItemGroupUtils.registerPolymerItemGroup(Sabotage.identifier("general"), ITEM_GROUP); } private static T register(String id, T item) { return Registry.register(Registries.ITEM, Sabotage.identifier(id), item); diff --git a/src/main/java/me/ellieis/Sabotage/game/custom/items/DetectiveShears.java b/src/main/java/me/ellieis/Sabotage/game/custom/items/DetectiveShears.java new file mode 100644 index 0000000..218c81a --- /dev/null +++ b/src/main/java/me/ellieis/Sabotage/game/custom/items/DetectiveShears.java @@ -0,0 +1,40 @@ +package me.ellieis.Sabotage.game.custom.items; + +import eu.pb4.polymer.core.api.item.PolymerItem; +import me.ellieis.Sabotage.Sabotage; +import me.ellieis.Sabotage.game.phase.SabotageActive; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.util.ActionResult; +import net.minecraft.util.Hand; +import net.minecraft.world.World; +import org.jetbrains.annotations.Nullable; + +public class DetectiveShears extends Item implements PolymerItem { + public DetectiveShears(Settings settings) { + super(settings); + } + + @Override + public ActionResult useOnEntity(ItemStack item, PlayerEntity plr, LivingEntity entity, Hand hand) { + World world = plr.getEntityWorld(); + if (!world.isClient()) { + for (SabotageActive game : Sabotage.activeGames) { + if (game.getWorld().equals(world)) { + game.testEntity((ServerPlayerEntity) plr, entity); + break; + } + } + } + return ActionResult.FAIL; + } + + @Override + public Item getPolymerItem(ItemStack itemStack, @Nullable ServerPlayerEntity player) { + return Items.SHEARS; + } +} diff --git a/src/main/java/me/ellieis/Sabotage/game/custom/items/SabotageChestItem.java b/src/main/java/me/ellieis/Sabotage/game/custom/items/SabotageChestItem.java deleted file mode 100644 index aabf87a..0000000 --- a/src/main/java/me/ellieis/Sabotage/game/custom/items/SabotageChestItem.java +++ /dev/null @@ -1,11 +0,0 @@ -package me.ellieis.Sabotage.game.custom.items; - -import eu.pb4.polymer.core.api.item.PolymerBlockItem; -import net.minecraft.block.Block; -import net.minecraft.item.Item; - -public class SabotageChestItem extends PolymerBlockItem { - public SabotageChestItem(Block block, Settings settings, Item virtualItem) { - super(block, settings, virtualItem); - } -} diff --git a/src/main/java/me/ellieis/Sabotage/game/phase/SabotageActive.java b/src/main/java/me/ellieis/Sabotage/game/phase/SabotageActive.java index d5a306b..116ebad 100644 --- a/src/main/java/me/ellieis/Sabotage/game/phase/SabotageActive.java +++ b/src/main/java/me/ellieis/Sabotage/game/phase/SabotageActive.java @@ -14,7 +14,11 @@ import me.ellieis.Sabotage.game.map.SabotageMap; import me.ellieis.Sabotage.game.statistics.KarmaManager; import net.minecraft.entity.Entity; +import net.minecraft.entity.LivingEntity; import net.minecraft.entity.damage.DamageSource; +import net.minecraft.entity.effect.StatusEffectInstance; +import net.minecraft.entity.effect.StatusEffects; +import net.minecraft.item.ItemStack; import net.minecraft.network.message.MessageType; import net.minecraft.network.message.SentMessage; import net.minecraft.network.message.SignedMessage; @@ -48,9 +52,18 @@ import xyz.nucleoid.stimuli.event.player.PlayerDeathEvent; import xyz.nucleoid.stimuli.event.player.ReplacePlayerChatEvent; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Consumer; + +import static me.ellieis.Sabotage.game.custom.SabotageItems.DETECTIVE_SHEARS; public class SabotageActive { + private final SabotageConfig config; private final GameSpace gameSpace; private final SabotageMap map; @@ -62,6 +75,7 @@ public class SabotageActive { private PlayerSet initialSaboteurs; private final Map playersOldTeams = new HashMap<>(); private final KarmaManager karmaManager; + private final ArrayList tasks = new ArrayList<>(); private long startTime; private long endTime; private GameActivity activity; @@ -203,8 +217,7 @@ public void pickRoles() { if (sabCount < 1) { sabCount = 1; } - int detCount = playerCount / 8; - + int detCount = playerCount / 2; for (ServerPlayerEntity plr : plrList) { if (detCount >= 1) { detectives.add(plr); @@ -225,6 +238,11 @@ public void pickRoles() { saboteurs.playSound(SoundEvents.ENTITY_ILLUSIONER_CAST_SPELL); saboteurs.playSound(SoundEvents.AMBIENT_SOUL_SAND_VALLEY_MOOD.value()); + // give detectives their portable tester + for (ServerPlayerEntity detective : detectives) { + detective.getInventory().insertStack(new ItemStack(DETECTIVE_SHEARS)); + } + // name tag coloring Scoreboard scoreboard = gameSpace.getServer().getScoreboard(); scoreboard.addTeam("unknown"); @@ -286,6 +304,30 @@ public void changeTeam(ServerPlayerEntity plr, ServerPlayerEntity other, TeamS2C } } } + public void testEntity(ServerPlayerEntity plr, LivingEntity entity) { + Roles role = getPlayerRole(plr); + if (role == Roles.DETECTIVE) { + if (entity.isPlayer()) { + final ServerPlayerEntity playerEntity = (ServerPlayerEntity) entity; + plr.addStatusEffect(new StatusEffectInstance(StatusEffects.BLINDNESS, 40)); + plr.addStatusEffect(new StatusEffectInstance(StatusEffects.SLOWNESS, 100)); + playerEntity.addStatusEffect(new StatusEffectInstance(StatusEffects.BLINDNESS, 100)); + playerEntity.addStatusEffect(new StatusEffectInstance(StatusEffects.SLOWNESS, 200, 3)); + int revealTime = (int) world.getTime() + 200; + Consumer func = (currentTime) -> { + Roles plrRole = getPlayerRole(playerEntity); + gameSpace.getPlayers().sendMessage( + Text.translatable("sabotage.detective_shears_reveal", + playerEntity.getName(), + Text.translatable("sabotage." + ( + (plrRole == Roles.SABOTEUR) ? "saboteur" : + (plrRole == Roles.DETECTIVE) ? "detective" : "innocent") + ).formatted(getRoleColor(plrRole)))); + }; + tasks.add(new Task(revealTime, func)); + } + } + } private Text createAttackerKillMessage(ServerPlayerEntity plr, int karma) { Roles role = getPlayerRole(plr); Formatting victimColor = getRoleColor(role); @@ -562,6 +604,13 @@ public void onTick() { } case ACTIVE -> { + for (int i = 0; i < tasks.size(); i++) { + Task task = tasks.get(i); + if (task.executionTime <= time) { + task.task.accept((int) time); + tasks.remove(task); + } + } if (time % 20 == 0) { // second has passed double timePassed = Math.floor((world.getTime() / 20) - (startTime / 20)) - config.countdownTime() - config.gracePeriod(); @@ -594,3 +643,12 @@ public void onTick() { } } } + +class Task { + final int executionTime; + final Consumer task; + public Task(int executionTime, Consumer task) { + this.executionTime = executionTime; + this.task = task; + } +} \ No newline at end of file diff --git a/src/main/resources/data/sabotage/lang/en_us.json b/src/main/resources/data/sabotage/lang/en_us.json index cd34174..76502f5 100644 --- a/src/main/resources/data/sabotage/lang/en_us.json +++ b/src/main/resources/data/sabotage/lang/en_us.json @@ -1,15 +1,16 @@ { "gameType.sabotage.sabotage": "Sabotage", "block.sabotage.sabotage_chest": "Sabotage Chest", - + "item.sabotage.detective_shears": "Portable Tester", "sabotage.waiting": "Waiting for players.", "sabotage.full": "Game is full.", "sabotage.sidebar.countdown": "Get ready!", - "sabotage.sidebar.grace_period.plural": "You have %s seconds left.", + "sabotage.sidebar.grace_period.plural": " You have %s seconds left.", "sabotage.sidebar.grace_period.singular": "You have %s second left.", "sabotage.sidebar.role": "Your role is: %s", "sabotage.sidebar.role.desc": "Find and kill all %s", "sabotage.sidebar.time_left": "Time left: %02d:%02d", + "sabotage.detective_shears_reveal": "%s was a %s!", "sabotage.game_start": "The game has started! You have %s seconds to collect items, gear, or hide. Your roles will be selected after the grace period.", "sabotage.game_end": "The game has ended! The following players were saboteurs: %s", "sabotage.game_end.innocents": "Congratulations to all alive %s and %s for figuring out who the %s were.",