Attempt new blackhole explosion algos, but nothing works

This commit is contained in:
DBotThePony 2022-01-10 18:55:30 +07:00
parent 1fae349547
commit c28ff851c9
Signed by: DBot
GPG Key ID: DCC23B5715498507
10 changed files with 932 additions and 227 deletions

View File

@ -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);

View File

@ -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<BlockEntityEnergyCounter> ENERGY_COUNTER = BlockEntityType.Builder.of(BlockEntityEnergyCounter::new, Blocks.ENERGY_COUNTER).build(null);
public static final BlockEntityType<BlockEntityChemicalGenerator> CHEMICAL_GENERATOR = BlockEntityType.Builder.of(BlockEntityChemicalGenerator::new, Blocks.CHEMICAL_GENERATOR).build(null);
public static final BlockEntityType<BlockEntityExplosionDebugger> DEBUG_EXPLOSION_SMALL = BlockEntityType.Builder.of(BlockEntityExplosionDebugger::new, Blocks.DEBUG_EXPLOSION_SMALL).build(null);
public static final BlockEntityType<BlockEntitySphereDebugger> 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");
}

View File

@ -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)

View File

@ -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 <T : BlockEntity?> getTicker(
p_153212_: Level,
p_153213_: BlockState,
p_153214_: BlockEntityType<T>
): BlockEntityTicker<T>? {
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 <T : BlockEntity?> getTicker(
p_153212_: Level,
p_153213_: BlockState,
p_153214_: BlockEntityType<T>
): BlockEntityTicker<T>? {
if (!p_153212_.isClientSide && p_153214_ === Registry.BlockEntities.DEBUG_SPHERE_POINTS) {
return BlockEntityTicker {_, _, _, t -> if (t is BlockEntitySphereDebugger) t.tick()}
}
return null
}
}

View File

@ -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) {

View File

@ -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<Float> {
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<QueuedExplosion>()
private val rings = ArrayList<RingExplosion>()
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)) {

View File

@ -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)
}
}
}
}

View File

@ -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<ExplosionSphere>()
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<Int>()
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<ExplosionRay>()
private val densityMap = HashMap<BlockPos, MutableInt>()
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<Int>()
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<Vec3> {
val list = ArrayList<Vec3>()
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<Float> {
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<QueuedExplosion>()
private val rings = ArrayList<RingExplosion>()
private val hives = ArrayList<ExplosionRayHive>()
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()
}
}
}
}

View File

@ -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))
}
}
}

View File

@ -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