From c28ff851c98ea753e5895ac778beab5ffa6fea80 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Mon, 10 Jan 2022 18:55:30 +0700 Subject: [PATCH] Attempt new blackhole explosion algos, but nothing works --- .../mc/otm/OverdriveThatMatters.java | 4 +- .../java/ru/dbotthepony/mc/otm/Registry.java | 32 + src/main/kotlin/ru/dbotthepony/mc/otm/Ext.kt | 11 +- .../mc/otm/block/BlockExplosionDebugger.kt | 50 ++ .../entity/BlockEntityChemicalGenerator.kt | 1 + .../entity/blackhole/BlockEntityBlackHole.kt | 217 +----- .../blackhole/BlockEntityExplosionDebugger.kt | 49 ++ .../otm/block/entity/blackhole/Explosions.kt | 678 ++++++++++++++++++ .../kotlin/ru/dbotthepony/mc/otm/core/Math.kt | 115 +++ .../mc/otm/graph/Abstract6Graph.kt | 2 +- 10 files changed, 932 insertions(+), 227 deletions(-) create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/block/BlockExplosionDebugger.kt create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/blackhole/BlockEntityExplosionDebugger.kt create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/blackhole/Explosions.kt create mode 100644 src/main/kotlin/ru/dbotthepony/mc/otm/core/Math.kt diff --git a/src/main/java/ru/dbotthepony/mc/otm/OverdriveThatMatters.java b/src/main/java/ru/dbotthepony/mc/otm/OverdriveThatMatters.java index 308aa834e..353078a31 100644 --- a/src/main/java/ru/dbotthepony/mc/otm/OverdriveThatMatters.java +++ b/src/main/java/ru/dbotthepony/mc/otm/OverdriveThatMatters.java @@ -20,7 +20,7 @@ import net.minecraftforge.fml.event.lifecycle.InterModProcessEvent; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import ru.dbotthepony.mc.otm.block.entity.blackhole.BlackHoleExplosionQueue; +import ru.dbotthepony.mc.otm.block.entity.blackhole.ExplosionQueue; import ru.dbotthepony.mc.otm.capability.MatteryCapability; import ru.dbotthepony.mc.otm.capability.android.AndroidCapability; import ru.dbotthepony.mc.otm.capability.android.AndroidCapabilityPlayer; @@ -131,7 +131,7 @@ public class OverdriveThatMatters { MinecraftForge.EVENT_BUS.register(AndroidCapabilityPlayer.Companion); MinecraftForge.EVENT_BUS.register(AndroidCapability.Companion); MinecraftForge.EVENT_BUS.register(MatterRegistry.class); - MinecraftForge.EVENT_BUS.register(BlackHoleExplosionQueue.Companion); + MinecraftForge.EVENT_BUS.register(ExplosionQueue.Companion); FMLJavaModLoadingContext.get().getModEventBus().register(MatteryCapability.class); diff --git a/src/main/java/ru/dbotthepony/mc/otm/Registry.java b/src/main/java/ru/dbotthepony/mc/otm/Registry.java index 8dfd84e12..9f2c53603 100644 --- a/src/main/java/ru/dbotthepony/mc/otm/Registry.java +++ b/src/main/java/ru/dbotthepony/mc/otm/Registry.java @@ -36,6 +36,8 @@ import ru.dbotthepony.mc.otm.block.*; import ru.dbotthepony.mc.otm.block.entity.*; import ru.dbotthepony.mc.otm.android.AndroidFeatureType; import ru.dbotthepony.mc.otm.block.entity.blackhole.BlockEntityBlackHole; +import ru.dbotthepony.mc.otm.block.entity.blackhole.BlockEntityExplosionDebugger; +import ru.dbotthepony.mc.otm.block.entity.blackhole.BlockEntitySphereDebugger; import ru.dbotthepony.mc.otm.client.render.BlackHoleRenderer; import ru.dbotthepony.mc.otm.client.render.SkinElement; import ru.dbotthepony.mc.otm.core.Fraction; @@ -114,6 +116,10 @@ public class Registry { public static final ResourceLocation ENERGY_COUNTER = new ResourceLocation(OverdriveThatMatters.MOD_ID, "energy_counter"); // нужен рецепт public static final ResourceLocation CHEMICAL_GENERATOR = new ResourceLocation(OverdriveThatMatters.MOD_ID, "chemical_generator"); // нужен рецепт + + public static final ResourceLocation DEBUG_EXPLOSION_SMALL = new ResourceLocation(OverdriveThatMatters.MOD_ID, "debug_explosion_small"); + public static final ResourceLocation DEBUG_SPHERE_POINTS = new ResourceLocation(OverdriveThatMatters.MOD_ID, "debug_sphere_points"); + public static final ResourceLocation BLACK_HOLE = new ResourceLocation(OverdriveThatMatters.MOD_ID, "black_hole"); public static final ResourceLocation CARGO_CRATE = new ResourceLocation(OverdriveThatMatters.MOD_ID, "cargo_crate"); @@ -251,6 +257,9 @@ public class Registry { public static final BlockEnergyCounter ENERGY_COUNTER = new BlockEnergyCounter(); public static final BlockChemicalGenerator CHEMICAL_GENERATOR = new BlockChemicalGenerator(); + public static final BlockExplosionDebugger DEBUG_EXPLOSION_SMALL = new BlockExplosionDebugger(); + public static final BlockSphereDebugger DEBUG_SPHERE_POINTS = new BlockSphereDebugger(); + public static final BlockBlackHole BLACK_HOLE = new BlockBlackHole(); public static final Block TRITANIUM_ORE = new OreBlock( @@ -330,6 +339,9 @@ public class Registry { ENERGY_COUNTER.setRegistryName(Names.ENERGY_COUNTER); CHEMICAL_GENERATOR.setRegistryName(Names.CHEMICAL_GENERATOR); + DEBUG_EXPLOSION_SMALL.setRegistryName(Names.DEBUG_EXPLOSION_SMALL); + DEBUG_SPHERE_POINTS.setRegistryName(Names.DEBUG_SPHERE_POINTS); + TRITANIUM_BLOCK.setRegistryName(Names.TRITANIUM_BLOCK); TRITANIUM_STRIPED_BLOCK.setRegistryName(Names.TRITANIUM_STRIPED_BLOCK); CARBON_FIBRE_BLOCK.setRegistryName(Names.CARBON_FIBRE_BLOCK); @@ -362,6 +374,9 @@ public class Registry { event.getRegistry().register(ENERGY_COUNTER); event.getRegistry().register(CHEMICAL_GENERATOR); + event.getRegistry().register(DEBUG_EXPLOSION_SMALL); + event.getRegistry().register(DEBUG_SPHERE_POINTS); + for (var crate : CRATES) { event.getRegistry().register(crate); } @@ -391,6 +406,9 @@ public class Registry { public static final Item ENERGY_COUNTER = new BlockItem(Blocks.ENERGY_COUNTER, new Item.Properties().stacksTo(64).tab(OverdriveThatMatters.CREATIVE_TAB)); public static final Item CHEMICAL_GENERATOR = new BlockItem(Blocks.CHEMICAL_GENERATOR, new Item.Properties().stacksTo(64).tab(OverdriveThatMatters.CREATIVE_TAB)); + public static final Item DEBUG_EXPLOSION_SMALL = new BlockItem(Blocks.DEBUG_EXPLOSION_SMALL, new Item.Properties().stacksTo(64).tab(OverdriveThatMatters.CREATIVE_TAB)); + public static final Item DEBUG_SPHERE_POINTS = new BlockItem(Blocks.DEBUG_SPHERE_POINTS, new Item.Properties().stacksTo(64).tab(OverdriveThatMatters.CREATIVE_TAB)); + public static final Item TRITANIUM_ORE_CLUMP = new Item(new Item.Properties().stacksTo(64).tab(OverdriveThatMatters.CREATIVE_TAB)); public static final Item TRITANIUM_INGOT = new Item(new Item.Properties().stacksTo(64).tab(OverdriveThatMatters.CREATIVE_TAB)); @@ -489,6 +507,9 @@ public class Registry { ENERGY_COUNTER.setRegistryName(Names.ENERGY_COUNTER); CHEMICAL_GENERATOR.setRegistryName(Names.CHEMICAL_GENERATOR); + DEBUG_EXPLOSION_SMALL.setRegistryName(Names.DEBUG_EXPLOSION_SMALL); + DEBUG_SPHERE_POINTS.setRegistryName(Names.DEBUG_SPHERE_POINTS); + TRITANIUM_ORE.setRegistryName(Names.TRITANIUM_ORE); DEEPSLATE_TRITANIUM_ORE.setRegistryName(Names.DEEPSLATE_TRITANIUM_ORE); TRITANIUM_RAW_BLOCK.setRegistryName(Names.TRITANIUM_RAW_BLOCK); @@ -558,6 +579,9 @@ public class Registry { event.getRegistry().register(ENERGY_COUNTER); event.getRegistry().register(CHEMICAL_GENERATOR); + event.getRegistry().register(DEBUG_EXPLOSION_SMALL); + event.getRegistry().register(DEBUG_SPHERE_POINTS); + event.getRegistry().register(TRITANIUM_ORE); event.getRegistry().register(DEEPSLATE_TRITANIUM_ORE); event.getRegistry().register(TRITANIUM_RAW_BLOCK); @@ -633,6 +657,9 @@ public class Registry { public static final BlockEntityType ENERGY_COUNTER = BlockEntityType.Builder.of(BlockEntityEnergyCounter::new, Blocks.ENERGY_COUNTER).build(null); public static final BlockEntityType CHEMICAL_GENERATOR = BlockEntityType.Builder.of(BlockEntityChemicalGenerator::new, Blocks.CHEMICAL_GENERATOR).build(null); + public static final BlockEntityType DEBUG_EXPLOSION_SMALL = BlockEntityType.Builder.of(BlockEntityExplosionDebugger::new, Blocks.DEBUG_EXPLOSION_SMALL).build(null); + public static final BlockEntityType DEBUG_SPHERE_POINTS = BlockEntityType.Builder.of(BlockEntitySphereDebugger::new, Blocks.DEBUG_SPHERE_POINTS).build(null); + static { ANDROID_STATION.setRegistryName(Names.ANDROID_STATION); BATTERY_BANK.setRegistryName(Names.BATTERY_BANK); @@ -651,6 +678,9 @@ public class Registry { ITEM_MONITOR.setRegistryName(Names.ITEM_MONITOR); ENERGY_COUNTER.setRegistryName(Names.ENERGY_COUNTER); CHEMICAL_GENERATOR.setRegistryName(Names.CHEMICAL_GENERATOR); + + DEBUG_EXPLOSION_SMALL.setRegistryName(Names.DEBUG_EXPLOSION_SMALL); + DEBUG_SPHERE_POINTS.setRegistryName(Names.DEBUG_SPHERE_POINTS); } @SubscribeEvent @@ -673,6 +703,8 @@ public class Registry { event.getRegistry().register(ITEM_MONITOR); event.getRegistry().register(ENERGY_COUNTER); event.getRegistry().register(CHEMICAL_GENERATOR); + event.getRegistry().register(DEBUG_EXPLOSION_SMALL); + event.getRegistry().register(DEBUG_SPHERE_POINTS); // OverdriveThatMatters.LOGGER.info("Registered block entities"); } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/Ext.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/Ext.kt index f345ae6e3..db18c82c4 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/Ext.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/Ext.kt @@ -6,18 +6,9 @@ import net.minecraft.core.Vec3i import net.minecraft.nbt.CompoundTag import net.minecraft.nbt.ListTag import net.minecraft.nbt.Tag +import net.minecraft.world.phys.Vec3 import java.util.function.Consumer -operator fun Vec3i.plus(direction: Vec3i): Vec3i = this.offset(direction) -operator fun Vec3i.plus(direction: Direction): Vec3i = this.offset(direction.normal) -operator fun Vec3i.minus(direction: Vec3i): Vec3i = this.subtract(direction) -operator fun Vec3i.minus(direction: Direction): Vec3i = this.subtract(direction.normal) - -operator fun BlockPos.plus(direction: Vec3i): BlockPos = this.offset(direction) -operator fun BlockPos.plus(direction: Direction): BlockPos = this.offset(direction.normal) -operator fun BlockPos.minus(direction: Vec3i): BlockPos = this.subtract(direction) -operator fun BlockPos.minus(direction: Direction): BlockPos = this.subtract(direction.normal) - operator fun Direction.unaryMinus(): Direction = this.opposite operator fun Vec3i.unaryMinus(): Vec3i = Vec3i(-x, -y, -z) operator fun BlockPos.unaryMinus(): BlockPos = BlockPos(-x, -y, -z) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/BlockExplosionDebugger.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/BlockExplosionDebugger.kt new file mode 100644 index 000000000..900a6d290 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/BlockExplosionDebugger.kt @@ -0,0 +1,50 @@ +package ru.dbotthepony.mc.otm.block + +import net.minecraft.core.BlockPos +import net.minecraft.world.level.Level +import net.minecraft.world.level.block.Block +import net.minecraft.world.level.block.EntityBlock +import net.minecraft.world.level.block.entity.BlockEntity +import net.minecraft.world.level.block.entity.BlockEntityTicker +import net.minecraft.world.level.block.entity.BlockEntityType +import net.minecraft.world.level.block.state.BlockState +import net.minecraft.world.level.material.Material +import ru.dbotthepony.mc.otm.Registry +import ru.dbotthepony.mc.otm.block.entity.blackhole.BlockEntityExplosionDebugger +import ru.dbotthepony.mc.otm.block.entity.blackhole.BlockEntitySphereDebugger + +class BlockExplosionDebugger : Block(Properties.of(Material.STONE)), EntityBlock { + override fun newBlockEntity(p_153215_: BlockPos, p_153216_: BlockState): BlockEntity { + return BlockEntityExplosionDebugger(p_153215_, p_153216_) + } + + override fun getTicker( + p_153212_: Level, + p_153213_: BlockState, + p_153214_: BlockEntityType + ): BlockEntityTicker? { + if (!p_153212_.isClientSide && p_153214_ === Registry.BlockEntities.DEBUG_EXPLOSION_SMALL) { + return BlockEntityTicker {_, _, _, t -> if (t is BlockEntityExplosionDebugger) t.tick()} + } + + return null + } +} + +class BlockSphereDebugger : Block(Properties.of(Material.STONE)), EntityBlock { + override fun newBlockEntity(p_153215_: BlockPos, p_153216_: BlockState): BlockEntity { + return BlockEntitySphereDebugger(p_153215_, p_153216_) + } + + override fun getTicker( + p_153212_: Level, + p_153213_: BlockState, + p_153214_: BlockEntityType + ): BlockEntityTicker? { + if (!p_153212_.isClientSide && p_153214_ === Registry.BlockEntities.DEBUG_SPHERE_POINTS) { + return BlockEntityTicker {_, _, _, t -> if (t is BlockEntitySphereDebugger) t.tick()} + } + + return null + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/BlockEntityChemicalGenerator.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/BlockEntityChemicalGenerator.kt index 49fd16afb..eeb710ed8 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/BlockEntityChemicalGenerator.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/BlockEntityChemicalGenerator.kt @@ -31,6 +31,7 @@ import ru.dbotthepony.mc.otm.block.BlockMatteryRotatable import ru.dbotthepony.mc.otm.block.entity.worker.WorkerState import ru.dbotthepony.mc.otm.capability.MatteryMachineEnergyStorage import ru.dbotthepony.mc.otm.capability.receiveEnergy +import ru.dbotthepony.mc.otm.core.plus import java.lang.ref.WeakReference class BlockEntityChemicalGenerator(pos: BlockPos, state: BlockState) : BlockEntityMattery(Registry.BlockEntities.CHEMICAL_GENERATOR, pos, state) { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/blackhole/BlockEntityBlackHole.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/blackhole/BlockEntityBlackHole.kt index ae08dc613..b03dd7885 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/blackhole/BlockEntityBlackHole.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/blackhole/BlockEntityBlackHole.kt @@ -3,8 +3,6 @@ package ru.dbotthepony.mc.otm.block.entity.blackhole import net.minecraft.client.Minecraft import net.minecraft.core.BlockPos import net.minecraft.nbt.CompoundTag -import net.minecraft.nbt.ListTag -import net.minecraft.nbt.Tag import net.minecraft.network.Connection import net.minecraft.network.protocol.PacketFlow import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket @@ -13,231 +11,22 @@ import net.minecraft.world.entity.Entity import net.minecraft.world.entity.LivingEntity import net.minecraft.world.entity.item.ItemEntity import net.minecraft.world.entity.player.Player -import net.minecraft.world.level.BlockGetter import net.minecraft.world.level.Explosion -import net.minecraft.world.level.ExplosionDamageCalculator -import net.minecraft.world.level.Level import net.minecraft.world.level.block.Block import net.minecraft.world.level.block.Blocks import net.minecraft.world.level.block.entity.BlockEntity import net.minecraft.world.level.block.state.BlockState import net.minecraft.world.level.levelgen.structure.BoundingBox -import net.minecraft.world.level.material.FluidState -import net.minecraft.world.level.saveddata.SavedData import net.minecraft.world.phys.AABB import net.minecraft.world.phys.Vec3 -import net.minecraftforge.event.TickEvent -import net.minecraftforge.eventbus.api.SubscribeEvent import ru.dbotthepony.mc.otm.Registry -import ru.dbotthepony.mc.otm.block.entity.blackhole.BlackHoleExplosionQueue.Companion.factory -import ru.dbotthepony.mc.otm.core.Fraction +import ru.dbotthepony.mc.otm.block.entity.blackhole.ExplosionQueue.Companion.queueForLevel +import ru.dbotthepony.mc.otm.core.* import ru.dbotthepony.mc.otm.matter.MatterRegistry import ru.dbotthepony.mc.otm.set -import java.util.* -import kotlin.collections.ArrayList -import kotlin.math.cos import kotlin.math.pow -import kotlin.math.sin import kotlin.math.sqrt -private object BlackHoleExplosionDamageCalculator : ExplosionDamageCalculator() { - override fun getBlockExplosionResistance( - explosion: Explosion, - getter: BlockGetter, - pos: BlockPos, - state: BlockState, - fstate: FluidState - ): Optional { - return if (state.isAir && fstate.isEmpty) Optional.empty() else Optional.of( - Math.sqrt( - Math.max( - 0f, - Math.max( - state.getExplosionResistance(getter, pos, explosion), - fstate.getExplosionResistance(getter, pos, explosion) - ) - ).toDouble() - ).toFloat() - ) - } -} - -private data class RingExplosion(val x: Double, val y: Double, val z: Double, val radius: Double, val strength: Float) { - fun serializeNBT(): CompoundTag { - return CompoundTag().also { - it["x"] = x - it["y"] = y - it["z"] = z - it["radius"] = radius - it["strength"] = strength - } - } - - fun explode(queue: BlackHoleExplosionQueue) { - val fragments = radius.toInt() * 8 - val stackStep = Math.PI / fragments.toDouble() - val sectorStep = Math.PI / fragments.toDouble() * 2 - - for (stack in 0 until fragments) { - val stackAngle = Math.PI / 2 - stack * stackStep - val xy = radius * 15 * cos(stackAngle) - val z = radius * 15 * sin(stackAngle) - - for (sector in 0 until fragments) { - val sectorAngle = sector * sectorStep - - queue.explode( - this.x + xy * cos(sectorAngle), - this.y + xy * sin(sectorAngle), - this.z + z, - strength - ) - } - } - } - - companion object { - @JvmStatic - fun deserializeNBT(tag: CompoundTag): RingExplosion { - return RingExplosion( - tag.getDouble("x"), - tag.getDouble("y"), - tag.getDouble("z"), - tag.getDouble("radius"), - tag.getFloat("strength") - ) - } - } -} - -private data class QueuedExplosion(val x: Double, val y: Double, val z: Double, val radius: Float) { - fun serializeNBT(): CompoundTag { - return CompoundTag().also { - it["x"] = x - it["y"] = y - it["z"] = z - it["radius"] = radius - } - } - - fun explode(level: Level) { - level.explode( - null, - Registry.DAMAGE_HAWKING_RADIATION, - BlackHoleExplosionDamageCalculator, - x, - y, - z, - radius, - false, - Explosion.BlockInteraction.DESTROY - ) - } - - companion object { - fun deserializeNBT(tag: CompoundTag): QueuedExplosion { - return QueuedExplosion(tag.getDouble("x"), tag.getDouble("y"), tag.getDouble("z"), tag.getFloat("radius")) - } - } -} - -class BlackHoleExplosionQueue(private val level: ServerLevel) : SavedData() { - private var indexExplosion = 0 - private var indexRing = 0 - private val explosions = ArrayList() - private val rings = ArrayList() - - override fun save(tag: CompoundTag): CompoundTag { - val listExplosions = ListTag() - val listRings = ListTag() - - for (i in indexExplosion until explosions.size) - listExplosions.add(explosions[i].serializeNBT()) - - for (i in indexRing until rings.size) - listRings.add(rings[i].serializeNBT()) - - tag["explosions"] = listExplosions - tag["rings"] = listRings - - return tag - } - - fun load(tag: CompoundTag) { - explosions.clear() - rings.clear() - - indexExplosion = 0 - indexRing = 0 - - for (explosion in tag.getList("explosions", Tag.TAG_COMPOUND.toInt())) - explosions.add(QueuedExplosion.deserializeNBT(explosion as CompoundTag)) - - for (ring in tag.getList("rings", Tag.TAG_COMPOUND.toInt())) - rings.add(RingExplosion.deserializeNBT(ring as CompoundTag)) - } - - fun explode(x: Double, y: Double, z: Double, radius: Float) { - explosions.add(QueuedExplosion(x, y, z, radius)) - isDirty = true - } - - fun explodeRing(x: Double, y: Double, z: Double, radius: Double, strength: Float) { - rings.add(RingExplosion(x, y, z, radius, strength)) - isDirty = true - } - - fun tick() { - if (explosions.size != 0) { - setDirty() - var iterations = 0 - - for (i in indexExplosion until explosions.size) { - explosions[i].explode(level) - indexExplosion++ - - if (iterations++ == 4) { - break - } - } - - if (indexExplosion >= explosions.size) { - indexExplosion = 0 - explosions.clear() - } - } else if (rings.size != 0) { - if (indexRing >= rings.size) { - indexRing = 0 - rings.clear() - } else { - rings[indexRing++].explode(this) - } - } - } - - companion object { - @JvmStatic - fun factory(level: ServerLevel): BlackHoleExplosionQueue { - return level.dataStorage.computeIfAbsent( - { - val factory = BlackHoleExplosionQueue(level) - factory.load(it) - factory - }, - { BlackHoleExplosionQueue(level) }, - "otm_blackhole_explosion_queue" - ) - } - - @SubscribeEvent - fun onWorldTick(event: TickEvent.WorldTickEvent) { - if (event.phase == TickEvent.Phase.START && event.world is ServerLevel) { - factory(event.world as ServerLevel).tick() - } - } - } -} - class BlockEntityBlackHole(p_155229_: BlockPos, p_155230_: BlockState) : BlockEntity(Registry.BlockEntities.BLACK_HOLE, p_155229_, p_155230_) { var mass = BASELINE_MASS set(mass) { @@ -291,7 +80,7 @@ class BlockEntityBlackHole(p_155229_: BlockPos, p_155230_: BlockState) : BlockEn val x0 = blockPos.x + 0.5 val y0 = blockPos.y + 0.5 val z0 = blockPos.z + 0.5 - val queue = factory(level) + val queue = queueForLevel(level) var radius = 0 while (radius < Math.ceil(gravitationStrength * 4)) { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/blackhole/BlockEntityExplosionDebugger.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/blackhole/BlockEntityExplosionDebugger.kt new file mode 100644 index 000000000..aa48134c9 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/blackhole/BlockEntityExplosionDebugger.kt @@ -0,0 +1,49 @@ +package ru.dbotthepony.mc.otm.block.entity.blackhole + +import net.minecraft.core.BlockPos +import net.minecraft.server.level.ServerLevel +import net.minecraft.world.level.block.Block +import net.minecraft.world.level.block.Blocks +import net.minecraft.world.level.block.entity.BlockEntity +import net.minecraft.world.level.block.state.BlockState +import net.minecraft.world.phys.Vec3 +import ru.dbotthepony.mc.otm.Registry +import ru.dbotthepony.mc.otm.core.plus +import ru.dbotthepony.mc.otm.core.times + +class BlockEntityExplosionDebugger(p_155229_: BlockPos, p_155230_: BlockState) : BlockEntity(Registry.BlockEntities.DEBUG_EXPLOSION_SMALL, p_155229_, p_155230_) { + private var hive: ExplosionRayHive? = null + + fun tick() { + if (hive == null) { + val hive = ExplosionRayHive(level as ServerLevel) + this.hive = hive + + val tx = blockPos.x.toDouble() + 0.5 + val ty = blockPos.y.toDouble() + 0.5 + val tz = blockPos.z.toDouble() + 0.5 + val tpos = Vec3(tx, ty, tz) + + for (normal in ExplosionRayHive.evenlyDistributedPoints(1000)) { + hive.addRay(normal + tpos, normal, 200.0) + } + } + + hive!!.step() + } +} + +class BlockEntitySphereDebugger(p_155229_: BlockPos, p_155230_: BlockState) : BlockEntity(Registry.BlockEntities.DEBUG_SPHERE_POINTS, p_155229_, p_155230_) { + private var placed = false + + fun tick() { + if (!placed) { + placed = true + + for (normal in ExplosionRayHive.evenlyDistributedPoints(400)) { + val multiplied = normal * 20.0 + level!!.setBlock(blockPos + BlockPos(multiplied.x, multiplied.y, multiplied.z), Blocks.COAL_BLOCK.defaultBlockState(), Block.UPDATE_ALL) + } + } + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/blackhole/Explosions.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/blackhole/Explosions.kt new file mode 100644 index 000000000..c55260b1c --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/block/entity/blackhole/Explosions.kt @@ -0,0 +1,678 @@ +package ru.dbotthepony.mc.otm.block.entity.blackhole + +import net.minecraft.core.BlockPos +import net.minecraft.nbt.CompoundTag +import net.minecraft.nbt.DoubleTag +import net.minecraft.nbt.ListTag +import net.minecraft.nbt.Tag +import net.minecraft.server.level.ServerLevel +import net.minecraft.world.level.BlockGetter +import net.minecraft.world.level.Explosion +import net.minecraft.world.level.ExplosionDamageCalculator +import net.minecraft.world.level.Level +import net.minecraft.world.level.block.Block +import net.minecraft.world.level.block.Blocks +import net.minecraft.world.level.block.state.BlockState +import net.minecraft.world.level.material.FluidState +import net.minecraft.world.level.saveddata.SavedData +import net.minecraft.world.phys.Vec3 +import net.minecraftforge.event.TickEvent +import net.minecraftforge.eventbus.api.SubscribeEvent +import org.apache.logging.log4j.LogManager +import ru.dbotthepony.mc.otm.Registry +import ru.dbotthepony.mc.otm.block.BlockExplosionDebugger +import ru.dbotthepony.mc.otm.core.* +import ru.dbotthepony.mc.otm.core.Vector +import ru.dbotthepony.mc.otm.set +import java.util.* +import kotlin.collections.ArrayList +import kotlin.collections.HashMap +import kotlin.math.acos +import kotlin.math.cos +import kotlin.math.sin +import kotlin.math.sqrt + +private fun round(v: Double): Int { + return (v + 0.5).toInt() +} + +private val sphere = arrayOf( + // ядро + BlockPos(-1, -1, -1), + BlockPos(-1, -1, 0), + BlockPos(-1, -1, 1), + BlockPos(-1, 0, -1), + BlockPos(-1, 0, 0), + BlockPos(-1, 0, 1), + BlockPos(-1, 1, -1), + BlockPos(-1, 1, 0), + BlockPos(-1, 1, 1), + BlockPos(0, -1, -1), + BlockPos(0, -1, 0), + BlockPos(0, -1, 1), + BlockPos(0, 0, -1), + BlockPos(0, 0, 0), + BlockPos(0, 0, 1), + BlockPos(0, 1, -1), + BlockPos(0, 1, 0), + BlockPos(0, 1, 1), + BlockPos(1, -1, -1), + BlockPos(1, -1, 0), + BlockPos(1, -1, 1), + BlockPos(1, 0, -1), + BlockPos(1, 0, 0), + BlockPos(1, 0, 1), + BlockPos(1, 1, -1), + BlockPos(1, 1, 0), + BlockPos(1, 1, 1), + + // столбы + BlockPos(-2, 0, 0), + BlockPos(2, 0, 0), + BlockPos(0, 2, 0), + BlockPos(0, -2, 0), + BlockPos(0, 0, 2), + BlockPos(0, 0, -2), +) + +private val initialDirections = arrayOf( + Vector(1.0, 0.0, 0.0), + Vector(-1.0, 0.0, 0.0), + Vector(0.0, 1.0, 0.0), + Vector(0.0, -1.0, 0.0), + Vector(0.0, 0.0, 1.0), + Vector(0.0, 0.0, -1.0), + + Vector(-1.0, -1.0, -1.0).normalize(), + Vector(1.0, -1.0, 1.0).normalize(), + Vector(1.0, -1.0, -1.0).normalize(), + Vector(-1.0, -1.0, 1.0).normalize(), + Vector(-1.0, 1.0, -1.0).normalize(), + Vector(1.0, 1.0, 1.0).normalize(), + Vector(1.0, 1.0, -1.0).normalize(), + Vector(-1.0, 1.0, 1.0).normalize(), +) + +class ExplosionSphere(val hive: ExplosionSphereHive, var pos: Vec3, var stepVelocity: Vec3, var force: Double) { + val initialPos = pos + private var lastSplitPos = pos + val level: ServerLevel get() = hive.level + val blockPos: BlockPos get() = BlockPos(round(pos.x), round(pos.y), round(pos.z)) + + fun travelled(): Double { + return pos.distanceTo(initialPos) + } + + private fun travelledFromLastSplit(): Double { + return pos.distanceTo(lastSplitPos) + } + + fun step(): Boolean { + if (force <= 0.0) { + return false + } + + val blockPos = blockPos + + for (point in sphere) { + val finalPos = blockPos + point + val block = level.getBlockState(finalPos) + + if (!block.isAir && block.block !is BlockExplosionDebugger) { + val explosion = Explosion(level, null, null, null, pos.x, pos.y, pos.z, force.toFloat(),false, Explosion.BlockInteraction.BREAK) + val explosionResistance = block.getExplosionResistance(level, blockPos, explosion) + + if (explosionResistance > force) { + // поглощено + // TODO: вместо полного поглощения отражение + force = 0.0 + return false + } else { + // взорвано + force -= explosionResistance + + // TODO: дропы когда будет добавлена более общая версия + level.setBlock(blockPos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL) + } + } + } + + pos += stepVelocity + + force -= 0.4 + + if (travelledFromLastSplit() >= TRAVEL_TO_SPLIT && force >= 10) { + force /= 2.0 + + val up = stepVelocity.up() + val left = stepVelocity.left() + + val a = stepVelocity.rotateAroundAxis(up, Math.PI / 4) + val b = stepVelocity.rotateAroundAxis(up, -Math.PI / 4) + val c = stepVelocity.rotateAroundAxis(left, Math.PI / 4) + val d = stepVelocity.rotateAroundAxis(left, -Math.PI / 4) + + hive.addRay(pos, a, force) + hive.addRay(pos, b, force) + hive.addRay(pos, c, force) + hive.addRay(pos, d, force) + + lastSplitPos = pos + } + + return force > 0.0 + } + + fun serializeNbt(): CompoundTag { + return CompoundTag() + } + + companion object { + const val TRAVEL_TO_SPLIT = 4.0 + + fun deserializeNbt(hive: ExplosionSphereHive, tag: CompoundTag): ExplosionSphere { + return ExplosionSphere(hive, Vector.ZERO, Vector.ZERO, 0.0) + } + } +} + +class ExplosionSphereHive(val level: ServerLevel) { + private val spheres = ArrayList() + + var stepNumber = 0 + private set + + fun addRay(pos: Vec3, stepVelocity: Vec3, force: Double) { + spheres.add(ExplosionSphere(this, pos, stepVelocity, force)) + } + + fun addDefaultRays(pos: Vec3, force: Double) { + for (stepVelocity in initialDirections) { + addRay(pos, stepVelocity, force * 8) + } + } + + fun step() { + stepNumber++ + + val toRemove = ArrayList() + + for (i in 0 until spheres.size) { + if (!spheres[i].step()) { + toRemove.add(i) + } + } + + for (i in toRemove.size - 1 downTo 0) { + spheres.removeAt(toRemove[i]) + } + } + + fun serializeNbt(): CompoundTag { + return CompoundTag().also { + it["spheres"] = ListTag().also { + for (ray in spheres) + it.add(ray.serializeNbt()) + } + } + } + + fun deserializeNbt(tag: CompoundTag) { + (tag["spheres"] as? ListTag)?.also { + for (elem in it) { + spheres.add(ExplosionSphere.deserializeNbt(this, elem as CompoundTag)) + } + } + } + + fun isEmpty(): Boolean { + return spheres.isEmpty() + } +} + +class ExplosionRay(val hive: ExplosionRayHive, var pos: Vec3, var stepVelocity: Vec3, var force: Double) { + val initialPos = pos + private var lastSplitPos = pos + val level: ServerLevel get() = hive.level + val blockPos: BlockPos get() = BlockPos(round(pos.x), round(pos.y), round(pos.z)) + private var prev: MutableInt? = null + + fun travelled(): Double { + return pos.distanceTo(initialPos) + } + + private fun travelledFromLastSplit(): Double { + return pos.distanceTo(lastSplitPos) + } + + fun step(spread: Boolean): Boolean { + if (force <= 0.0) { + return false + } + + val blockPos = blockPos + + if (!level.isInWorldBounds(blockPos)) + return false + + // val chunk = level.chunkSource.getChunkNow(SectionPos.blockToSectionCoord(blockPos.x), SectionPos.blockToSectionCoord(blockPos.z)) ?: return true + val block = level.getBlockState(blockPos) + + if (!block.isAir && block.block !is BlockExplosionDebugger) { + val explosion = Explosion(level, null, null, null, pos.x, pos.y, pos.z, force.toFloat(),false, Explosion.BlockInteraction.BREAK) + val explosionResistance = block.getExplosionResistance(level, blockPos, explosion) + + if (explosionResistance > force) { + // поглощено + // TODO: вместо полного поглощения отражение + force = 0.0 + return false + } else { + // взорвано + force -= explosionResistance + + // TODO: дропы когда будет добавлена более общая версия + level.setBlock(blockPos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL) + } + } + + val old = this.blockPos + pos += stepVelocity + val new = this.blockPos + + if (old != new) { + val prev = prev + + if (prev != null) { + prev.dec() + + if (prev.value <= 0) { + hive.checkKey(old) + } + } + + this.prev = hive.incDensity(new) + } + + force -= 0.4 + + if (spread && travelledFromLastSplit() >= TRAVEL_TO_SPLIT * sqrt(travelled() / 9.0) && force >= 10) { + force /= 2.0 + + val up = stepVelocity.up() + val left = stepVelocity.left() + + val a = stepVelocity.rotateAroundAxis(up, Math.PI / 5.5) + val b = stepVelocity.rotateAroundAxis(up, -Math.PI / 5.5) + val c = stepVelocity.rotateAroundAxis(left, Math.PI / 5.5) + val d = stepVelocity.rotateAroundAxis(left, -Math.PI / 5.5) + + hive.addRay(pos, a, force) + hive.addRay(pos, b, force) + hive.addRay(pos, c, force) + hive.addRay(pos, d, force) + + lastSplitPos = pos + } + + return force > 0f + } + + fun serializeNbt(): CompoundTag { + return CompoundTag().also { + it["pos"] = ListTag().also { + it.add(DoubleTag.valueOf(pos.x)) + it.add(DoubleTag.valueOf(pos.y)) + it.add(DoubleTag.valueOf(pos.z)) + } + + it["stepVelocity"] = ListTag().also { + it.add(DoubleTag.valueOf(stepVelocity.x)) + it.add(DoubleTag.valueOf(stepVelocity.y)) + it.add(DoubleTag.valueOf(stepVelocity.z)) + } + + it["force"] = force + } + } + + companion object { + const val TRAVEL_TO_SPLIT = 4.0 + + fun deserializeNbt(hive: ExplosionRayHive, tag: CompoundTag): ExplosionRay { + val pos = tag["pos"] as ListTag + val stepVelocity = tag["stepVelocity"] as ListTag + + return ExplosionRay(hive, + Vector((pos[0] as DoubleTag).asDouble, (pos[1] as DoubleTag).asDouble, (pos[2] as DoubleTag).asDouble), + Vector((stepVelocity[0] as DoubleTag).asDouble, (stepVelocity[1] as DoubleTag).asDouble, (stepVelocity[2] as DoubleTag).asDouble), + (tag["force"] as DoubleTag).asDouble + ) + } + } +} + +data class MutableInt(var value: Int = 0) { + fun inc() {value++} + fun dec() {value--} + fun zero() = value <= 0 +} + +class ExplosionRayHive(val level: ServerLevel) { + private val rays = ArrayList() + private val densityMap = HashMap() + var stepNumber = 0 + private set + + fun incDensity(pos: BlockPos): MutableInt { + return densityMap.computeIfAbsent(pos) {MutableInt()}.also(MutableInt::inc) + } + + fun decDensity(pos: BlockPos) { + val value = densityMap.computeIfAbsent(pos) {MutableInt()} + value.dec() + + if (value.zero()) { + densityMap.remove(pos) + } + } + + fun checkKey(pos: BlockPos) { + val value = densityMap.computeIfAbsent(pos) {MutableInt()} + + if (value.zero()) { + densityMap.remove(pos) + } + } + + fun addRay(pos: Vec3, stepVelocity: Vec3, force: Double) { + rays.add(ExplosionRay(this, pos, stepVelocity, force)) + } + + fun step() { + stepNumber++ + + val toRemove = ArrayList() + val density = calculateDensity() + + for (i in 0 until rays.size) { + if (!rays[i].step(density < 3.0)) { + toRemove.add(i) + decDensity(rays[i].blockPos) + } + } + + for (i in toRemove.size - 1 downTo 0) { + rays.removeAt(toRemove[i]) + } + + if (stepNumber % 4 == 0) { + LOGGER.info("At step {} density of hive {} with {} elements is {}", stepNumber, this, rays.size, density) + } + } + + fun calculateDensity(): Double { + return rays.size.toDouble() / densityMap.size + } + + fun serializeNbt(): CompoundTag { + return CompoundTag().also { + it["rays"] = ListTag().also { + for (ray in rays) + it.add(ray.serializeNbt()) + } + } + } + + fun deserializeNbt(tag: CompoundTag) { + (tag["rays"] as? ListTag)?.also { + for (elem in it) { + rays.add(ExplosionRay.deserializeNbt(this, elem as CompoundTag)) + } + } + } + + fun isEmpty(): Boolean { + return rays.isEmpty() + } + + companion object { + private val LOGGER = LogManager.getLogger() + private val SQUARE_5 = sqrt(5.0) + + fun evenlyDistributedPoints(amount: Int): List { + val list = ArrayList() + + for (i in 0 .. amount) { + val idx = i.toDouble() / amount + val phi = acos(1.0 - 2.0 * idx) + val theta = Math.PI * (1.0 + SQUARE_5) * i + + list.add(Vec3(cos(theta) * sin(phi), sin(theta) * sin(phi), cos(phi))) + } + + return list + } + } +} + +private object BlackHoleExplosionDamageCalculator : ExplosionDamageCalculator() { + override fun getBlockExplosionResistance( + explosion: Explosion, + getter: BlockGetter, + pos: BlockPos, + state: BlockState, + fstate: FluidState + ): Optional { + return if (state.isAir && fstate.isEmpty) Optional.empty() else Optional.of( + Math.sqrt( + Math.max( + 0f, + Math.max( + state.getExplosionResistance(getter, pos, explosion), + fstate.getExplosionResistance(getter, pos, explosion) + ) + ).toDouble() + ).toFloat() + ) + } +} + +private data class RingExplosion(val x: Double, val y: Double, val z: Double, val radius: Double, val strength: Float) { + fun serializeNBT(): CompoundTag { + return CompoundTag().also { + it["x"] = x + it["y"] = y + it["z"] = z + it["radius"] = radius + it["strength"] = strength + } + } + + fun explode(queue: ExplosionQueue) { + for (pos in ExplosionRayHive.evenlyDistributedPoints(radius.toInt() * 80)) { + val (x, y, z) = pos + + queue.explode( + this.x + x * radius * 15, + this.y + y * radius * 15, + this.z + z * radius * 15, + strength + ) + } + } + + companion object { + @JvmStatic + fun deserializeNBT(tag: CompoundTag): RingExplosion { + return RingExplosion( + tag.getDouble("x"), + tag.getDouble("y"), + tag.getDouble("z"), + tag.getDouble("radius"), + tag.getFloat("strength") + ) + } + } +} + +private data class QueuedExplosion(val x: Double, val y: Double, val z: Double, val radius: Float) { + fun serializeNBT(): CompoundTag { + return CompoundTag().also { + it["x"] = x + it["y"] = y + it["z"] = z + it["radius"] = radius + } + } + + fun explode(level: Level) { + level.explode( + null, + Registry.DAMAGE_HAWKING_RADIATION, + BlackHoleExplosionDamageCalculator, + x, + y, + z, + radius, + false, + Explosion.BlockInteraction.DESTROY + ) + } + + companion object { + fun deserializeNBT(tag: CompoundTag): QueuedExplosion { + return QueuedExplosion(tag.getDouble("x"), tag.getDouble("y"), tag.getDouble("z"), tag.getFloat("radius")) + } + } +} + +class ExplosionQueue(private val level: ServerLevel) : SavedData() { + private var indexExplosion = 0 + private var indexRing = 0 + private val explosions = ArrayList() + private val rings = ArrayList() + private val hives = ArrayList() + + override fun save(tag: CompoundTag): CompoundTag { + val listExplosions = ListTag() + val listRings = ListTag() + + for (i in indexExplosion until explosions.size) + listExplosions.add(explosions[i].serializeNBT()) + + for (i in indexRing until rings.size) + listRings.add(rings[i].serializeNBT()) + + tag["explosions"] = listExplosions + tag["rings"] = listRings + // tag["hives"] = ListTag().also { + // for (hive in hives) { + // it.add(hive.serializeNbt()) + // } + // } + + return tag + } + + fun load(tag: CompoundTag) { + explosions.clear() + rings.clear() + hives.clear() + + indexExplosion = 0 + indexRing = 0 + + for (explosion in tag.getList("explosions", Tag.TAG_COMPOUND.toInt())) + explosions.add(QueuedExplosion.deserializeNBT(explosion as CompoundTag)) + + for (ring in tag.getList("rings", Tag.TAG_COMPOUND.toInt())) + rings.add(RingExplosion.deserializeNBT(ring as CompoundTag)) + + for (hive in tag.getList("hives", Tag.TAG_COMPOUND.toInt())) + hives.add(ExplosionRayHive(level).also { it.deserializeNbt(hive as CompoundTag) }) + } + + fun explode(x: Double, y: Double, z: Double, radius: Float) { + if (level.isOutsideBuildHeight(BlockPos(x, y + 24, z)) || level.isOutsideBuildHeight(BlockPos(x, y - 24, z))) + return + + explosions.add(QueuedExplosion(x, y, z, radius)) + isDirty = true + } + + fun explodeRing(x: Double, y: Double, z: Double, radius: Double, strength: Float) { + rings.add(RingExplosion(x, y, z, radius, strength)) + isDirty = true + } + + fun explodeRays(tpos: Vector, force: Double) { + val hive = ExplosionRayHive(level) + hives.add(hive) + + for (normal in ExplosionRayHive.evenlyDistributedPoints(1000)) { + hive.addRay(normal + tpos, normal, force) + } + + isDirty = true + } + + fun tick() { + if (explosions.size != 0) { + isDirty = true + var iterations = 0 + + for (i in indexExplosion until explosions.size) { + explosions[i].explode(level) + indexExplosion++ + + if (iterations++ == 4) { + break + } + } + + if (indexExplosion >= explosions.size) { + indexExplosion = 0 + explosions.clear() + } + } else if (rings.size != 0) { + if (indexRing >= rings.size) { + indexRing = 0 + rings.clear() + } else { + rings[indexRing++].explode(this) + } + } + + for (i in hives.size - 1 downTo 0) { + isDirty = true + hives[i].step() + + if (hives[i].isEmpty()) { + hives.removeAt(i) + } + } + } + + companion object { + @JvmStatic + fun queueForLevel(level: ServerLevel): ExplosionQueue { + return level.dataStorage.computeIfAbsent( + { + val factory = ExplosionQueue(level) + factory.load(it) + factory + }, + { ExplosionQueue(level) }, + "otm_blackhole_explosion_queue" + ) + } + + @SubscribeEvent + fun onWorldTick(event: TickEvent.WorldTickEvent) { + if (event.phase == TickEvent.Phase.START && event.world is ServerLevel) { + queueForLevel(event.world as ServerLevel).tick() + } + } + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/core/Math.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/core/Math.kt new file mode 100644 index 000000000..0a634df46 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/core/Math.kt @@ -0,0 +1,115 @@ +package ru.dbotthepony.mc.otm.core + +import net.minecraft.core.BlockPos +import net.minecraft.core.Direction +import net.minecraft.core.Vec3i +import net.minecraft.world.phys.Vec3 +import kotlin.math.sin +import kotlin.math.cos +import kotlin.math.acos + +typealias Vector = Vec3 + +val VECTOR_UP = Vector(0.0, 1.0, 0.0) +val VECTOR_DOWN = Vector(0.0, -1.0, 0.0) +val VECTOR_BACK = Vector(0.0, 0.0, 1.0) + +fun BlockPos.asVector(): Vector { + return Vector(x + 0.5, y + 0.5, z + 0.5) +} + +fun Vector.asAngle(): Angle { + val norm = normalize() + + if (norm.x < 0) { + return Angle(acos(norm.dot(VECTOR_UP)) - Angle.PI_HALF, -acos(Vector(norm.x, 0.0, norm.z).normalize().dot(VECTOR_BACK))) + } + + return Angle(acos(norm.dot(VECTOR_UP)) - Angle.PI_HALF, acos(Vector(norm.x, 0.0, norm.z).normalize().dot(VECTOR_BACK))) +} + +operator fun BlockPos.plus(direction: Vec3i): BlockPos = this.offset(direction) +operator fun BlockPos.plus(direction: Direction): BlockPos = this.offset(direction.normal) +operator fun BlockPos.minus(direction: Vec3i): BlockPos = this.subtract(direction) +operator fun BlockPos.minus(direction: Direction): BlockPos = this.subtract(direction.normal) + +operator fun Vec3i.plus(direction: Vec3i): Vec3i = this.offset(direction) +operator fun Vec3i.plus(direction: Direction): Vec3i = this.offset(direction.normal) +operator fun Vec3i.minus(direction: Vec3i): Vec3i = this.subtract(direction) +operator fun Vec3i.minus(direction: Direction): Vec3i = this.subtract(direction.normal) + +operator fun Vector.plus(direction: Vector): Vector = this.add(direction) +operator fun Vector.minus(direction: Vector): Vector = this.subtract(direction) + +operator fun Vector.times(v: Double): Vector = this.multiply(v, v, v) + +fun Vector.left() = asAngle().left() * this.length() +fun Vector.right() = asAngle().right() * this.length() +fun Vector.up() = asAngle().up() * this.length() +fun Vector.down() = asAngle().down() * this.length() + +fun Vector.rotateAroundAxis(axis: Vector, rotation: Double): Vector { + return this * cos(rotation) + axis.cross(this) * sin(rotation) + axis * axis.dot(this) * (1 - cos(rotation)) +} + +operator fun Vector.component1() = x +operator fun Vector.component2() = y +operator fun Vector.component3() = z + +data class Angle(val pitch: Double = 0.0, val yaw: Double = 0.0, val roll: Double = 0.0) { + fun forward(): Vector { + val yaw = yaw - PI_HALF + val x = cos(yaw) * cos(pitch) + val y = -sin(pitch) + val z = sin(yaw) * cos(pitch) + + return Vector(x, y, z) + } + + fun right(): Vector { + val pitch = 0.0 + val x = cos(yaw) * cos(pitch) + val y = -sin(pitch) + val z = sin(yaw) * cos(pitch) + + return Vector(x, y, z) + } + + fun left(): Vector { + val yaw = yaw + PI_HALF + val pitch = 0.0 + val x = cos(yaw) * cos(pitch) + val y = -sin(pitch) + val z = sin(yaw) * cos(pitch) + + return Vector(x, y, z) + } + + fun up(): Vector { + val yaw = yaw - PI_HALF + val pitch = pitch - PI_HALF + val x = cos(yaw) * cos(pitch) + val y = -sin(pitch) + val z = sin(yaw) * cos(pitch) + + return Vector(x, y, z) + } + + fun down(): Vector { + val yaw = yaw - PI_HALF + val pitch = pitch + PI_HALF + val x = cos(yaw) * cos(pitch) + val y = -sin(pitch) + val z = sin(yaw) * cos(pitch) + + return Vector(x, y, z) + } + + companion object { + const val PI_HALF = Math.PI / 2.0 + + fun degrees(pitch: Double, yaw: Double, roll: Double): Angle { + return Angle(Math.toDegrees(pitch), Math.toDegrees(yaw), Math.toDegrees(roll)) + } + } +} diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/graph/Abstract6Graph.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/graph/Abstract6Graph.kt index f0780036b..7b3b2f983 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/graph/Abstract6Graph.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/graph/Abstract6Graph.kt @@ -6,7 +6,7 @@ import net.minecraft.core.SectionPos import net.minecraft.server.level.ServerLevel import net.minecraft.world.level.block.entity.BlockEntity import ru.dbotthepony.mc.otm.OverdriveThatMatters -import ru.dbotthepony.mc.otm.plus +import ru.dbotthepony.mc.otm.core.plus import java.util.* import kotlin.collections.ArrayList