Move black holes to kotlin
This commit is contained in:
parent
3039bff89b
commit
ec6c903b6f
@ -20,7 +20,7 @@ import net.minecraftforge.fml.event.lifecycle.InterModProcessEvent;
|
|||||||
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
|
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import ru.dbotthepony.mc.otm.block.entity.BlockEntityBlackHole;
|
import ru.dbotthepony.mc.otm.block.entity.blackhole.BlackHoleExplosionQueue;
|
||||||
import ru.dbotthepony.mc.otm.capability.MatteryCapability;
|
import ru.dbotthepony.mc.otm.capability.MatteryCapability;
|
||||||
import ru.dbotthepony.mc.otm.capability.android.AndroidCapability;
|
import ru.dbotthepony.mc.otm.capability.android.AndroidCapability;
|
||||||
import ru.dbotthepony.mc.otm.capability.android.AndroidCapabilityPlayer;
|
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(AndroidCapabilityPlayer.Companion);
|
||||||
MinecraftForge.EVENT_BUS.register(AndroidCapability.Companion);
|
MinecraftForge.EVENT_BUS.register(AndroidCapability.Companion);
|
||||||
MinecraftForge.EVENT_BUS.register(MatterRegistry.class);
|
MinecraftForge.EVENT_BUS.register(MatterRegistry.class);
|
||||||
MinecraftForge.EVENT_BUS.register(BlockEntityBlackHole.BlackHoleExplosionQueue.class);
|
MinecraftForge.EVENT_BUS.register(BlackHoleExplosionQueue.Companion);
|
||||||
|
|
||||||
FMLJavaModLoadingContext.get().getModEventBus().register(MatteryCapability.class);
|
FMLJavaModLoadingContext.get().getModEventBus().register(MatteryCapability.class);
|
||||||
|
|
||||||
|
@ -35,6 +35,7 @@ import ru.dbotthepony.mc.otm.android.feature.AndroidNanobotsRegeneration;
|
|||||||
import ru.dbotthepony.mc.otm.block.*;
|
import ru.dbotthepony.mc.otm.block.*;
|
||||||
import ru.dbotthepony.mc.otm.block.entity.*;
|
import ru.dbotthepony.mc.otm.block.entity.*;
|
||||||
import ru.dbotthepony.mc.otm.android.AndroidFeatureType;
|
import ru.dbotthepony.mc.otm.android.AndroidFeatureType;
|
||||||
|
import ru.dbotthepony.mc.otm.block.entity.blackhole.BlockEntityBlackHole;
|
||||||
import ru.dbotthepony.mc.otm.client.render.BlackHoleRenderer;
|
import ru.dbotthepony.mc.otm.client.render.BlackHoleRenderer;
|
||||||
import ru.dbotthepony.mc.otm.client.render.SkinElement;
|
import ru.dbotthepony.mc.otm.client.render.SkinElement;
|
||||||
import ru.dbotthepony.mc.otm.core.Fraction;
|
import ru.dbotthepony.mc.otm.core.Fraction;
|
||||||
|
@ -1,474 +0,0 @@
|
|||||||
package ru.dbotthepony.mc.otm.block.entity;
|
|
||||||
|
|
||||||
import net.minecraft.MethodsReturnNonnullByDefault;
|
|
||||||
import net.minecraft.client.Minecraft;
|
|
||||||
import net.minecraft.core.BlockPos;
|
|
||||||
import net.minecraft.nbt.CompoundTag;
|
|
||||||
import net.minecraft.nbt.ListTag;
|
|
||||||
import net.minecraft.nbt.StringTag;
|
|
||||||
import net.minecraft.nbt.Tag;
|
|
||||||
import net.minecraft.network.Connection;
|
|
||||||
import net.minecraft.network.protocol.PacketFlow;
|
|
||||||
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
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.OverdriveThatMatters;
|
|
||||||
import ru.dbotthepony.mc.otm.Registry;
|
|
||||||
import ru.dbotthepony.mc.otm.capability.MatteryCapability;
|
|
||||||
import ru.dbotthepony.mc.otm.core.Fraction;
|
|
||||||
import ru.dbotthepony.mc.otm.matter.MatterRegistry;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import javax.annotation.ParametersAreNonnullByDefault;
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
@MethodsReturnNonnullByDefault
|
|
||||||
@ParametersAreNonnullByDefault
|
|
||||||
public class BlockEntityBlackHole extends BlockEntity {
|
|
||||||
public BlockEntityBlackHole(BlockPos p_155229_, BlockState p_155230_) {
|
|
||||||
super(Registry.BlockEntities.BLACK_HOLE, p_155229_, p_155230_);
|
|
||||||
setMass(mass);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final Fraction NORMAL_MASS = new Fraction(1_000);
|
|
||||||
private Fraction mass = NORMAL_MASS;
|
|
||||||
private double gravitation_strength = 1;
|
|
||||||
private boolean suppress_updates = true;
|
|
||||||
public boolean spin_direction = false;
|
|
||||||
|
|
||||||
public double getGravitationStrength() {
|
|
||||||
return gravitation_strength;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class BlackHoleExplosionDamageCalculator extends ExplosionDamageCalculator {
|
|
||||||
@Override
|
|
||||||
public Optional<Float> getBlockExplosionResistance(Explosion explosion, BlockGetter getter, BlockPos pos, BlockState state, FluidState fstate) {
|
|
||||||
return state.isAir() && fstate.isEmpty() ?
|
|
||||||
Optional.empty() :
|
|
||||||
Optional.of(
|
|
||||||
(float) Math.sqrt(
|
|
||||||
Math.max(0,
|
|
||||||
Math.max(
|
|
||||||
state.getExplosionResistance(getter, pos, explosion),
|
|
||||||
fstate.getExplosionResistance(getter, pos, explosion)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final BlackHoleExplosionDamageCalculator INSTANCE = new BlackHoleExplosionDamageCalculator();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class BlackHoleExplosionQueue extends SavedData {
|
|
||||||
private record QueuedExplosion(double x, double y, double z, float radius) {
|
|
||||||
public CompoundTag serializeNBT() {
|
|
||||||
var tag = new CompoundTag();
|
|
||||||
|
|
||||||
tag.putDouble("x", x);
|
|
||||||
tag.putDouble("y", y);
|
|
||||||
tag.putDouble("z", z);
|
|
||||||
tag.putFloat("radius", radius);
|
|
||||||
|
|
||||||
return tag;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static QueuedExplosion deserializeNBT(CompoundTag tag) {
|
|
||||||
return new QueuedExplosion(tag.getDouble("x"), tag.getDouble("y"), tag.getDouble("z"), tag.getFloat("radius"));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void explode(Level level) {
|
|
||||||
level.explode(
|
|
||||||
null,
|
|
||||||
Registry.DAMAGE_HAWKING_RADIATION,
|
|
||||||
BlackHoleExplosionDamageCalculator.INSTANCE,
|
|
||||||
x,
|
|
||||||
y,
|
|
||||||
z,
|
|
||||||
radius,
|
|
||||||
false,
|
|
||||||
Explosion.BlockInteraction.DESTROY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private record RingExplosion(double x, double y, double z, double radius, float strength) {
|
|
||||||
public CompoundTag serializeNBT() {
|
|
||||||
var tag = new CompoundTag();
|
|
||||||
|
|
||||||
tag.putDouble("x", x);
|
|
||||||
tag.putDouble("y", y);
|
|
||||||
tag.putDouble("z", z);
|
|
||||||
tag.putDouble("radius", radius);
|
|
||||||
|
|
||||||
return tag;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static RingExplosion deserializeNBT(CompoundTag tag) {
|
|
||||||
return new RingExplosion(tag.getDouble("x"), tag.getDouble("y"), tag.getDouble("z"), tag.getDouble("radius"), tag.getFloat("strength"));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void explode(BlackHoleExplosionQueue queue) {
|
|
||||||
final int fragments = (int) radius * 8;
|
|
||||||
|
|
||||||
final double stack_step = Math.PI / (double) fragments;
|
|
||||||
final double sector_step = Math.PI / (double) fragments * 2;
|
|
||||||
|
|
||||||
for (int stack = 0; stack < fragments; stack++) {
|
|
||||||
final double stack_angle = Math.PI / 2 - stack * stack_step;
|
|
||||||
final double xy = radius * 15 * Math.cos(stack_angle);
|
|
||||||
final double z = radius * 15 * Math.sin(stack_angle);
|
|
||||||
|
|
||||||
for (int sector = 0; sector < fragments; sector++) {
|
|
||||||
final double sector_angle = sector * sector_step;
|
|
||||||
|
|
||||||
queue.explode(
|
|
||||||
this.x + xy * Math.cos(sector_angle),
|
|
||||||
this.y + xy * Math.sin(sector_angle),
|
|
||||||
this.z + z,
|
|
||||||
strength
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private int index = 0;
|
|
||||||
private int index_ring = 0;
|
|
||||||
private final ArrayList<QueuedExplosion> explosions = new ArrayList<>();
|
|
||||||
private final ArrayList<RingExplosion> rings = new ArrayList<>();
|
|
||||||
private final ServerLevel level;
|
|
||||||
|
|
||||||
public BlackHoleExplosionQueue(ServerLevel level) {
|
|
||||||
this.level = level;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CompoundTag save(CompoundTag tag) {
|
|
||||||
var list = new ListTag();
|
|
||||||
var list_rings = new ListTag();
|
|
||||||
|
|
||||||
for (int i = index; i < explosions.size(); i++)
|
|
||||||
list.add(explosions.get(i).serializeNBT());
|
|
||||||
|
|
||||||
for (int i = index_ring; i < rings.size(); i++)
|
|
||||||
list_rings.add(rings.get(i).serializeNBT());
|
|
||||||
|
|
||||||
tag.put("explosions", list);
|
|
||||||
tag.put("rings", list_rings);
|
|
||||||
|
|
||||||
return tag;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void load(CompoundTag tag) {
|
|
||||||
explosions.clear();
|
|
||||||
rings.clear();
|
|
||||||
index = 0;
|
|
||||||
index_ring = 0;
|
|
||||||
|
|
||||||
for (var explosion : tag.getList("explosions", Tag.TAG_COMPOUND))
|
|
||||||
explosions.add(QueuedExplosion.deserializeNBT((CompoundTag) explosion));
|
|
||||||
|
|
||||||
for (var ring : tag.getList("rings", Tag.TAG_COMPOUND))
|
|
||||||
rings.add(RingExplosion.deserializeNBT((CompoundTag) ring));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static BlackHoleExplosionQueue factory(ServerLevel level) {
|
|
||||||
return level.getDataStorage().computeIfAbsent(
|
|
||||||
(tag) -> {
|
|
||||||
var factory = new BlackHoleExplosionQueue(level);
|
|
||||||
factory.load(tag);
|
|
||||||
return factory;
|
|
||||||
},
|
|
||||||
|
|
||||||
() -> new BlackHoleExplosionQueue(level),
|
|
||||||
|
|
||||||
"otm_blackhole_explosion_queue"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void explode(double x, double y, double z, float radius) {
|
|
||||||
explosions.add(new QueuedExplosion(x, y, z, radius));
|
|
||||||
setDirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void explodeRing(double x, double y, double z, double radius, float strength) {
|
|
||||||
rings.add(new RingExplosion(x, y, z, radius, strength));
|
|
||||||
setDirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void tick() {
|
|
||||||
if (explosions.size() != 0) {
|
|
||||||
setDirty();
|
|
||||||
|
|
||||||
int iterations = 0;
|
|
||||||
|
|
||||||
for (int i = index; i < explosions.size(); i++) {
|
|
||||||
explosions.get(i).explode(level);
|
|
||||||
index++;
|
|
||||||
|
|
||||||
if (iterations++ == 4) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index >= explosions.size()) {
|
|
||||||
index = 0;
|
|
||||||
explosions.clear();
|
|
||||||
}
|
|
||||||
} else if (rings.size() != 0) {
|
|
||||||
if (index_ring >= rings.size()) {
|
|
||||||
index_ring = 0;
|
|
||||||
rings.clear();
|
|
||||||
} else {
|
|
||||||
rings.get(index_ring++).explode(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SubscribeEvent
|
|
||||||
public static void onWorldTick(TickEvent.WorldTickEvent event) {
|
|
||||||
if (event.phase == TickEvent.Phase.START && event.world instanceof ServerLevel level) {
|
|
||||||
factory(level).tick();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void collapse() {
|
|
||||||
level.setBlock(getBlockPos(), Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL);
|
|
||||||
|
|
||||||
if (gravitation_strength > 0.25) {
|
|
||||||
final double x0 = getBlockPos().getX() + 0.5;
|
|
||||||
final double y0 = getBlockPos().getY() + 0.5;
|
|
||||||
final double z0 = getBlockPos().getZ() + 0.5;
|
|
||||||
|
|
||||||
var queue = BlackHoleExplosionQueue.factory((ServerLevel) level);
|
|
||||||
|
|
||||||
for (int radius = 0; radius < Math.ceil(gravitation_strength * 4); radius++) {
|
|
||||||
queue.explodeRing(
|
|
||||||
x0,
|
|
||||||
y0,
|
|
||||||
z0,
|
|
||||||
radius,
|
|
||||||
(float) Math.min(20, Math.max(1, (gravitation_strength * 4 - radius) * 20)));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
level.explode(
|
|
||||||
null,
|
|
||||||
Registry.DAMAGE_HAWKING_RADIATION,
|
|
||||||
null,
|
|
||||||
getBlockPos().getX() + 0.5D,
|
|
||||||
getBlockPos().getY() + 0.5D,
|
|
||||||
getBlockPos().getZ() + 0.5D,
|
|
||||||
(float) gravitation_strength * 60,
|
|
||||||
false,
|
|
||||||
Explosion.BlockInteraction.DESTROY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addMass(Fraction mass) {
|
|
||||||
setMass(this.mass.plus(mass));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMass(Fraction mass) {
|
|
||||||
if (mass.compareTo(Fraction.ZERO) <= 0) {
|
|
||||||
collapse();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.mass = mass;
|
|
||||||
setChanged();
|
|
||||||
gravitation_strength = mass.div(NORMAL_MASS).toDouble();
|
|
||||||
|
|
||||||
if (gravitation_strength > 1) {
|
|
||||||
gravitation_strength = Math.min(20, Math.sqrt(gravitation_strength));
|
|
||||||
} else {
|
|
||||||
gravitation_strength = Math.max(0.08, Math.pow(gravitation_strength, 2));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (level != null && !level.isClientSide && !suppress_updates)
|
|
||||||
level.sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(), Block.UPDATE_CLIENTS);
|
|
||||||
|
|
||||||
affected_bounds = new BoundingBox(
|
|
||||||
(int) (-30 * gravitation_strength),
|
|
||||||
(int) (-30 * gravitation_strength),
|
|
||||||
(int) (-30 * gravitation_strength),
|
|
||||||
(int) (30 * gravitation_strength),
|
|
||||||
(int) (30 * gravitation_strength),
|
|
||||||
(int) (30 * gravitation_strength)
|
|
||||||
).move(getBlockPos());
|
|
||||||
|
|
||||||
affected_bounds_aabb = AABB.of(affected_bounds);
|
|
||||||
}
|
|
||||||
|
|
||||||
// shared functions
|
|
||||||
public CompoundTag writeBlackHoleData(CompoundTag tag) {
|
|
||||||
tag.putString("mass", mass.toString());
|
|
||||||
tag.putBoolean("spin_direction", spin_direction);
|
|
||||||
return tag;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void readBlackHoleData(CompoundTag tag) {
|
|
||||||
if (tag.contains("mass"))
|
|
||||||
setMass(Fraction.deserializeNBT(tag.get("mass")));
|
|
||||||
|
|
||||||
spin_direction = tag.getBoolean("spin_direction");
|
|
||||||
}
|
|
||||||
|
|
||||||
// disk io
|
|
||||||
@Override
|
|
||||||
public void saveAdditional(CompoundTag nbt) {
|
|
||||||
super.saveAdditional(nbt);
|
|
||||||
writeBlackHoleData(nbt);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void load(CompoundTag tag) {
|
|
||||||
super.load(tag);
|
|
||||||
readBlackHoleData(tag);
|
|
||||||
}
|
|
||||||
|
|
||||||
// received either by game engine (from getUpdateTag on ClientLevelPacket)
|
|
||||||
// or by onDataPacket
|
|
||||||
@Override
|
|
||||||
public void handleUpdateTag(CompoundTag tag) {
|
|
||||||
super.handleUpdateTag(tag);
|
|
||||||
readBlackHoleData(tag);
|
|
||||||
}
|
|
||||||
|
|
||||||
// called by game engine for ClientLevelPacket
|
|
||||||
@Override
|
|
||||||
public CompoundTag getUpdateTag() {
|
|
||||||
return writeBlackHoleData(super.getUpdateTag());
|
|
||||||
}
|
|
||||||
|
|
||||||
// called by game engine on block updates
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public ClientboundBlockEntityDataPacket getUpdatePacket() {
|
|
||||||
return ClientboundBlockEntityDataPacket.create(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// called by game engine by forge patches when ClientboundBlockEntityDataPacket is received
|
|
||||||
@Override
|
|
||||||
public void onDataPacket(Connection net, ClientboundBlockEntityDataPacket pkt) {
|
|
||||||
super.onDataPacket(net, pkt);
|
|
||||||
|
|
||||||
if (net.getReceiving() == PacketFlow.CLIENTBOUND) {
|
|
||||||
handleUpdateTag(pkt.getTag());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private BoundingBox affected_bounds;
|
|
||||||
private AABB affected_bounds_aabb;
|
|
||||||
|
|
||||||
public BoundingBox affectedBounds() {
|
|
||||||
return affected_bounds;
|
|
||||||
}
|
|
||||||
|
|
||||||
public AABB affectedBoundsAABB() {
|
|
||||||
return affected_bounds_aabb;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setDeltaMovement(Entity living, Vec3 center, double distance, boolean weaker) {
|
|
||||||
//final double mult = Math.min(2, (30 * this.gravitation_strength) / Math.max(1, Math.pow(distance, 2)));
|
|
||||||
// Сила притяжения
|
|
||||||
final double mult = Math.pow(1 - distance / (30 * this.gravitation_strength), 2) * this.gravitation_strength / 8;
|
|
||||||
|
|
||||||
// Притяжение к ядру
|
|
||||||
final var delta = living.position().vectorTo(center).normalize();
|
|
||||||
|
|
||||||
if (distance < this.gravitation_strength) {
|
|
||||||
living.setDeltaMovement(living.getDeltaMovement().add(delta.multiply(mult, mult, mult)));
|
|
||||||
} else {
|
|
||||||
// Закручивание
|
|
||||||
final var rotate = spin_direction ? delta.yRot((float) (Math.PI / 2)) : delta.yRot((float) (-Math.PI / 2));
|
|
||||||
|
|
||||||
if (weaker)
|
|
||||||
living.setDeltaMovement(living.getDeltaMovement().add(rotate.multiply(mult * 0.4f, mult * 0.4f, mult * 0.4f)).add(delta.multiply(mult * 0.33f, mult * 0.33f, mult * 0.33f)));
|
|
||||||
else
|
|
||||||
living.setDeltaMovement(living.getDeltaMovement().add(rotate.multiply(mult * 0.4f, mult * 0.4f, mult * 0.4f)).add(delta.multiply(mult, mult, mult)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T extends BlockEntity> void clientTicker(Level level, BlockPos blockPos, BlockState blockState, T t) {
|
|
||||||
if (t instanceof BlockEntityBlackHole tile) {
|
|
||||||
var ply = Minecraft.getInstance().player;
|
|
||||||
final var center = Vec3.atCenterOf(tile.getBlockPos());
|
|
||||||
|
|
||||||
if (!ply.getAbilities().mayfly) {
|
|
||||||
final double distance = ply.position().distanceTo(center);
|
|
||||||
tile.setDeltaMovement(ply, center, distance, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var item : tile.level.getEntitiesOfClass(ItemEntity.class, tile.affected_bounds_aabb)) {
|
|
||||||
final double distance = item.position().distanceTo(center);
|
|
||||||
tile.setDeltaMovement(item, center, distance, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final Fraction HAWKING_MASS_LOSE_STEP = new Fraction("-0.1");
|
|
||||||
|
|
||||||
public static <T extends BlockEntity> void ticker(Level level, BlockPos blockPos, BlockState blockState, T t) {
|
|
||||||
if (t instanceof BlockEntityBlackHole tile) {
|
|
||||||
tile.suppress_updates = false;
|
|
||||||
|
|
||||||
final var center = Vec3.atCenterOf(tile.getBlockPos());
|
|
||||||
|
|
||||||
for (var living : level.getEntitiesOfClass(LivingEntity.class, tile.affected_bounds_aabb)) {
|
|
||||||
final double distance = living.position().distanceTo(center);
|
|
||||||
|
|
||||||
if (!(living instanceof Player ply) || !ply.getAbilities().mayfly) {
|
|
||||||
tile.setDeltaMovement(living, center, distance, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (distance < tile.gravitation_strength + 1) {
|
|
||||||
living.hurt(Registry.DAMAGE_EVENT_HORIZON, (float) (tile.gravitation_strength / distance));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var item : level.getEntitiesOfClass(ItemEntity.class, tile.affected_bounds_aabb)) {
|
|
||||||
final double distance = item.position().distanceTo(center);
|
|
||||||
tile.setDeltaMovement(item, center, distance, false);
|
|
||||||
|
|
||||||
if (distance < tile.gravitation_strength + 1) {
|
|
||||||
if (item.hurt(Registry.DAMAGE_EVENT_HORIZON, (float) (tile.gravitation_strength / distance)) && item.isRemoved()) {
|
|
||||||
if (item.getItem().getItem() == Registry.Items.GRAVITATIONAL_DISRUPTOR) {
|
|
||||||
tile.collapse();
|
|
||||||
} else {
|
|
||||||
var mass = MatterRegistry.getMatterValue(item.getItem());
|
|
||||||
|
|
||||||
if (mass.compareTo(Fraction.ZERO) > 0)
|
|
||||||
tile.addMass(mass);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// шанс 1% что черная дыра потеряет 0.1 MtU каждую секунду * силу гравитации дыры ^ -1
|
|
||||||
if (level.random.nextDouble() < 0.01 * 0.05 * (1 / tile.gravitation_strength)) {
|
|
||||||
tile.addMass(HAWKING_MASS_LOSE_STEP);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -7,7 +7,7 @@ import net.minecraft.client.renderer.MultiBufferSource;
|
|||||||
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
|
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
|
||||||
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
|
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
|
||||||
import net.minecraft.world.phys.Vec3;
|
import net.minecraft.world.phys.Vec3;
|
||||||
import ru.dbotthepony.mc.otm.block.entity.BlockEntityBlackHole;
|
import ru.dbotthepony.mc.otm.block.entity.blackhole.BlockEntityBlackHole;
|
||||||
|
|
||||||
import static org.lwjgl.opengl.GL33.*;
|
import static org.lwjgl.opengl.GL33.*;
|
||||||
|
|
||||||
|
@ -27,6 +27,8 @@ operator fun CompoundTag.set(s: String, value: Int) = putInt(s, value)
|
|||||||
operator fun CompoundTag.set(s: String, value: Byte) = putByte(s, value)
|
operator fun CompoundTag.set(s: String, value: Byte) = putByte(s, value)
|
||||||
operator fun CompoundTag.set(s: String, value: Short) = putShort(s, value)
|
operator fun CompoundTag.set(s: String, value: Short) = putShort(s, value)
|
||||||
operator fun CompoundTag.set(s: String, value: Long) = putLong(s, value)
|
operator fun CompoundTag.set(s: String, value: Long) = putLong(s, value)
|
||||||
|
operator fun CompoundTag.set(s: String, value: Float) = putFloat(s, value)
|
||||||
|
operator fun CompoundTag.set(s: String, value: Double) = putDouble(s, value)
|
||||||
operator fun CompoundTag.set(s: String, value: String) = putString(s, value)
|
operator fun CompoundTag.set(s: String, value: String) = putString(s, value)
|
||||||
operator fun CompoundTag.set(s: String, value: Boolean) = putBoolean(s, value)
|
operator fun CompoundTag.set(s: String, value: Boolean) = putBoolean(s, value)
|
||||||
operator fun CompoundTag.set(s: String, value: ByteArray) = putByteArray(s, value)
|
operator fun CompoundTag.set(s: String, value: ByteArray) = putByteArray(s, value)
|
||||||
|
@ -14,7 +14,7 @@ import net.minecraft.world.level.material.MaterialColor
|
|||||||
import net.minecraft.world.phys.shapes.CollisionContext
|
import net.minecraft.world.phys.shapes.CollisionContext
|
||||||
import net.minecraft.world.phys.shapes.VoxelShape
|
import net.minecraft.world.phys.shapes.VoxelShape
|
||||||
import ru.dbotthepony.mc.otm.Registry
|
import ru.dbotthepony.mc.otm.Registry
|
||||||
import ru.dbotthepony.mc.otm.block.entity.BlockEntityBlackHole
|
import ru.dbotthepony.mc.otm.block.entity.blackhole.BlockEntityBlackHole
|
||||||
import ru.dbotthepony.mc.otm.shapes.BlockShapes
|
import ru.dbotthepony.mc.otm.shapes.BlockShapes
|
||||||
|
|
||||||
class BlockBlackHole :
|
class BlockBlackHole :
|
||||||
@ -29,7 +29,10 @@ class BlockBlackHole :
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState): BlockEntity {
|
override fun newBlockEntity(blockPos: BlockPos, blockState: BlockState): BlockEntity {
|
||||||
return BlockEntityBlackHole(blockPos, blockState)
|
return BlockEntityBlackHole(
|
||||||
|
blockPos,
|
||||||
|
blockState
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun <T : BlockEntity?> getTicker(
|
override fun <T : BlockEntity?> getTicker(
|
||||||
@ -39,21 +42,11 @@ class BlockBlackHole :
|
|||||||
): BlockEntityTicker<T>? {
|
): BlockEntityTicker<T>? {
|
||||||
if (p_153214_ !== Registry.BlockEntities.BLACK_HOLE) return null
|
if (p_153214_ !== Registry.BlockEntities.BLACK_HOLE) return null
|
||||||
|
|
||||||
return if (p_153212_.isClientSide) BlockEntityTicker { level: Level, blockPos: BlockPos, blockState: BlockState, t: T ->
|
if (p_153212_.isClientSide) {
|
||||||
BlockEntityBlackHole.clientTicker(
|
return BlockEntityTicker { _, _, _, t -> if (t is BlockEntityBlackHole) t.clientTick() }
|
||||||
level,
|
|
||||||
blockPos,
|
|
||||||
blockState,
|
|
||||||
t
|
|
||||||
)
|
|
||||||
} else BlockEntityTicker { level: Level, blockPos: BlockPos, blockState: BlockState, t: T ->
|
|
||||||
BlockEntityBlackHole.ticker(
|
|
||||||
level,
|
|
||||||
blockPos,
|
|
||||||
blockState,
|
|
||||||
t
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return BlockEntityTicker { _, _, _, t -> if (t is BlockEntityBlackHole) t.tick() }
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -0,0 +1,469 @@
|
|||||||
|
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
|
||||||
|
import net.minecraft.server.level.ServerLevel
|
||||||
|
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.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) {
|
||||||
|
if (mass <= Fraction.ZERO) {
|
||||||
|
collapse()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
field = mass
|
||||||
|
|
||||||
|
setChanged()
|
||||||
|
gravitationStrength = mass.div(BASELINE_MASS).toDouble()
|
||||||
|
|
||||||
|
if (gravitationStrength > 1) {
|
||||||
|
gravitationStrength = 20.0.coerceAtMost(sqrt(gravitationStrength))
|
||||||
|
} else {
|
||||||
|
gravitationStrength = 0.08.coerceAtLeast(gravitationStrength.pow(2.0))
|
||||||
|
}
|
||||||
|
|
||||||
|
val level = level
|
||||||
|
|
||||||
|
if (level != null && !level.isClientSide && !suppress_updates)
|
||||||
|
level.sendBlockUpdated(blockPos, blockState, blockState, Block.UPDATE_CLIENTS)
|
||||||
|
|
||||||
|
affectedBounds = BoundingBox(
|
||||||
|
(-30 * gravitationStrength).toInt(),
|
||||||
|
(-30 * gravitationStrength).toInt(),
|
||||||
|
(-30 * gravitationStrength).toInt(),
|
||||||
|
(30 * gravitationStrength).toInt(),
|
||||||
|
(30 * gravitationStrength).toInt(),
|
||||||
|
(30 * gravitationStrength).toInt()
|
||||||
|
).move(
|
||||||
|
blockPos
|
||||||
|
)
|
||||||
|
|
||||||
|
affectedBoundsAABB = AABB.of(affectedBounds)
|
||||||
|
}
|
||||||
|
|
||||||
|
var gravitationStrength = 1.0
|
||||||
|
private set
|
||||||
|
|
||||||
|
private var suppress_updates = true
|
||||||
|
var spin_direction = false
|
||||||
|
|
||||||
|
fun collapse() {
|
||||||
|
val level = level as? ServerLevel ?: return
|
||||||
|
|
||||||
|
level.setBlock(blockPos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL)
|
||||||
|
|
||||||
|
if (gravitationStrength > 0.25) {
|
||||||
|
val x0 = blockPos.x + 0.5
|
||||||
|
val y0 = blockPos.y + 0.5
|
||||||
|
val z0 = blockPos.z + 0.5
|
||||||
|
val queue = factory(level)
|
||||||
|
var radius = 0
|
||||||
|
|
||||||
|
while (radius < Math.ceil(gravitationStrength * 4)) {
|
||||||
|
queue.explodeRing(
|
||||||
|
x0,
|
||||||
|
y0,
|
||||||
|
z0,
|
||||||
|
radius.toDouble(), Math.min(20.0, Math.max(1.0, (gravitationStrength * 4 - radius) * 20)).toFloat()
|
||||||
|
)
|
||||||
|
|
||||||
|
radius++
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
level.explode(
|
||||||
|
null,
|
||||||
|
Registry.DAMAGE_HAWKING_RADIATION,
|
||||||
|
null,
|
||||||
|
blockPos.x + 0.5,
|
||||||
|
blockPos.y + 0.5,
|
||||||
|
blockPos.z + 0.5,
|
||||||
|
gravitationStrength.toFloat() * 60,
|
||||||
|
false,
|
||||||
|
Explosion.BlockInteraction.DESTROY
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// shared functions
|
||||||
|
fun writeBlackHoleData(tag: CompoundTag): CompoundTag {
|
||||||
|
tag["mass"] = mass.serializeNBT()
|
||||||
|
tag["spin_direction"] = spin_direction
|
||||||
|
return tag
|
||||||
|
}
|
||||||
|
|
||||||
|
fun readBlackHoleData(tag: CompoundTag) {
|
||||||
|
tag["mass"]?.let {
|
||||||
|
mass = Fraction.deserializeNBT(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_direction = tag.getBoolean("spin_direction")
|
||||||
|
}
|
||||||
|
|
||||||
|
// disk io
|
||||||
|
public override fun saveAdditional(nbt: CompoundTag) {
|
||||||
|
super.saveAdditional(nbt)
|
||||||
|
writeBlackHoleData(nbt)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun load(tag: CompoundTag) {
|
||||||
|
super.load(tag)
|
||||||
|
readBlackHoleData(tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
// received either by game engine (from getUpdateTag on ClientLevelPacket)
|
||||||
|
// or by onDataPacket
|
||||||
|
override fun handleUpdateTag(tag: CompoundTag) {
|
||||||
|
super.handleUpdateTag(tag)
|
||||||
|
readBlackHoleData(tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
// called by game engine for ClientLevelPacket
|
||||||
|
override fun getUpdateTag(): CompoundTag {
|
||||||
|
return writeBlackHoleData(super.getUpdateTag())
|
||||||
|
}
|
||||||
|
|
||||||
|
// called by game engine on block updates
|
||||||
|
override fun getUpdatePacket(): ClientboundBlockEntityDataPacket? {
|
||||||
|
return ClientboundBlockEntityDataPacket.create(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
// called by game engine by forge patches when ClientboundBlockEntityDataPacket is received
|
||||||
|
override fun onDataPacket(net: Connection, pkt: ClientboundBlockEntityDataPacket) {
|
||||||
|
super.onDataPacket(net, pkt)
|
||||||
|
|
||||||
|
if (net.receiving == PacketFlow.CLIENTBOUND) {
|
||||||
|
handleUpdateTag(pkt.tag!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var affectedBounds = BoundingBox(0, 0, 0, 1, 1, 1)
|
||||||
|
private set
|
||||||
|
var affectedBoundsAABB = AABB.of(affectedBounds)
|
||||||
|
private set
|
||||||
|
|
||||||
|
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), 2.0) * gravitationStrength / 8
|
||||||
|
|
||||||
|
// Притяжение к ядру
|
||||||
|
val delta = living.position().vectorTo(center).normalize()
|
||||||
|
|
||||||
|
if (distance < gravitationStrength) {
|
||||||
|
living.deltaMovement = living.deltaMovement.add(delta.multiply(mult, mult, mult))
|
||||||
|
} else {
|
||||||
|
// Закручивание
|
||||||
|
val rotate = if (spin_direction) delta.yRot((Math.PI / 2).toFloat()) else delta.yRot((-Math.PI / 2).toFloat())
|
||||||
|
|
||||||
|
if (weaker)
|
||||||
|
living.deltaMovement = living.deltaMovement
|
||||||
|
.add(rotate.multiply(mult * 0.4f, mult * 0.4f, mult * 0.4f))
|
||||||
|
.add(delta.multiply(mult * 0.33f, mult * 0.33f, mult * 0.33f))
|
||||||
|
else
|
||||||
|
living.deltaMovement = living.deltaMovement
|
||||||
|
.add(rotate.multiply(mult * 0.4f, mult * 0.4f, mult * 0.4f))
|
||||||
|
.add(delta.multiply(mult, mult, mult))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clientTick() {
|
||||||
|
val ply = Minecraft.getInstance().player!!
|
||||||
|
val center = Vec3.atCenterOf(blockPos)
|
||||||
|
|
||||||
|
if (!ply.abilities.mayfly) {
|
||||||
|
val distance = ply.position().distanceTo(center)
|
||||||
|
setDeltaMovement(ply, center, distance, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (item in level!!.getEntitiesOfClass(ItemEntity::class.java, affectedBoundsAABB)) {
|
||||||
|
val distance = item.position().distanceTo(center)
|
||||||
|
setDeltaMovement(item, center, distance, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun tick() {
|
||||||
|
val level = level as ServerLevel
|
||||||
|
suppress_updates = false
|
||||||
|
|
||||||
|
val center = Vec3.atCenterOf(blockPos)
|
||||||
|
|
||||||
|
for (living in level.getEntitiesOfClass(LivingEntity::class.java, affectedBoundsAABB)) {
|
||||||
|
val distance = living.position().distanceTo(center)
|
||||||
|
|
||||||
|
if (living !is Player || !living.abilities.mayfly) {
|
||||||
|
setDeltaMovement(living, center, distance, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (distance < gravitationStrength + 1) {
|
||||||
|
living.hurt(Registry.DAMAGE_EVENT_HORIZON, (gravitationStrength / distance).toFloat())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (item in level.getEntitiesOfClass(ItemEntity::class.java, affectedBoundsAABB)) {
|
||||||
|
val distance = item.position().distanceTo(center)
|
||||||
|
setDeltaMovement(item, center, distance, false)
|
||||||
|
|
||||||
|
if (distance < gravitationStrength + 1) {
|
||||||
|
if (item.hurt(Registry.DAMAGE_EVENT_HORIZON, (gravitationStrength / distance).toFloat()) && item.isRemoved) {
|
||||||
|
if (item.item.item === Registry.Items.GRAVITATIONAL_DISRUPTOR) {
|
||||||
|
collapse()
|
||||||
|
} else {
|
||||||
|
val mass = MatterRegistry.getMatterValue(item.item)
|
||||||
|
|
||||||
|
if (mass > Fraction.ZERO)
|
||||||
|
this.mass += mass
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// шанс 1% что черная дыра потеряет 0.1 MtU каждую секунду * силу гравитации дыры ^ -1
|
||||||
|
if (level.random.nextDouble() < 0.01 * 0.05 * (1 / gravitationStrength)) {
|
||||||
|
this.mass += HAWKING_MASS_LOSE_STEP
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
mass = mass
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val BASELINE_MASS = Fraction(1_000)
|
||||||
|
val HAWKING_MASS_LOSE_STEP = Fraction("-0.1")
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user