diff --git a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/advancements/AdvancementData.kt b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/advancements/AdvancementData.kt index f63082661..ddee26360 100644 --- a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/advancements/AdvancementData.kt +++ b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/advancements/AdvancementData.kt @@ -8,7 +8,6 @@ import net.minecraft.advancements.critereon.InventoryChangeTrigger import net.minecraft.world.item.DyeColor import net.minecraft.world.item.ItemStack import net.minecraftforge.common.data.ExistingFileHelper -import ru.dbotthepony.mc.otm.core.TranslatableComponent import ru.dbotthepony.mc.otm.core.registryName import ru.dbotthepony.mc.otm.datagen.lang.MatteryLanguageProvider import ru.dbotthepony.mc.otm.datagen.modLocation @@ -16,6 +15,7 @@ import ru.dbotthepony.mc.otm.registry.MItemTags import ru.dbotthepony.mc.otm.registry.MItems import ru.dbotthepony.mc.otm.registry.MRegistry import ru.dbotthepony.mc.otm.triggers.BlackHoleTrigger +import ru.dbotthepony.mc.otm.triggers.NailedEntityTrigger import java.util.function.Consumer fun addAdvancements(serializer: Consumer, existingFileHelper: ExistingFileHelper, lang: MatteryLanguageProvider) { @@ -553,4 +553,18 @@ fun addAdvancements(serializer: Consumer, existingFileHelper: Exist .addCriterion("essence1", criterion(MItems.ESSENCE_CAPSULE)) .addCriterion("essence2", criterion(MItems.ESSENCE_DRIVE)) .save(serializer, modLocation("regular/essence_capsule"), existingFileHelper) + + AdvancementBuilder() + .parent(root) + .display( + itemStack = ItemStack(MItems.EXPLOSIVE_HAMMER).also { MItems.EXPLOSIVE_HAMMER.prime(it) }, + title = translation.add("explosive_hammer", "I Did It Like This") { + russian("Я сделал это вот так") + }, + description = translation.add("explosive_hammer.desc", "Nail down something (or someone)") { + russian("Пригвоздите что-либо (или кого-либо)") + } + ) + .addCriterion("damage", NailedEntityTrigger.Instance()) + .save(serializer, modLocation("regular/explosive_hammer"), existingFileHelper) } 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 be3ad5e5f..a577a70d2 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 @@ -348,6 +348,11 @@ private fun death(provider: MatteryLanguageProvider) { death("otm_emp.player", "%2\$s blew fuzes of %1\$s") death("otm_emp.player.item", "%2\$s blew fuzes of %1\$s using %3\$s") + death(MRegistry.DAMAGE_EXPLOSIVE_HAMMER_NAME, "%1\$s's fun time with hammer is over") + death(MRegistry.DAMAGE_HAMMER_NAIL_NAME, "%1\$s got nailed") + death(MRegistry.DAMAGE_HAMMER_NAIL_NAME + ".player", "%1\$s got nailed by %\$2") + death(MRegistry.DAMAGE_HAMMER_NAIL_NAME + ".player.item", "%1\$s got nailed by %2\$s using %3\$s") + death(MRegistry.DAMAGE_EXOPACK_PROBE_ID, "%1\$s couldn't handle spinal surgery") death("${MRegistry.DAMAGE_EXOPACK_PROBE_ID}.player", "%1\$s couldn't handle spinal surgery whilst fighting %2\$s") } @@ -447,6 +452,12 @@ private fun blocks(provider: MatteryLanguageProvider) { private fun items(provider: MatteryLanguageProvider) { with(provider.english) { + add(MItems.EXPLOSIVE_HAMMER, "Explosive Hammer") + add(MItems.EXPLOSIVE_HAMMER, "desc", "For those who feel bored") + add(MItems.EXPLOSIVE_HAMMER, "primed", "Primed!") + add(MItems.EXPLOSIVE_HAMMER, "not_primed0", "Not primed") + add(MItems.EXPLOSIVE_HAMMER, "not_primed1", "Prime at crafting table with little of gunpowder and nugget payload") + add(MItems.EXOPACK_PROBE, "Exopack Probe") add(MItems.ExopackUpgrades.INVENTORY_UPGRADE_CREATIVE, "Creative Exopack Inventory Upgrade") add(MItems.ExopackUpgrades.CRAFTING_UPGRADE, "Exopack Crafting Upgrade") 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 02c856631..1e8345a1e 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 @@ -355,6 +355,11 @@ private fun death(provider: MatteryLanguageProvider) { death("otm_emp.player", "%2\$s выбил все предохранители %1\$s") death("otm_emp.player.item", "%2\$s выбил все предохранители %1\$s используя %3\$s") + death(MRegistry.DAMAGE_EXPLOSIVE_HAMMER_NAME, "Время развлечений у %1\$s с молотком подошло к концу") + death(MRegistry.DAMAGE_HAMMER_NAIL_NAME, "%1\$s был пригвождён") + death(MRegistry.DAMAGE_HAMMER_NAIL_NAME + ".player", "%1\$s был пригвождён %2\$s") + death(MRegistry.DAMAGE_HAMMER_NAIL_NAME + ".player.item", "%1\$s был пригвождён %2\$s используя %3\$s") + death(MRegistry.DAMAGE_EXOPACK_PROBE_ID, "%1\$s не выдержал спинную хирургию") death("${MRegistry.DAMAGE_EXOPACK_PROBE_ID}.player", "%1\$s не выдержал спинную хирургию пока сражался с %2\$s") } @@ -454,6 +459,12 @@ private fun blocks(provider: MatteryLanguageProvider) { private fun items(provider: MatteryLanguageProvider) { with(provider.russian) { + add(MItems.EXPLOSIVE_HAMMER, "Молоток-убийца") + add(MItems.EXPLOSIVE_HAMMER, "desc", "Для тех, кому стало скучно") + add(MItems.EXPLOSIVE_HAMMER, "primed", "Заряжен!") + add(MItems.EXPLOSIVE_HAMMER, "not_primed0", "Не заряжен") + add(MItems.EXPLOSIVE_HAMMER, "not_primed1", "Для зарядки добавьте немного пороха и наконечник-самородок") + add(MItems.EXOPACK_PROBE, "Маяк экзопака") add(MItems.ExopackUpgrades.INVENTORY_UPGRADE_CREATIVE, "Творческое обновление инвентаря экзопака") add(MItems.ExopackUpgrades.CRAFTING_UPGRADE, "Обновление сетки крафта экзопака") diff --git a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/recipes/CraftingTableRecipes.kt b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/recipes/CraftingTableRecipes.kt index 1c6ce2577..5369c5c2a 100644 --- a/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/recipes/CraftingTableRecipes.kt +++ b/src/data/kotlin/ru/dbotthepony/mc/otm/datagen/recipes/CraftingTableRecipes.kt @@ -15,6 +15,7 @@ import ru.dbotthepony.mc.otm.registry.MItems import ru.dbotthepony.mc.otm.registry.MRegistry import ru.dbotthepony.mc.otm.core.registryName import ru.dbotthepony.mc.otm.datagen.modLocation +import ru.dbotthepony.mc.otm.recipe.ExplosiveHammerPrimingRecipe import ru.dbotthepony.mc.otm.recipe.UpgradeRecipe import java.util.function.Consumer @@ -331,4 +332,13 @@ fun addCraftingTableRecipes(consumer: Consumer) { .unlockedBy(MItemTags.HARDENED_GLASS_PANES) .unlockedBy(MItemTags.TRITANIUM_INGOTS) .build(consumer) + + consumer.accept(ExplosiveHammerPrimingRecipe(modLocation("hammer_priming"), Ingredient.of(Tags.Items.NUGGETS_IRON)).finishedRecipe) + + MatteryRecipe(MItems.EXPLOSIVE_HAMMER, category = RecipeCategory.COMBAT) + .rowB(Tags.Items.INGOTS_IRON) + .rowAB(Tags.Items.INGOTS_IRON, Tags.Items.RODS_WOODEN) + .rowB(Tags.Items.RODS_WOODEN) + .unlockedBy(Items.FLINT_AND_STEEL) + .build(consumer) } diff --git a/src/main/java/ru/dbotthepony/mc/otm/OverdriveThatMatters.java b/src/main/java/ru/dbotthepony/mc/otm/OverdriveThatMatters.java index 20ee86646..7d2d1603b 100644 --- a/src/main/java/ru/dbotthepony/mc/otm/OverdriveThatMatters.java +++ b/src/main/java/ru/dbotthepony/mc/otm/OverdriveThatMatters.java @@ -46,6 +46,7 @@ import ru.dbotthepony.mc.otm.config.ServerCompatConfig; import ru.dbotthepony.mc.otm.config.ServerConfig; import ru.dbotthepony.mc.otm.config.ToolsConfig; import ru.dbotthepony.mc.otm.core.math.Decimal; +import ru.dbotthepony.mc.otm.item.ExplosiveHammerItem; import ru.dbotthepony.mc.otm.item.ItemTritaniumArmor; import ru.dbotthepony.mc.otm.item.QuantumBatteryItem; import ru.dbotthepony.mc.otm.item.weapon.AbstractWeaponItem; @@ -191,6 +192,8 @@ public final class OverdriveThatMatters { EVENT_BUS.addListener(EventPriority.NORMAL, EnderTeleporterFeature.Companion::onEntityDeath); EVENT_BUS.addListener(EventPriority.HIGH, ItemTritaniumArmor.Companion::onHurt); + EVENT_BUS.addListener(EventPriority.NORMAL, ExplosiveHammerItem.Companion::onLeftClickBlock); + MatteryPlayerNetworkChannel.INSTANCE.register(); MenuNetworkChannel.INSTANCE.register(); WeaponNetworkChannel.INSTANCE.register(); diff --git a/src/main/java/ru/dbotthepony/mc/otm/registry/MRecipes.java b/src/main/java/ru/dbotthepony/mc/otm/registry/MRecipes.java index f7a72fec6..9652761e5 100644 --- a/src/main/java/ru/dbotthepony/mc/otm/registry/MRecipes.java +++ b/src/main/java/ru/dbotthepony/mc/otm/registry/MRecipes.java @@ -9,6 +9,7 @@ import net.minecraftforge.registries.DeferredRegister; import net.minecraftforge.registries.ForgeRegistries; import ru.dbotthepony.mc.otm.OverdriveThatMatters; import ru.dbotthepony.mc.otm.recipe.EnergyContainerRecipe; +import ru.dbotthepony.mc.otm.recipe.ExplosiveHammerPrimingRecipe; import ru.dbotthepony.mc.otm.recipe.PlatePressRecipe; import ru.dbotthepony.mc.otm.recipe.PlatePressRecipeFactory; import ru.dbotthepony.mc.otm.recipe.UpgradeRecipe; @@ -30,6 +31,7 @@ public class MRecipes { public static final MatteryRecipeType PLATE_PRESS = new MatteryRecipeType<>(OverdriveThatMatters.loc(MNames.PLATE_PRESS)); public static final MatteryRecipeType ENERGY_CONTAINER = new MatteryRecipeType<>(OverdriveThatMatters.loc("energy_container")); public static final MatteryRecipeType UPGRADE = new MatteryRecipeType<>(OverdriveThatMatters.loc("upgrade")); + public static final MatteryRecipeType HAMMER_PRIMING = new MatteryRecipeType<>(OverdriveThatMatters.loc("hammer_priming")); private static final DeferredRegister> serializerRegistry = DeferredRegister.create(ForgeRegistries.RECIPE_SERIALIZERS, OverdriveThatMatters.MOD_ID); private static final DeferredRegister> typeRegistry = DeferredRegister.create(ForgeRegistries.RECIPE_TYPES, OverdriveThatMatters.MOD_ID); @@ -38,10 +40,12 @@ public class MRecipes { serializerRegistry.register(MNames.PLATE_PRESS, () -> PlatePressRecipeFactory.INSTANCE); serializerRegistry.register(ENERGY_CONTAINER.name.getPath(), () -> EnergyContainerRecipe.Companion); serializerRegistry.register(UPGRADE.name.getPath(), () -> UpgradeRecipe.Companion); + serializerRegistry.register(HAMMER_PRIMING.name.getPath(), () -> ExplosiveHammerPrimingRecipe.Companion); typeRegistry.register(MNames.PLATE_PRESS, () -> PLATE_PRESS); typeRegistry.register(ENERGY_CONTAINER.name.getPath(), () -> ENERGY_CONTAINER); typeRegistry.register(UPGRADE.name.getPath(), () -> UPGRADE); + typeRegistry.register(HAMMER_PRIMING.name.getPath(), () -> HAMMER_PRIMING); } public static void register(IEventBus bus) { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/android/feature/ShockwaveFeature.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/android/feature/ShockwaveFeature.kt index 70b6605e9..15b574090 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/android/feature/ShockwaveFeature.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/android/feature/ShockwaveFeature.kt @@ -140,7 +140,7 @@ class ShockwaveFeature(capability: MatteryPlayerCapability) : AndroidSwitchableF entity.deltaMovement += (entity.position - ply.position).normalize() * (multiplier * 3.0) if (ply is ServerPlayer) { - ShockwaveDamageMobTrigger.trigger(ply as ServerPlayer, entity, source, damage) + ShockwaveDamageMobTrigger.trigger(ply as ServerPlayer, entity, damage, source) } } @@ -156,7 +156,7 @@ class ShockwaveFeature(capability: MatteryPlayerCapability) : AndroidSwitchableF entity.deltaMovement += (entity.position - ply.position).normalize() * (multiplier * 3.0) if (ply is ServerPlayer) { - ShockwaveDamageMobTrigger.trigger(ply as ServerPlayer, entity, source, damage) + ShockwaveDamageMobTrigger.trigger(ply as ServerPlayer, entity, damage, source) } } else { entity.deltaMovement += (entity.position - ply.position).normalize() * (multiplier * 6.0) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/MatteryBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/MatteryBlockEntity.kt index b3b4f491f..1d3ef857c 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/MatteryBlockEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/MatteryBlockEntity.kt @@ -23,6 +23,8 @@ import net.minecraft.world.level.block.entity.BlockEntity import net.minecraft.world.level.block.entity.BlockEntityType import net.minecraft.world.level.block.entity.RandomizableContainerBlockEntity import net.minecraft.world.level.block.state.BlockState +import net.minecraft.world.level.chunk.LevelChunk +import net.minecraft.world.phys.Vec3 import net.minecraftforge.common.capabilities.Capability import net.minecraftforge.common.capabilities.ForgeCapabilities import net.minecraftforge.common.util.INBTSerializable @@ -59,6 +61,7 @@ import ru.dbotthepony.mc.otm.onceServer import java.lang.ref.WeakReference import java.util.* import java.util.function.Supplier +import java.util.stream.Stream import kotlin.properties.ReadWriteProperty import kotlin.reflect.KProperty import kotlin.reflect.KProperty0 @@ -730,6 +733,38 @@ abstract class MatteryBlockEntity(p_155228_: BlockEntityType<*>, p_155229_: Bloc private val vec2Dir = Int2ObjectOpenHashMap() + /** + * Returns stream of players watching (tracking) specified [chunkPos] in [level] + */ + fun watchingPlayers(chunkPos: Long, level: Level): Stream { + if (level !is ServerLevel) + return Stream.empty() + + val subs = playerMap[level] ?: return Stream.empty() + val chunk = subs[chunkPos] ?: return Stream.empty() + return chunk.players.stream() + } + + /** + * Returns stream of players watching (tracking) specified [chunkPos] in [level] + */ + fun watchingPlayers(chunkPos: ChunkPos, level: Level) = watchingPlayers(chunkPos.toLong(), level) + + /** + * Returns stream of players watching (tracking) specified [blockPos] in [level] + */ + fun watchingPlayers(blockPos: BlockPos, level: Level) = watchingPlayers(ChunkPos(blockPos), level) + + /** + * Returns stream of players watching (tracking) specified [blockPos] in [level] + */ + fun watchingPlayers(blockPos: Vec3, level: Level) = watchingPlayers(ChunkPos.asLong(SectionPos.blockToSectionCoord(blockPos.x), SectionPos.blockToSectionCoord(blockPos.z)), level) + + /** + * Returns stream of players watching (tracking) specified [chunk] + */ + fun watchingPlayers(chunk: LevelChunk) = watchingPlayers(chunk.pos, chunk.level) + private fun vecKey(value: Vec3i): Int { if (value.x !in -1 .. 1) return -1 if (value.y !in -1 .. 1) return -1 diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/blackhole/BlackHoleBlockEntity.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/blackhole/BlackHoleBlockEntity.kt index e95438428..4f04495ea 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/blackhole/BlackHoleBlockEntity.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/blackhole/BlackHoleBlockEntity.kt @@ -21,6 +21,8 @@ import ru.dbotthepony.mc.otm.block.BlackHoleBlock import ru.dbotthepony.mc.otm.block.entity.tech.GravitationStabilizerBlockEntity import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity import ru.dbotthepony.mc.otm.block.entity.blackhole.ExplosionQueue.Companion.queueForLevel +import ru.dbotthepony.mc.otm.core.getExplosionResistance +import ru.dbotthepony.mc.otm.core.gracefulBlockBreak import ru.dbotthepony.mc.otm.core.math.Decimal import ru.dbotthepony.mc.otm.core.math.plus import ru.dbotthepony.mc.otm.registry.MBlockEntities @@ -101,7 +103,7 @@ class BlackHoleBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mattery if (setByRemote) { updateMass() } - }) + }).property fun stabilizerAttached(stabilizer: GravitationStabilizerBlockEntity) { if (stabilizer in stabilizers) @@ -162,10 +164,10 @@ class BlackHoleBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mattery nbt["spin_direction"] = spinDirection } - override fun load(tag: CompoundTag) { - super.load(tag) - mass = tag.map("mass", Decimal::deserializeNBT) ?: BASELINE_MASS - spinDirection = tag.getBoolean("spin_direction") + override fun load(nbt: CompoundTag) { + super.load(nbt) + mass = nbt.map("mass", Decimal::deserializeNBT) ?: BASELINE_MASS + spinDirection = nbt.getBoolean("spin_direction") } var affectedBounds = BoundingBox(0, 0, 0, 1, 1, 1) @@ -176,7 +178,7 @@ class BlackHoleBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mattery private fun setDeltaMovement(living: Entity, center: Vec3, distance: Double, weaker: Boolean) { //final double mult = Math.min(2, (30 * this.gravitation_strength) / Math.max(1, Math.pow(distance, 2))); // Сила притяжения - val mult = Math.pow((1 - distance / (30 * gravitationStrength)).coerceAtLeast(0.0), 2.0) * gravitationStrength / 8 + val mult = (1 - distance / (30 * gravitationStrength)).coerceAtLeast(0.0).pow(2.0) * gravitationStrength / 8 // Притяжение к ядру val delta = living.position().vectorTo(center).normalize() @@ -291,18 +293,7 @@ class BlackHoleBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mattery if (!getBlock.isAir && getBlock.block !is BlackHoleBlock) { val speed = getBlock.getDestroySpeed(level, pos) - val eResist = try { - getBlock.getExplosionResistance(level, pos, null) - } catch (err: NullPointerException) { - getBlock.block.explosionResistance - // Потому что возможно какой-либо мод не ожидает что Explosion == null - // особенно учитывая что интерфейс IForgeBlock не имеет @ParamsAreNonnullByDefault - // и аргумент не помечен как @Nullable - // тем самым имеет тип Explosion! который указывается как Explosion? .. Explosion!! - } catch (err: IllegalArgumentException) { - getBlock.block.explosionResistance - } - + val eResist = getBlock.getExplosionResistance(level, pos) var strengthLinear = sqrt(blockPos.distSqr(pos)) if (strengthLinear < gravitationStrength) { @@ -310,9 +301,7 @@ class BlackHoleBlockEntity(p_155229_: BlockPos, p_155230_: BlockState) : Mattery } if (speed >= 0f && (speed * 2f).coerceAtLeast(eResist / 3f) < gravitationStrength * (12.0 / strengthLinear)) { - Block.dropResources(getBlock, level, pos, level.getBlockEntity(pos)) - getBlock.block.destroy(level, pos, getBlock) - level.setBlock(pos, getBlock.fluidState.createLegacyBlock(), Block.UPDATE_ALL) + level.gracefulBlockBreak(pos, getBlock) } } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/config/ItemsConfig.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/config/ItemsConfig.kt index 02864211c..153753a22 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/config/ItemsConfig.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/config/ItemsConfig.kt @@ -98,4 +98,76 @@ object ItemsConfig : AbstractConfig("items") { val FLUID_CAPSULE_CAPACITY: Int by builder.defineInRange("FLUID_CAPSULE_CAPACITY", 1000, 1, Int.MAX_VALUE) val FLUID_TANK_CAPACITY: Int by builder.defineInRange("FLUID_TANK_CAPACITY", 32_000, 1, Int.MAX_VALUE) + + object ExplosiveHammer { + val MAX_TRAVEL: Double by builder + .comment("Max distance, in blocks, the nail can travel", "This also dictates potential max damage nail can deal (controlled by TRAVEL_DAMAGE_MULT)") + .comment("The nail penetrate through blocks and entities, losing power in process") + .comment("The nail can not travel into unloaded chunks and will stop immediately if it does so") + .comment("The nail travels its entire path instantaneously") + .defineInRange("MAX_TRAVEL", 48.0, 0.0, Double.MAX_VALUE) + + val TRAVEL_EXPLOSION_RESIST_MULT: Double by builder + .comment("Multiplier of explosion resistance of blocks on nail path") + .comment("If explosion resistance multiplied by this is bigger than nail current force (base of MAX_TRAVEL)") + .comment("then nail movement halts, otherwise nail force is reduced by explosion resistance multiplied by this") + .comment("and block is broken (if DAMAGE_BLOCKS is true, otherwise it only acts as stopping power)") + .comment("---") + .comment("Values over 1 will make blocks more resistant to be penetrated through (and broken)") + .comment("Values below 1 will make blocks less resistant to be penetrated though (and broken)") + .comment("Value of 0 means no matter how strong block resistance is, it will be broken by nail", "(this includes bedrock!!!)") + .comment("---") + .defineInRange("TRAVEL_EXPLOSION_RESIST_MULT", 1.75, 0.0, Double.MAX_VALUE) + + val TRAVEL_DAMAGE_MULT: Double by builder + .comment("Multiplier of current nail force (base of MAX_TRAVEL) for damage values") + .comment("That is, any being on path of nail will get damaged by nail power multiplied by this") + .defineInRange("TRAVEL_DAMAGE_MULT", 0.6, 0.0, Double.MAX_VALUE) + + val TRAVEL_MAX_HEALTH_MULT: Double by builder + .comment("Multiplier of being's max health in nail path subtracted from nail force when penetrating through") + .defineInRange("TRAVEL_MAX_HEALTH_MULT", 0.4, 0.0, Double.MAX_VALUE) + + val DAMAGE_BLOCKS: Boolean by builder + .comment("Should hammer (**not** explosion) damage blocks", "Blocks damaged this way will always be dropped") + .define("DAMAGE_BLOCKS", true) + + val EXPLOSION_DAMAGE_BLOCKS: Boolean by builder + .comment("Should hammer **explosion** damage blocks") + .define("EXPLOSION_DAMAGE_BLOCKS", false) + + val FLY_OFF_CHANCE: Double by builder + .comment("Chance that hammer will fly off hands of attacker upon impact") + .defineInRange("FLY_OFF_CHANCE", 0.6, 0.0, 1.0) + + val SELF_HARM_CHANCE: Double by builder + .comment("When hammer DOES NOT fly off hands, what is chance of it damaging the user?", "(this does not account for damage because of hitting things close)", "(idiot)") + .defineInRange("SELF_HARM_CHANCE", 0.4, 0.0, 1.0) + + val SELF_HARM_MIN_DAMAGE: Double by builder + .comment("When hammer DOES NOT fly off hands, what is minimal damage it deals to the user (in half of hearts)?") + .defineInRange("SELF_HARM_MIN_DAMAGE", 2.0, 0.0, Double.MAX_VALUE) + + val SELF_HARM_MAX_DAMAGE: Double by builder + .comment("When hammer DOES NOT fly off hands, what is maximal damage it deals to the user (in half of hearts)?") + .defineInRange("SELF_HARM_MAX_DAMAGE", 10.0, 0.0, Double.MAX_VALUE) + + val FLY_OFF_DAMAGE_CHANCE: Double by builder + .comment("When hammer flies off hands, what is chance of it damaging the user?") + .defineInRange("FLY_OFF_DAMAGE_CHANCE", 0.8, 0.0, 1.0) + + val FLY_OFF_MIN_DAMAGE: Double by builder + .comment("When hammer flies off hands, what is minimal damage it deals to the user (in half of hearts)?") + .defineInRange("FLY_OFF_MIN_DAMAGE", 4.0, 0.0, Double.MAX_VALUE) + + val FLY_OFF_MAX_DAMAGE: Double by builder + .comment("When hammer flies off hands, what is maximal damage it deals to the user (in half of hearts)?") + .defineInRange("FLY_OFF_MAX_DAMAGE", 25.0, 0.0, Double.MAX_VALUE) + } + + init { + builder.push("ExplosiveHammer") + ExplosiveHammer + builder.pop() + } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt index dfd5c2a12..1ebb97476 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/Ext.kt @@ -22,6 +22,8 @@ import net.minecraft.world.item.ItemStack import net.minecraft.world.item.Items import net.minecraft.world.item.crafting.Ingredient import net.minecraft.world.level.BlockGetter +import net.minecraft.world.level.Level +import net.minecraft.world.level.block.Block import net.minecraft.world.level.block.state.BlockState import net.minecraft.world.level.block.state.StateHolder import net.minecraft.world.level.block.state.properties.Property @@ -220,6 +222,12 @@ fun BlockState.getExplosionResistance(level: BlockGetter, pos: BlockPos): Float } } +fun Level.gracefulBlockBreak(blockPos: BlockPos, block: BlockState = getBlockState(blockPos)) { + Block.dropResources(block, this, blockPos, getBlockEntity(blockPos)) + block.block.destroy(this, blockPos, block) + setBlock(blockPos, block.fluidState.createLegacyBlock(), Block.UPDATE_ALL) +} + fun MutableCollection.addAll(elements: Iterator) { for (item in elements) { add(item) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/item/ExplosiveHammerItem.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/item/ExplosiveHammerItem.kt new file mode 100644 index 000000000..b6962151b --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/item/ExplosiveHammerItem.kt @@ -0,0 +1,210 @@ +package ru.dbotthepony.mc.otm.item + +import it.unimi.dsi.fastutil.objects.ObjectArraySet +import net.minecraft.ChatFormatting +import net.minecraft.core.BlockPos +import net.minecraft.network.chat.Component +import net.minecraft.network.protocol.game.ClientboundExplodePacket +import net.minecraft.server.level.ServerLevel +import net.minecraft.server.level.ServerPlayer +import net.minecraft.world.InteractionHand +import net.minecraft.world.damagesource.DamageSource +import net.minecraft.world.entity.LivingEntity +import net.minecraft.world.entity.item.ItemEntity +import net.minecraft.world.entity.player.Player +import net.minecraft.world.item.Item +import net.minecraft.world.item.ItemStack +import net.minecraft.world.item.TooltipFlag +import net.minecraft.world.level.Explosion +import net.minecraft.world.level.Level +import net.minecraft.world.phys.AABB +import net.minecraft.world.phys.Vec3 +import net.minecraftforge.common.MinecraftForge +import net.minecraftforge.event.entity.player.PlayerInteractEvent +import net.minecraftforge.event.level.BlockEvent +import ru.dbotthepony.mc.otm.block.entity.MatteryBlockEntity +import ru.dbotthepony.mc.otm.config.ItemsConfig +import ru.dbotthepony.mc.otm.core.TranslatableComponent +import ru.dbotthepony.mc.otm.core.getExplosionResistance +import ru.dbotthepony.mc.otm.core.gracefulBlockBreak +import ru.dbotthepony.mc.otm.core.math.component1 +import ru.dbotthepony.mc.otm.core.math.component2 +import ru.dbotthepony.mc.otm.core.math.component3 +import ru.dbotthepony.mc.otm.core.math.plus +import ru.dbotthepony.mc.otm.core.math.times +import ru.dbotthepony.mc.otm.core.math.toDoubleVector +import ru.dbotthepony.mc.otm.core.nbt.set +import ru.dbotthepony.mc.otm.core.position +import ru.dbotthepony.mc.otm.core.tagNotNull +import ru.dbotthepony.mc.otm.registry.HammerNailDamageSource +import ru.dbotthepony.mc.otm.registry.MRegistry +import ru.dbotthepony.mc.otm.triggers.NailedEntityTrigger + +class ExplosiveHammerItem(durability: Int = 64) : Item(Properties().stacksTo(1).fireResistant().durability(durability)) { + override fun canBeHurtBy(pDamageSource: DamageSource): Boolean { + return super.canBeHurtBy(pDamageSource) && !pDamageSource.isExplosion + } + + fun isPrimed(itemStack: ItemStack): Boolean { + return itemStack.tag?.getBoolean("primed") ?: false + } + + fun prime(itemStack: ItemStack) { + itemStack.tagNotNull["primed"] = true + } + + fun unprime(itemStack: ItemStack) { + if (isPrimed(itemStack)) + itemStack.tagNotNull["primed"] = false + } + + private val aabb = AABB(-0.1, -0.1, -0.1, 0.1, 0.1, 0.1) + + fun attackAt(itemStack: ItemStack, attacker: LivingEntity, pos: Vec3, aim: Vec3, hand: InteractionHand) { + if (!isPrimed(itemStack) || attacker.level.isClientSide) + return + + val (ex, ey, ez) = pos + + // взрыв в месте удара молотком + val exp = Explosion(attacker.level, attacker, ex, ey, ez, 1f, false, if (ItemsConfig.ExplosiveHammer.EXPLOSION_DAMAGE_BLOCKS) Explosion.BlockInteraction.DESTROY_WITH_DECAY else Explosion.BlockInteraction.KEEP) + exp.explode() + exp.finalizeExplosion(true) + + if (!ItemsConfig.ExplosiveHammer.EXPLOSION_DAMAGE_BLOCKS) + exp.clearToBlow() + + val level = attacker.level as ServerLevel + + val hitEntities = ObjectArraySet() + hitEntities.add(attacker) + var canTravel = ItemsConfig.ExplosiveHammer.MAX_TRAVEL + var rayPos = pos + var lastBlockPos: BlockPos? = null + + // "полёт" гвоздя + // так как у меня алгоритм "своеобразный", я изобрету велосипед ибо я такой нехороший и непослушный + // Трассировка луча используя наивный метод + while (canTravel > 0.0) { + val blockPos = BlockPos(rayPos) + + if (blockPos != lastBlockPos) { + if (!level.hasChunkAt(blockPos)) break + + val block = level.getBlockState(blockPos) + + if (!block.isAir) { + val resist = block.getExplosionResistance(level, blockPos) * ItemsConfig.ExplosiveHammer.TRAVEL_EXPLOSION_RESIST_MULT + + if (resist >= 0.0) { + if (resist <= canTravel) { + canTravel -= resist + val cond = ItemsConfig.ExplosiveHammer.DAMAGE_BLOCKS && + (attacker !is Player || level.mayInteract(attacker, blockPos) && !MinecraftForge.EVENT_BUS.post(BlockEvent.BreakEvent(level, blockPos, block, attacker))) + + if (cond) { + level.gracefulBlockBreak(blockPos, block) + } + } else { + break + } + } + } + + lastBlockPos = blockPos + } + + val rayBox = aabb.move(rayPos) + val entities = level.getEntities(null, rayBox) { it is LivingEntity && it.isAlive && !it.isSpectator && hitEntities.add(it) } as List + val damageSource = HammerNailDamageSource(attacker, itemStack) + + for (it in entities) { + val damage = canTravel * ItemsConfig.ExplosiveHammer.TRAVEL_DAMAGE_MULT + canTravel -= it.maxHealth * ItemsConfig.ExplosiveHammer.TRAVEL_MAX_HEALTH_MULT + + it.hurt(damageSource, damage.toFloat()) + + if (attacker is ServerPlayer) { + NailedEntityTrigger.trigger(attacker, it, damage.toFloat(), damageSource) + } + } + + val delta = canTravel.coerceAtMost(0.25) + canTravel -= delta + rayPos += aim * delta + } + + MatteryBlockEntity.watchingPlayers(pos, attacker.level) + .filter { it.position.distanceTo(pos) <= 64.0 } + .forEach { + it.connection.send(ClientboundExplodePacket(ex, ey, ez, 1f, exp.toBlow, exp.hitPlayers[it])) + } + + if (attacker !is Player || !attacker.isCreative) { + unprime(itemStack) + + itemStack.hurtAndBreak(1, attacker) { + it.broadcastBreakEvent(hand) + } + + if (!itemStack.isEmpty && attacker.random.nextDouble() <= ItemsConfig.ExplosiveHammer.FLY_OFF_CHANCE) { + attacker.setItemInHand(hand, ItemStack.EMPTY) + + val (x, y, z) = attacker.position + val entity = ItemEntity(attacker.level, x, y, z, itemStack) + + val (xv, yv, zv) = attacker.getViewVector(0f) + + entity.deltaMovement = Vec3( + attacker.random.nextDouble() * xv * -0.5, + attacker.random.nextDouble() * yv * -0.5, + (attacker.random.nextDouble() + 0.65) * zv * -2.0, + ) + + attacker.level.addFreshEntity(entity) + + if (attacker.random.nextDouble() <= ItemsConfig.ExplosiveHammer.FLY_OFF_DAMAGE_CHANCE) { + attacker.invulnerableTime = 0 + val dmg = ItemsConfig.ExplosiveHammer.FLY_OFF_MIN_DAMAGE + attacker.random.nextDouble() * (ItemsConfig.ExplosiveHammer.FLY_OFF_MAX_DAMAGE - ItemsConfig.ExplosiveHammer.FLY_OFF_MIN_DAMAGE) + attacker.hurt(MRegistry.DAMAGE_EXPLOSIVE_HAMMER, dmg.toFloat()) + } + } else if (attacker.random.nextDouble() <= ItemsConfig.ExplosiveHammer.SELF_HARM_CHANCE) { + attacker.invulnerableTime = 0 + val dmg = ItemsConfig.ExplosiveHammer.SELF_HARM_MIN_DAMAGE + attacker.random.nextDouble() * (ItemsConfig.ExplosiveHammer.SELF_HARM_MAX_DAMAGE - ItemsConfig.ExplosiveHammer.SELF_HARM_MIN_DAMAGE) + attacker.hurt(MRegistry.DAMAGE_EXPLOSIVE_HAMMER, dmg.toFloat()) + } + } + } + + override fun hurtEnemy(pStack: ItemStack, pTarget: LivingEntity, pAttacker: LivingEntity): Boolean { + val aim = pAttacker.getViewVector(0f) + val pos = pAttacker.eyePosition + aim * pTarget.position.distanceTo(pAttacker.position) + attackAt(pStack, pAttacker, pos, aim, InteractionHand.MAIN_HAND) + return true + } + + override fun appendHoverText(pStack: ItemStack, pLevel: Level?, pTooltipComponents: MutableList, pIsAdvanced: TooltipFlag) { + super.appendHoverText(pStack, pLevel, pTooltipComponents, pIsAdvanced) + + pTooltipComponents.add(TranslatableComponent("$descriptionId.desc").withStyle(ChatFormatting.DARK_GRAY)) + + if (isPrimed(pStack)) { + pTooltipComponents.add(TranslatableComponent("$descriptionId.primed").withStyle(ChatFormatting.GRAY)) + } else { + pTooltipComponents.add(TranslatableComponent("$descriptionId.not_primed0").withStyle(ChatFormatting.GRAY)) + pTooltipComponents.add(TranslatableComponent("$descriptionId.not_primed1").withStyle(ChatFormatting.GRAY)) + } + } + + companion object { + fun onLeftClickBlock(event: PlayerInteractEvent.LeftClickBlock) { + val item = event.itemStack.item + + if (item is ExplosiveHammerItem) { + val (x, y, z) = event.pos + item.attackAt(event.itemStack, event.entity, Vec3(x + 0.5, y + 0.5, z + 0.5), event.face?.opposite?.normal?.toDoubleVector() ?: event.entity.getViewVector(0f), event.hand) + event.isCanceled = true + } + } + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/recipe/ExplosiveHammerPrimingRecipe.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/recipe/ExplosiveHammerPrimingRecipe.kt new file mode 100644 index 000000000..925c5f3f1 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/recipe/ExplosiveHammerPrimingRecipe.kt @@ -0,0 +1,109 @@ +package ru.dbotthepony.mc.otm.recipe + +import com.google.gson.JsonObject +import com.google.gson.JsonSyntaxException +import net.minecraft.core.NonNullList +import net.minecraft.data.recipes.FinishedRecipe +import net.minecraft.network.FriendlyByteBuf +import net.minecraft.resources.ResourceLocation +import net.minecraft.world.inventory.CraftingContainer +import net.minecraft.world.item.ItemStack +import net.minecraft.world.item.crafting.CraftingBookCategory +import net.minecraft.world.item.crafting.CraftingRecipe +import net.minecraft.world.item.crafting.Ingredient +import net.minecraft.world.item.crafting.RecipeSerializer +import net.minecraft.world.level.Level +import net.minecraftforge.common.Tags +import ru.dbotthepony.mc.otm.container.stream +import ru.dbotthepony.mc.otm.core.isActuallyEmpty +import ru.dbotthepony.mc.otm.core.isNotEmpty +import ru.dbotthepony.mc.otm.core.set +import ru.dbotthepony.mc.otm.item.ExplosiveHammerItem +import ru.dbotthepony.mc.otm.registry.MItems + +class ExplosiveHammerPrimingRecipe(private val _id: ResourceLocation, val payload: Ingredient) : CraftingRecipe { + override fun isIncomplete(): Boolean { + return payload.isActuallyEmpty + } + + override fun getId(): ResourceLocation { + return _id + } + + override fun isSpecial(): Boolean { + return true + } + + override fun matches(pContainer: CraftingContainer, pLevel: Level): Boolean { + val result = pContainer.stream().filter { it.isNotEmpty }.toList() + if (result.size != 3) return false + return result.any { it.item is ExplosiveHammerItem } && + result.any { it.`is`(Tags.Items.GUNPOWDER) } && + result.any { payload.test(it) } + } + + override fun assemble(pContainer: CraftingContainer): ItemStack { + val hammer = pContainer.stream().filter { it.isNotEmpty && it.item is ExplosiveHammerItem }.findAny() + if (hammer.isEmpty) return ItemStack.EMPTY + + return hammer.orElseThrow().copyWithCount(1).also { + (it.item as ExplosiveHammerItem).prime(it) + } + } + + override fun canCraftInDimensions(pWidth: Int, pHeight: Int): Boolean { + return pWidth * pHeight >= 3 + } + + override fun getResultItem(): ItemStack { + return ItemStack.EMPTY + } + + override fun getSerializer(): RecipeSerializer<*> { + return Companion + } + + override fun category(): CraftingBookCategory { + return CraftingBookCategory.EQUIPMENT + } + + override fun getIngredients(): NonNullList { + return NonNullList.of(Ingredient.of(), Ingredient.of(MItems.EXPLOSIVE_HAMMER), Ingredient.of(Tags.Items.GUNPOWDER), payload) + } + + val finishedRecipe = object : FinishedRecipe { + override fun serializeRecipeData(pJson: JsonObject) { + pJson["payload"] = payload.toJson() + } + + override fun getId(): ResourceLocation { + return _id + } + + override fun getType(): RecipeSerializer<*> { + return Companion + } + + override fun serializeAdvancement(): JsonObject? { + return null + } + + override fun getAdvancementId(): ResourceLocation? { + return null + } + } + + companion object : RecipeSerializer { + override fun fromJson(pRecipeId: ResourceLocation, pSerializedRecipe: JsonObject): ExplosiveHammerPrimingRecipe { + return ExplosiveHammerPrimingRecipe(pRecipeId, Ingredient.fromJson(pSerializedRecipe["payload"] ?: throw JsonSyntaxException("Missing `payload` from hammer priming recipe in $pRecipeId"))) + } + + override fun fromNetwork(pRecipeId: ResourceLocation, pBuffer: FriendlyByteBuf): ExplosiveHammerPrimingRecipe { + return ExplosiveHammerPrimingRecipe(pRecipeId, Ingredient.fromNetwork(pBuffer)) + } + + override fun toNetwork(pBuffer: FriendlyByteBuf, pRecipe: ExplosiveHammerPrimingRecipe) { + pRecipe.payload.toNetwork(pBuffer) + } + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/CreativeTabs.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/CreativeTabs.kt index ea350bfa5..f97a70b1c 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/CreativeTabs.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/CreativeTabs.kt @@ -146,6 +146,9 @@ internal fun addMainCreativeTabItems(consumer: CreativeModeTab.Output) { energized(MItems.ENERGY_SWORD) energized(MItems.PLASMA_RIFLE) + accept(MItems.EXPLOSIVE_HAMMER) + accept(ItemStack(MItems.EXPLOSIVE_HAMMER).also { MItems.EXPLOSIVE_HAMMER.prime(it) }) + accept(MItems.BLACK_HOLE_SCANNER) accept(MItems.GRAVITATION_FIELD_LIMITER) accept(MItems.GRAVITATION_FIELD_SENSOR) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/DamageSources.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/DamageSources.kt index 64b91e687..356944726 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/DamageSources.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/DamageSources.kt @@ -184,3 +184,9 @@ class PlasmaDamageSource(entity: Entity? = null, inflictor: ItemStack? = null) : return false } } + +class HammerNailDamageSource(entity: Entity? = null, inflictor: ItemStack? = null) : MatteryDamageSource(MRegistry.DAMAGE_HAMMER_NAIL_NAME, entity, inflictor) { + override fun scalesWithDifficulty(): Boolean { + return false + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MItems.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MItems.kt index d9853eece..307590f22 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MItems.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MItems.kt @@ -215,6 +215,8 @@ object MItems { ::TRITANIUM_BOOTS ) + val EXPLOSIVE_HAMMER: ExplosiveHammerItem by registry.register("explosive_hammer") { ExplosiveHammerItem() } + val ENERGY_SWORD: Item by registry.register(MNames.ENERGY_SWORD) { EnergySwordItem() } val PLASMA_RIFLE: Item by registry.register(MNames.PLASMA_RIFLE) { PlasmaRifleItem() } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MRegistry.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MRegistry.kt index 002eb701a..7e7eae7b2 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MRegistry.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/registry/MRegistry.kt @@ -44,6 +44,7 @@ import ru.dbotthepony.mc.otm.triggers.BecomeHumaneTrigger import ru.dbotthepony.mc.otm.triggers.BlackHoleTrigger import ru.dbotthepony.mc.otm.triggers.EnderTeleporterFallDeathTrigger import ru.dbotthepony.mc.otm.triggers.FallDampenersSaveTrigger +import ru.dbotthepony.mc.otm.triggers.NailedEntityTrigger import ru.dbotthepony.mc.otm.triggers.NanobotsArmorTrigger import ru.dbotthepony.mc.otm.triggers.ShockwaveDamageMobTrigger import ru.dbotthepony.mc.otm.triggers.ShockwaveTrigger @@ -195,6 +196,8 @@ object MRegistry { const val DAMAGE_SHOCKWAVE_NAME = "otm_shockwave" const val DAMAGE_PLASMA_NAME = "otm_plasma" const val DAMAGE_COSMIC_RAYS_NAME = "otm_cosmic_rays" + const val DAMAGE_EXPLOSIVE_HAMMER_NAME = "otm_explosive_hammer" + const val DAMAGE_HAMMER_NAIL_NAME = "otm_hammer_nail" val DAMAGE_EXOPACK_PROBE = ImmutableDamageSource(DamageSource(DAMAGE_EXOPACK_PROBE_ID).bypassArmor().bypassMagic()) @@ -203,6 +206,7 @@ object MRegistry { val DAMAGE_EVENT_HORIZON = ImmutableDamageSource(DamageSource(DAMAGE_EVENT_HORIZON_ID).bypassMagic().bypassArmor()) val DAMAGE_HAWKING_RADIATION = ImmutableDamageSource(DamageSource(DAMAGE_HAWKING_RADIATION_ID)) val DAMAGE_COSMIC_RAYS = ImmutableDamageSource(DamageSource(DAMAGE_COSMIC_RAYS_NAME).bypassArmor().bypassMagic()) + val DAMAGE_EXPLOSIVE_HAMMER = ImmutableDamageSource(DamageSource(DAMAGE_EXPLOSIVE_HAMMER_NAME)) val DAMAGE_EMP: DamageSource = ImmutableDamageSource(EMPDamageSource()) @@ -270,6 +274,7 @@ object MRegistry { CriteriaTriggers.register(EnderTeleporterFallDeathTrigger) CriteriaTriggers.register(KillAsAndroidTrigger) CriteriaTriggers.register(AndroidTravelUnderwater) + CriteriaTriggers.register(NailedEntityTrigger) } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/triggers/HurtTrigger.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/triggers/HurtTrigger.kt new file mode 100644 index 000000000..73705d8fd --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/triggers/HurtTrigger.kt @@ -0,0 +1,61 @@ +package ru.dbotthepony.mc.otm.triggers + +import com.google.gson.JsonObject +import net.minecraft.advancements.critereon.AbstractCriterionTriggerInstance +import net.minecraft.advancements.critereon.DamagePredicate +import net.minecraft.advancements.critereon.DamageSourcePredicate +import net.minecraft.advancements.critereon.DeserializationContext +import net.minecraft.advancements.critereon.EntityPredicate +import net.minecraft.advancements.critereon.MinMaxBounds +import net.minecraft.advancements.critereon.SerializationContext +import net.minecraft.advancements.critereon.SimpleCriterionTrigger +import net.minecraft.resources.ResourceLocation +import net.minecraft.server.level.ServerPlayer +import net.minecraft.world.damagesource.DamageSource +import net.minecraft.world.entity.LivingEntity +import ru.dbotthepony.mc.otm.core.set + +abstract class HurtTrigger : SimpleCriterionTrigger() { + abstract val ID: ResourceLocation + + override fun getId(): ResourceLocation { + return ID + } + + override fun createInstance( + p_66248_: JsonObject, + p_66249_: EntityPredicate.Composite, + p_66250_: DeserializationContext + ): Instance { + return Instance( + EntityPredicate.Composite.fromJson(p_66248_, "entity_predicate", p_66250_), + (p_66248_["damage"] as? JsonObject)?.let(DamagePredicate::fromJson) ?: DamagePredicate.ANY + ) + } + + fun trigger(player: ServerPlayer, entity: LivingEntity, damage: Float, damageSource: DamageSource) { + val context = EntityPredicate.createContext(player, entity) + + trigger(player) { + it.predicate.matches(context) && it.damagePredicate.matches(player, damageSource, damage, damage, false) + } + } + + inner class Instance( + val predicate: EntityPredicate.Composite = EntityPredicate.Composite.ANY, + val damagePredicate: DamagePredicate = DamagePredicate( + MinMaxBounds.Doubles.atLeast(1.0), + MinMaxBounds.Doubles.atLeast(1.0), + EntityPredicate.ANY, + null, + DamageSourcePredicate.ANY + ) + ) : AbstractCriterionTriggerInstance(ID, EntityPredicate.Composite.ANY) { + override fun serializeToJson(pConditions: SerializationContext): JsonObject { + return super.serializeToJson(pConditions).also { + it["entity_predicate"] = predicate.toJson(pConditions) + it["damage"] = damagePredicate.serializeToJson() + } + } + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/triggers/NailedEntityTrigger.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/triggers/NailedEntityTrigger.kt new file mode 100644 index 000000000..dfe36bca3 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/triggers/NailedEntityTrigger.kt @@ -0,0 +1,8 @@ +package ru.dbotthepony.mc.otm.triggers + +import net.minecraft.resources.ResourceLocation +import ru.dbotthepony.mc.otm.OverdriveThatMatters + +object NailedEntityTrigger : HurtTrigger() { + override val ID = ResourceLocation(OverdriveThatMatters.MOD_ID, "hammer_nail_damage") +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/triggers/ShockwaveDamageMobTrigger.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/triggers/ShockwaveDamageMobTrigger.kt index e50e2a58c..55808cc84 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/triggers/ShockwaveDamageMobTrigger.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/triggers/ShockwaveDamageMobTrigger.kt @@ -1,63 +1,8 @@ package ru.dbotthepony.mc.otm.triggers -import com.google.gson.JsonObject -import net.minecraft.advancements.critereon.AbstractCriterionTriggerInstance -import net.minecraft.advancements.critereon.DamagePredicate -import net.minecraft.advancements.critereon.DamageSourcePredicate -import net.minecraft.advancements.critereon.DeserializationContext -import net.minecraft.advancements.critereon.EntityPredicate -import net.minecraft.advancements.critereon.MinMaxBounds -import net.minecraft.advancements.critereon.SerializationContext -import net.minecraft.advancements.critereon.SimpleCriterionTrigger import net.minecraft.resources.ResourceLocation -import net.minecraft.server.level.ServerPlayer -import net.minecraft.world.damagesource.DamageSource -import net.minecraft.world.entity.Entity import ru.dbotthepony.mc.otm.OverdriveThatMatters -import ru.dbotthepony.mc.otm.core.nbt.set -import ru.dbotthepony.mc.otm.core.set -object ShockwaveDamageMobTrigger: SimpleCriterionTrigger() { - val ID = ResourceLocation(OverdriveThatMatters.MOD_ID, "shockwave_damage_mob") - - override fun getId(): ResourceLocation { - return ID - } - - override fun createInstance( - p_66248_: JsonObject, - p_66249_: EntityPredicate.Composite, - p_66250_: DeserializationContext - ): Instance { - return Instance( - EntityPredicate.Composite.fromJson(p_66248_, "entity_predicate", p_66250_), - (p_66248_["damage"] as? JsonObject)?.let(DamagePredicate::fromJson) ?: DamagePredicate.ANY - ) - } - - fun trigger(player: ServerPlayer, entity: Entity, damageSource: DamageSource, damage: Float) { - val context = EntityPredicate.createContext(player, entity) - - trigger(player) { - it.predicate.matches(context) && it.damagePredicate.matches(player, damageSource, damage, damage, false) - } - } - - class Instance( - val predicate: EntityPredicate.Composite = EntityPredicate.Composite.ANY, - val damagePredicate: DamagePredicate = DamagePredicate( - MinMaxBounds.Doubles.atLeast(1.0), - MinMaxBounds.Doubles.atLeast(1.0), - EntityPredicate.ANY, - null, - DamageSourcePredicate.ANY - ) - ) : AbstractCriterionTriggerInstance(ID, EntityPredicate.Composite.ANY) { - override fun serializeToJson(p_16979_: SerializationContext): JsonObject { - return super.serializeToJson(p_16979_).also { - it["entity_predicate"] = predicate.toJson(p_16979_) - it["damage"] = damagePredicate.serializeToJson() - } - } - } +object ShockwaveDamageMobTrigger : HurtTrigger() { + override val ID = ResourceLocation(OverdriveThatMatters.MOD_ID, "shockwave_damage_mob") }