diff --git a/build.gradle b/build.gradle index 7858115..80cc778 100644 --- a/build.gradle +++ b/build.gradle @@ -126,7 +126,6 @@ dependencies { // Example mod dependency using a file as dependency // implementation files("libs/coolmod-${mc_version}-${coolmod_version}.jar") - // Example project dependency using a sister or child project: // implementation project(":myproject") diff --git a/gradle.properties b/gradle.properties index fc5b2a0..420a533 100644 --- a/gradle.properties +++ b/gradle.properties @@ -34,7 +34,7 @@ mod_name=Stalinium # The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default. mod_license=MIT # The mod version. See https://semver.org/ -mod_version=0.0.8 +mod_version=0.0.10 # The group ID for the mod. It is only important when publishing as an artifact to a Maven repository. # This should match the base package used for the mod sources. # See https://maven.apache.org/guides/mini/guide-naming-conventions.html diff --git a/src/main/java/net/krituximon/stalinium/Stalinium.java b/src/main/java/net/krituximon/stalinium/Stalinium.java index f9ebdfb..647cb0c 100644 --- a/src/main/java/net/krituximon/stalinium/Stalinium.java +++ b/src/main/java/net/krituximon/stalinium/Stalinium.java @@ -10,6 +10,8 @@ import net.krituximon.stalinium.screen.ModMenuTypes; import net.krituximon.stalinium.screen.custom.StaliniumPressMenu; import net.krituximon.stalinium.screen.custom.StaliniumPressScreen; import net.krituximon.stalinium.sound.ModSounds; +import net.krituximon.stalinium.util.PlacedLogStorage; +import net.krituximon.stalinium.util.PlayerLogEvents; import net.krituximon.stalinium.worldgen.ModFeatures; import net.krituximon.stalinium.worldgen.StaliniumVeinFeature; import net.minecraft.core.Registry; @@ -62,6 +64,7 @@ public class Stalinium ModSounds.register(modEventBus); ModParticles.register(modEventBus); ModCreativeModeTabs.register(modEventBus); + PlayerLogEvents.register(); // Register ourselves for server and other game events we are interested in. // Note that this is necessary if and only if we want *this* class (Stalinium) to respond directly to events. diff --git a/src/main/java/net/krituximon/stalinium/item/ModItems.java b/src/main/java/net/krituximon/stalinium/item/ModItems.java index 9b1e651..fb9641b 100644 --- a/src/main/java/net/krituximon/stalinium/item/ModItems.java +++ b/src/main/java/net/krituximon/stalinium/item/ModItems.java @@ -25,11 +25,11 @@ public class ModItems { public static final DeferredItem STALINIUM_SWORD = ITEMS.register("stalinium_sword", () -> new StaliniumSwordItem(ModTiers.STALINIUM, new Item.Properties() - .attributes(SwordItem.createAttributes(ModTiers.STALINIUM, 3, -2.4f)))); + .attributes(SwordItem.createAttributes(ModTiers.STALINIUM, 3f, -2.4f)))); public static final DeferredItem STALINIUM_AXE = ITEMS.register("stalinium_axe", () -> new StaliniumAxeItem(ModTiers.STALINIUM, new Item.Properties() - .attributes(AxeItem.createAttributes(ModTiers.STALINIUM, 5, -3.0f)))); + .attributes(AxeItem.createAttributes(ModTiers.STALINIUM, 5f, -3.0f)))); public static final DeferredItem STALINIUM_SHOVEL = ITEMS.register("stalinium_shovel", () -> new StaliniumShovelItem(ModTiers.STALINIUM, new Item.Properties() diff --git a/src/main/java/net/krituximon/stalinium/item/StaliniumAxeItem.java b/src/main/java/net/krituximon/stalinium/item/StaliniumAxeItem.java index 4c1b69c..1029b62 100644 --- a/src/main/java/net/krituximon/stalinium/item/StaliniumAxeItem.java +++ b/src/main/java/net/krituximon/stalinium/item/StaliniumAxeItem.java @@ -1,13 +1,17 @@ package net.krituximon.stalinium.item; +import net.krituximon.stalinium.util.PlacedLogStorage; import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; import net.minecraft.tags.BlockTags; +import net.minecraft.world.InteractionResult; import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.AxeItem; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Tier; +import net.minecraft.world.item.context.UseOnContext; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.state.BlockState; @@ -24,17 +28,21 @@ public class StaliniumAxeItem extends AxeItem { cutDownTree(level, pos, (Player)miningEntity, stack); return true; } - return super.mineBlock(stack, level, state, pos, miningEntity); + return true; } private void cutDownTree(Level level, BlockPos start, Player player, ItemStack stack) { Set toBreak = new HashSet<>(); Deque queue = new ArrayDeque<>(); queue.add(start); + PlacedLogStorage storage = PlacedLogStorage.get((ServerLevel) level); + while (!queue.isEmpty() && toBreak.size() < 256) { BlockPos current = queue.removeFirst(); if (toBreak.contains(current)) continue; BlockState bs = level.getBlockState(current); + if (storage.contains(current)) continue; + if (bs.is(BlockTags.LOGS)) { toBreak.add(current); for (int dx = -1; dx <= 1; dx++) { @@ -57,7 +65,7 @@ public class StaliniumAxeItem extends AxeItem { @Override public int getMaxDamage(ItemStack stack) { - return 0; + return 2; } @Override @@ -69,4 +77,13 @@ public class StaliniumAxeItem extends AxeItem { public boolean isDamaged(ItemStack stack) { return false; } + + @Override + public InteractionResult useOn(UseOnContext ctx) { + ItemStack stack = ctx.getItemInHand(); + int oldDamage = stack.getDamageValue(); + InteractionResult res = super.useOn(ctx); + stack.setDamageValue(oldDamage); + return res; + } } diff --git a/src/main/java/net/krituximon/stalinium/item/StaliniumShovelItem.java b/src/main/java/net/krituximon/stalinium/item/StaliniumShovelItem.java index da3000f..519d0ec 100644 --- a/src/main/java/net/krituximon/stalinium/item/StaliniumShovelItem.java +++ b/src/main/java/net/krituximon/stalinium/item/StaliniumShovelItem.java @@ -6,7 +6,6 @@ import net.minecraft.world.effect.MobEffectInstance; import net.minecraft.world.effect.MobEffects; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.player.Player; -import net.minecraft.world.item.AxeItem; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ShovelItem; import net.minecraft.world.item.Tier; @@ -15,6 +14,9 @@ import net.minecraft.world.level.block.state.BlockState; import java.util.List; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.item.context.UseOnContext; + public class StaliniumShovelItem extends ShovelItem { public StaliniumShovelItem(Tier tier, Properties properties) { super(tier, properties); @@ -27,9 +29,10 @@ public class StaliniumShovelItem extends ShovelItem { miningEntity.addEffect(haste); Level world = miningEntity.getCommandSenderWorld(); var box = miningEntity.getBoundingBox().inflate(10.0); - world.getEntitiesOfClass(LivingEntity.class, box, p -> p instanceof LivingEntity).forEach( - p -> p.addEffect(haste) - ); + List nearby = world.getEntitiesOfClass(Player.class, box, p -> p instanceof ServerPlayer && miningEntity.isAlliedTo(p)); + for (Player p : nearby) { + p.addEffect(haste); + } } return true; } @@ -46,6 +49,15 @@ public class StaliniumShovelItem extends ShovelItem { @Override public int getMaxDamage(ItemStack stack) { - return 0; + return 2; } -} + + @Override + public InteractionResult useOn(UseOnContext ctx) { + ItemStack stack = ctx.getItemInHand(); + int oldDamage = stack.getDamageValue(); + InteractionResult res = super.useOn(ctx); + stack.setDamageValue(oldDamage); + return res; + } +} \ No newline at end of file diff --git a/src/main/java/net/krituximon/stalinium/item/StaliniumSwordItem.java b/src/main/java/net/krituximon/stalinium/item/StaliniumSwordItem.java index 0cd0699..1f4f8d2 100644 --- a/src/main/java/net/krituximon/stalinium/item/StaliniumSwordItem.java +++ b/src/main/java/net/krituximon/stalinium/item/StaliniumSwordItem.java @@ -24,7 +24,7 @@ public class StaliniumSwordItem extends SwordItem { attacker.addEffect(strength); Level world = attacker.getCommandSenderWorld(); var box = attacker.getBoundingBox().inflate(5.0); - List nearby = world.getEntitiesOfClass(Player.class, box, p -> p instanceof ServerPlayer); + List nearby = world.getEntitiesOfClass(Player.class, box, p -> p instanceof ServerPlayer && attacker.isAlliedTo(p)); for (Player p : nearby) { p.addEffect(strength); } @@ -43,6 +43,11 @@ public class StaliniumSwordItem extends SwordItem { @Override public int getMaxDamage(ItemStack stack) { - return 0; + return 2; } -} + + @Override + public void postHurtEnemy(ItemStack stack, LivingEntity target, LivingEntity attacker) { + + } +} \ No newline at end of file diff --git a/src/main/java/net/krituximon/stalinium/util/PlacedLogStorage.java b/src/main/java/net/krituximon/stalinium/util/PlacedLogStorage.java new file mode 100644 index 0000000..93eeb43 --- /dev/null +++ b/src/main/java/net/krituximon/stalinium/util/PlacedLogStorage.java @@ -0,0 +1,85 @@ +package net.krituximon.stalinium.util; + +import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import net.minecraft.core.BlockPos; +import net.minecraft.core.HolderLookup; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.saveddata.SavedData; + +import java.util.Set; + +public class PlacedLogStorage extends SavedData { + + /* ------------------------------------------------------------------ */ + /* Data */ + /* ------------------------------------------------------------------ */ + + private final Set placedLogs = new ObjectOpenHashSet<>(); + + /* ------------------------------------------------------------------ */ + /* Constructors required by DataStorage.computeIfAbsent */ + /* ------------------------------------------------------------------ */ + + /** + * Creates a brand-new, empty storage object (used when no save-file exists yet). + */ + public PlacedLogStorage() { + } + + public PlacedLogStorage(CompoundTag compoundTag, HolderLookup.Provider provider) { + } + + @Override + public CompoundTag save(CompoundTag compoundTag, HolderLookup.Provider provider) { + long[] packed = placedLogs.stream().mapToLong(BlockPos::asLong).toArray(); + compoundTag.putLongArray("logs", packed); + return compoundTag; + } + + /** + * Re-hydrates the storage from NBT (used when the save-file already exists). + */ + public PlacedLogStorage(CompoundTag tag) { + for (long packed : tag.getLongArray("logs")) { + placedLogs.add(BlockPos.of(packed)); + } + } + + /* ------------------------------------------------------------------ */ + /* SavedData implementation */ + /* ------------------------------------------------------------------ */ + + + /* ------------------------------------------------------------------ */ + /* Public helpers (used by PlayerLogEvents, etc.) */ + /* ------------------------------------------------------------------ */ + + public void add(BlockPos pos) { + if (placedLogs.add(pos)) setDirty(); + } + + public void remove(BlockPos pos) { + if (placedLogs.remove(pos)) setDirty(); + } + + public boolean contains(BlockPos pos) { + return placedLogs.contains(pos); + } + + /* ------------------------------------------------------------------ */ + /* Accessor */ + /* ------------------------------------------------------------------ */ + + private static final String DATA_NAME = "stalinium_placed_logs"; + + public static PlacedLogStorage get(ServerLevel level) { + SavedData.Factory factory = + new SavedData.Factory<>( // + PlacedLogStorage::new, // Function + PlacedLogStorage::new); // Supplier + + return level.getDataStorage().computeIfAbsent(factory, DATA_NAME); + } + +} \ No newline at end of file diff --git a/src/main/java/net/krituximon/stalinium/util/PlayerLogEvents.java b/src/main/java/net/krituximon/stalinium/util/PlayerLogEvents.java new file mode 100644 index 0000000..6f34b40 --- /dev/null +++ b/src/main/java/net/krituximon/stalinium/util/PlayerLogEvents.java @@ -0,0 +1,40 @@ +package net.krituximon.stalinium.util; + +import net.minecraft.core.BlockPos; +import net.minecraft.tags.BlockTags; +import net.minecraft.world.entity.player.Player; +import net.minecraft.server.level.ServerLevel; +import net.neoforged.bus.api.IEventBus; +import net.neoforged.neoforge.common.NeoForge; +import net.neoforged.neoforge.event.level.BlockEvent; +import net.neoforged.bus.api.SubscribeEvent; + +/** + * Listens for block place & break events to mark / un-mark player placed logs. + * Register this class during mod-initialisation: MinecraftForge.EVENT_BUS.register(new PlayerLogEvents()); + */ +public class PlayerLogEvents { + + /* Player (or entity) places a block */ + @SubscribeEvent + public void onBlockPlaced(BlockEvent.EntityPlaceEvent e) { + if (!(e.getLevel() instanceof ServerLevel level)) return; + if (!e.getPlacedBlock().is(BlockTags.LOGS)) return; + + PlacedLogStorage.get(level).add(e.getPos()); + } + + /* Any time the block is removed (player break, piston, explosion …) */ + @SubscribeEvent + public void onBlockBroken(BlockEvent.BreakEvent e) { + if (!(e.getLevel() instanceof ServerLevel level)) return; + BlockPos pos = e.getPos(); + + PlacedLogStorage.get(level).remove(pos); + } + + public static void register() { + NeoForge.EVENT_BUS.register(new PlayerLogEvents()); + } + +} \ No newline at end of file