From 8fe2d8daa891ce27ecaddad17c6024e30a75d06d Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Mon, 30 Aug 2021 10:22:58 +0700 Subject: [PATCH] Black holes --- .gitignore | 13 ++ shapenator.js | 138 ++++++++++++ .../java/ru/dbotthepony/mc/otm/Registry.java | 19 +- .../mc/otm/block/BlockBlackHole.java | 46 +++- .../block/entity/BlockEntityBlackHole.java | 205 ++++++++++++++++++ .../overdrive_that_matters/lang/en_us.json | 9 + .../textures/block/black_hole.png | Bin 0 -> 141 bytes 7 files changed, 427 insertions(+), 3 deletions(-) create mode 100644 src/main/java/ru/dbotthepony/mc/otm/block/entity/BlockEntityBlackHole.java create mode 100644 src/main/resources/assets/overdrive_that_matters/textures/block/black_hole.png diff --git a/.gitignore b/.gitignore index c8ff0db40..e41c9c0bc 100644 --- a/.gitignore +++ b/.gitignore @@ -100,3 +100,16 @@ forge*changelog.txt /src/main/resources/assets/overdrive_that_matters/models/block/crate_red.json /src/main/resources/assets/overdrive_that_matters/models/block/crate_yellow.json /src/main/resources/assets/overdrive_that_matters/models/block/tritanium_block.json +/src/main/resources/assets/overdrive_that_matters/blockstates/black_hole.json +/src/main/resources/assets/overdrive_that_matters/models/block/black_hole.json +/src/main/resources/assets/overdrive_that_matters/models/item/black_hole.json +/src/main/resources/assets/overdrive_that_matters/models/item/carbon_fibre_block.json +/src/main/resources/assets/overdrive_that_matters/models/item/crate_black.json +/src/main/resources/assets/overdrive_that_matters/models/item/crate_blue.json +/src/main/resources/assets/overdrive_that_matters/models/item/crate_green.json +/src/main/resources/assets/overdrive_that_matters/models/item/crate_pink.json +/src/main/resources/assets/overdrive_that_matters/models/item/crate_purple.json +/src/main/resources/assets/overdrive_that_matters/models/item/crate_red.json +/src/main/resources/assets/overdrive_that_matters/models/item/crate_yellow.json +/src/main/resources/assets/overdrive_that_matters/models/item/tritanium_block.json +/src/main/resources/assets/overdrive_that_matters/models/item/tritanium_striped_block.json diff --git a/shapenator.js b/shapenator.js index bd6645efa..a2d3b0fff 100644 --- a/shapenator.js +++ b/shapenator.js @@ -2,6 +2,7 @@ const models = [ 'android_station', 'battery_bank', + 'black_hole', ['matter_scanner', 'matter_scanner_working'], 'pattern_storage', ['matter_replicator', 'matter_replicator_working'], @@ -16,12 +17,143 @@ const root = _root + 'models/block/' const time = Date.now() process.stdout.write('Regenerating data files\n') + +// "модель" горизонта событий / черной дыры +{ + const elements = [] + + // сфера + const layers = 32 + const radius = 16 + const checked = new Array(Math.pow(layers, 3)) + + // создание + слияние вдоль Y + for (let y = -layers / 2; y < layers / 2; y++) { + for (let x = -layers / 2; x < layers / 2; x++) { + for (let z = -layers / 2; z < layers / 2; z++) { + const dist1 = Math.sqrt(x * x + y * y + z * z) + + if (dist1 <= radius) { + const index1 = x + layers / 2 + (z + layers / 2) * layers + (y + layers / 2) * layers * layers + + if (!checked[index1]) { + let height = 0 + let index + + for (let y2 = 0; y2 < layers; y2++) { + const dist = Math.sqrt(x * x + (y + y2) * (y + y2) + z * z) + const index2 = x + layers / 2 + (z + layers / 2) * layers + (y + y2 + layers / 2) * layers * layers + + if (dist <= radius && !checked[index2]) { + height = y2 + index = index2 + } else { + break + } + } + + if (height > 0) { + checked[index] = { + "from": [4 + (x + layers / 2) / layers * 8, 4 + (y + layers / 2) / layers * 8, 4 + (z + layers / 2) / layers * 8], + "to": [4 + (x + layers / 2 + 1) / layers * 8, 4 + (y + layers / 2 + height) / layers * 8, 4 + (z + layers / 2 + 1) / layers * 8], + "faces": { + "down": { "texture": "#black_hole" }, + "up": { "texture": "#black_hole" }, + "north": { "texture": "#black_hole" }, + "south": { "texture": "#black_hole" }, + "west": { "texture": "#black_hole" }, + "east": { "texture": "#black_hole" } + } + } + + elements.push(checked[index]) + } + } + } + } + } + } + + // слияние вдоль X + for (let y = -layers / 2; y < layers / 2; y++) { + for (let z = -layers / 2; z < layers / 2; z++) { + let last_element = null + + for (let x = -layers / 2; x < layers / 2; x++) { + const index = x + layers / 2 + (z + layers / 2) * layers + (y + layers / 2) * layers * layers + + if (checked[index]) { + if (last_element) { + const from1 = checked[index].from + const from2 = last_element.from + + if (from1[1] == from2[1] && from1[2] == from2[2]) { + last_element.to[0] = checked[index].to[0] + checked[index] = last_element + } else { + last_element = checked[index] + } + } else { + last_element = checked[index] + } + } + } + } + } + + const merged_z = [] + + // слияние вдоль Z + for (let y = -layers / 2; y < layers / 2; y++) { + for (let x = -layers / 2; x < layers / 2; x++) { + let last_element = null + + for (let z = -layers / 2; z < layers / 2; z++) { + const index = x + layers / 2 + (z + layers / 2) * layers + (y + layers / 2) * layers * layers + + if (checked[index]) { + if (last_element) { + const from1 = checked[index].from + const from2 = last_element.from + + if (from1[0] == from2[0] && from1[1] == from2[1]) { + last_element.to[2] = checked[index].to[2] + checked[index] = last_element + } else { + if (!merged_z.includes(last_element)) { + merged_z.push(last_element) + } + + last_element = checked[index] + } + } else { + last_element = checked[index] + } + } + } + + if (last_element && !merged_z.includes(last_element)) { + merged_z.push(last_element) + } + } + } + + fs.writeFileSync(root + 'black_hole.json', JSON.stringify({ + "parent": "block/block", + "textures": { + "black_hole": "overdrive_that_matters:block/black_hole" + }, + "elements": merged_z + }, null, '\t')) +} + const handle = fs.openSync('./src/main/java/ru/dbotthepony/mc/otm/shapes/BlockShapes.java', 'w') fs.writeSync(handle, 'package ru.dbotthepony.mc.otm.shapes;\n\n\n') fs.writeSync(handle, `// This file is regenerated on each gradle run. Do not edit it!\n`) fs.writeSync(handle, 'public class BlockShapes {\n') +// модели блоков for (const _model of models) { let model, path @@ -201,6 +333,8 @@ const facings = [ 'crate_black', 'crate_pink', 'crate_purple', + + 'black_hole', ] const blocks2 = [ @@ -224,6 +358,10 @@ const facings = [ } } }, null, '\t')) + + fs.writeFileSync(_root + 'models/item/' + name + '.json', JSON.stringify({ + "parent": "overdrive_that_matters:block/" + name + }, null, '\t')) } for (const name of blocks2) { diff --git a/src/main/java/ru/dbotthepony/mc/otm/Registry.java b/src/main/java/ru/dbotthepony/mc/otm/Registry.java index 9b54e1bbb..36e4516de 100644 --- a/src/main/java/ru/dbotthepony/mc/otm/Registry.java +++ b/src/main/java/ru/dbotthepony/mc/otm/Registry.java @@ -40,12 +40,14 @@ import ru.dbotthepony.mc.otm.screen.*; import java.math.BigDecimal; public class Registry { - public static final DamageSource DAMAGE_BECOME_ANDROID = new DamageSource("otmBecomeAndroid"); - public static final DamageSource DAMAGE_BECOME_HUMANE = new DamageSource("otmBecomeHumane"); + public static final DamageSource DAMAGE_BECOME_ANDROID = new DamageSource("otm_become_android"); + public static final DamageSource DAMAGE_BECOME_HUMANE = new DamageSource("otm_become_humane"); + public static final DamageSource DAMAGE_EVENT_HORIZON = new DamageSource("otm_event_horizon"); static { DAMAGE_BECOME_ANDROID.bypassArmor().bypassInvul().bypassMagic(); DAMAGE_BECOME_HUMANE.bypassArmor().bypassInvul().bypassMagic(); + DAMAGE_EVENT_HORIZON.bypassMagic().bypassArmor(); } private static ForgeRegistry> ANDROID_FEATURES; @@ -109,6 +111,8 @@ public class Registry { public static final ResourceLocation MATTER_BOTTLER = new ResourceLocation(OverdriveThatMatters.MOD_ID, "matter_bottler"); public static final ResourceLocation DRIVE_VIEWER = new ResourceLocation(OverdriveThatMatters.MOD_ID, "drive_viewer"); + public static final ResourceLocation BLACK_HOLE = new ResourceLocation(OverdriveThatMatters.MOD_ID, "black_hole"); + // building blocks public static final ResourceLocation TRITANIUM_BLOCK = new ResourceLocation(OverdriveThatMatters.MOD_ID, "tritanium_block"); public static final ResourceLocation TRITANIUM_STRIPED_BLOCK = new ResourceLocation(OverdriveThatMatters.MOD_ID, "tritanium_striped_block"); @@ -204,6 +208,8 @@ public class Registry { public static final Block MATTER_BOTTLER = new BlockMatterBottler(); public static final Block DRIVE_VIEWER = new BlockDriveViewer(); + public static final Block BLACK_HOLE = new BlockBlackHole(); + public static final Block[] CRATES = new Block[Registry.CRATES.length]; public static final Block TRITANIUM_BLOCK = new Block( @@ -251,6 +257,7 @@ public class Registry { MATTER_REPLICATOR.setRegistryName(Names.MATTER_REPLICATOR); MATTER_BOTTLER.setRegistryName(Names.MATTER_BOTTLER); DRIVE_VIEWER.setRegistryName(Names.DRIVE_VIEWER); + BLACK_HOLE.setRegistryName(Names.BLACK_HOLE); TRITANIUM_BLOCK.setRegistryName(Names.TRITANIUM_BLOCK); TRITANIUM_STRIPED_BLOCK.setRegistryName(Names.TRITANIUM_STRIPED_BLOCK); @@ -273,6 +280,7 @@ public class Registry { event.getRegistry().register(TRITANIUM_BLOCK); event.getRegistry().register(TRITANIUM_STRIPED_BLOCK); event.getRegistry().register(CARBON_FIBRE_BLOCK); + event.getRegistry().register(BLACK_HOLE); for (var crate : CRATES) { event.getRegistry().register(crate); @@ -295,6 +303,8 @@ public class Registry { public static final Item MATTER_BOTTLER = new BlockItem(Blocks.MATTER_BOTTLER, new Item.Properties().stacksTo(64).tab(OverdriveThatMatters.CREATIVE_TAB)); public static final Item DRIVE_VIEWER = new BlockItem(Blocks.DRIVE_VIEWER, new Item.Properties().stacksTo(64).tab(OverdriveThatMatters.CREATIVE_TAB)); + public static final Item BLACK_HOLE = new BlockItem(Blocks.BLACK_HOLE, new Item.Properties().stacksTo(64).tab(OverdriveThatMatters.CREATIVE_TAB)); + public static final ItemPill PILL_ANDROID = new ItemPill(ItemPill.PillType.BECOME_ANDROID); public static final ItemPill PILL_HUMANE = new ItemPill(ItemPill.PillType.BECOME_HUMANE); @@ -339,6 +349,7 @@ public class Registry { MATTER_REPLICATOR.setRegistryName(Names.MATTER_REPLICATOR); MATTER_BOTTLER.setRegistryName(Names.MATTER_BOTTLER); DRIVE_VIEWER.setRegistryName(Names.DRIVE_VIEWER); + BLACK_HOLE.setRegistryName(Names.BLACK_HOLE); PILL_ANDROID.setRegistryName(Names.PILL_ANDROID); PILL_HUMANE.setRegistryName(Names.PILL_HUMANE); @@ -377,6 +388,7 @@ public class Registry { event.getRegistry().register(MATTER_REPLICATOR); event.getRegistry().register(MATTER_BOTTLER); event.getRegistry().register(DRIVE_VIEWER); + event.getRegistry().register(BLACK_HOLE); event.getRegistry().register(PILL_ANDROID); event.getRegistry().register(PILL_HUMANE); @@ -421,6 +433,7 @@ public class Registry { public static final BlockEntityType MATTER_REPLICATOR = BlockEntityType.Builder.of(BlockEntityMatterReplicator::new, Blocks.MATTER_REPLICATOR).build(null); public static final BlockEntityType MATTER_BOTTLER = BlockEntityType.Builder.of(BlockEntityMatterBottler::new, Blocks.MATTER_BOTTLER).build(null); public static final BlockEntityType DRIVE_VIEWER = BlockEntityType.Builder.of(BlockEntityDriveViewer::new, Blocks.DRIVE_VIEWER).build(null); + public static final BlockEntityType BLACK_HOLE = BlockEntityType.Builder.of(BlockEntityBlackHole::new, Blocks.BLACK_HOLE).build(null); static { ANDROID_STATION.setRegistryName(Names.ANDROID_STATION); @@ -434,6 +447,7 @@ public class Registry { MATTER_REPLICATOR.setRegistryName(Names.MATTER_REPLICATOR); MATTER_BOTTLER.setRegistryName(Names.MATTER_BOTTLER); DRIVE_VIEWER.setRegistryName(Names.DRIVE_VIEWER); + BLACK_HOLE.setRegistryName(Names.BLACK_HOLE); } @SubscribeEvent @@ -449,6 +463,7 @@ public class Registry { event.getRegistry().register(MATTER_REPLICATOR); event.getRegistry().register(MATTER_BOTTLER); event.getRegistry().register(DRIVE_VIEWER); + event.getRegistry().register(BLACK_HOLE); // OverdriveThatMatters.LOGGER.info("Registered block entities"); } diff --git a/src/main/java/ru/dbotthepony/mc/otm/block/BlockBlackHole.java b/src/main/java/ru/dbotthepony/mc/otm/block/BlockBlackHole.java index 947fb9a17..4015831de 100644 --- a/src/main/java/ru/dbotthepony/mc/otm/block/BlockBlackHole.java +++ b/src/main/java/ru/dbotthepony/mc/otm/block/BlockBlackHole.java @@ -1,6 +1,50 @@ package ru.dbotthepony.mc.otm.block; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.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 net.minecraft.world.level.material.MaterialColor; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.VoxelShape; +import ru.dbotthepony.mc.otm.Registry; +import ru.dbotthepony.mc.otm.block.entity.BlockEntityBlackHole; +import ru.dbotthepony.mc.otm.shapes.BlockShapes; -public class BlockBlackHole extends Block { +import javax.annotation.Nullable; + +public class BlockBlackHole extends Block implements EntityBlock { + public BlockBlackHole() { + super(Properties.of( + new Material.Builder(MaterialColor.COLOR_BLACK).noCollider().nonSolid().build() + ).strength(-1, 7200000.0F)); + } + + public static final VoxelShape SHAPE = BlockShapes.BLACK_HOLE.computeShape(); + + @Override + public VoxelShape getShape(BlockState p_60555_, BlockGetter p_60556_, BlockPos p_60557_, CollisionContext p_60558_) { + return SHAPE; + } + + @Nullable + @Override + public BlockEntity newBlockEntity(BlockPos blockPos, BlockState blockState) { + return new BlockEntityBlackHole(blockPos, blockState); + } + + @Nullable + @Override + public BlockEntityTicker getTicker(Level p_153212_, BlockState p_153213_, BlockEntityType p_153214_) { + if (p_153214_ != Registry.BlockEntities.BLACK_HOLE) + return null; + + return p_153212_.isClientSide ? BlockEntityBlackHole::clientTicker : BlockEntityBlackHole::ticker; + } } diff --git a/src/main/java/ru/dbotthepony/mc/otm/block/entity/BlockEntityBlackHole.java b/src/main/java/ru/dbotthepony/mc/otm/block/entity/BlockEntityBlackHole.java new file mode 100644 index 000000000..14c41d6b3 --- /dev/null +++ b/src/main/java/ru/dbotthepony/mc/otm/block/entity/BlockEntityBlackHole.java @@ -0,0 +1,205 @@ +package ru.dbotthepony.mc.otm.block.entity; + +import net.minecraft.client.Minecraft; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.StringTag; +import net.minecraft.network.Connection; +import net.minecraft.network.protocol.PacketFlow; +import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; +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.Level; +import net.minecraft.world.level.block.Block; +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.phys.AABB; +import net.minecraft.world.phys.Vec3; +import ru.dbotthepony.mc.otm.Registry; +import ru.dbotthepony.mc.otm.capability.MatteryCapability; +import ru.dbotthepony.mc.otm.matter.MatterRegistry; + +import javax.annotation.Nullable; +import java.math.BigDecimal; + +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 BigDecimal NORMAL_MASS = new BigDecimal(10_000); + private BigDecimal mass = NORMAL_MASS; + private double gravitation_strength = 1; + private boolean suppress_updates = true; + + public void collapse() { + + } + + public void addMass(BigDecimal mass) { + setMass(this.mass.add(mass)); + } + + public void setMass(BigDecimal mass) { + if (mass.compareTo(BigDecimal.ZERO) <= 0) { + collapse(); + return; + } + + this.mass = mass; + setChanged(); + gravitation_strength = mass.divide(NORMAL_MASS, MatteryCapability.ROUND_RULES).doubleValue(); + + 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()); + return tag; + } + + public void readBlackHoleData(CompoundTag tag) { + if (tag.get("mass") instanceof StringTag str) + setMass(new BigDecimal(str.getAsString())); + } + + // disk io + @Override + public CompoundTag save(CompoundTag tag) { + writeBlackHoleData(tag); + return super.save(tag); + } + + @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 updsates + @Nullable + @Override + public ClientboundBlockEntityDataPacket getUpdatePacket() { + return new ClientboundBlockEntityDataPacket(getBlockPos(), 0, writeBlackHoleData(new CompoundTag())); + } + + // 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) { + //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(); + living.setDeltaMovement(living.getDeltaMovement().add(delta.multiply(mult, mult, mult))); + } + + public static 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); + } + + for (var item : tile.level.getEntitiesOfClass(ItemEntity.class, tile.affected_bounds_aabb)) { + final double distance = item.position().distanceTo(center); + tile.setDeltaMovement(item, center, distance); + + if (distance < tile.gravitation_strength * 2) { + if (item.hurt(Registry.DAMAGE_EVENT_HORIZON, (float) (tile.gravitation_strength / distance)) && item.isRemoved()) { + var mass = MatterRegistry.getMatterValue(item.getItem()); + + if (mass.compareTo(BigDecimal.ZERO) > 0) + tile.addMass(mass); + } + } + } + } + } + + public static 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 : tile.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); + } + + if (distance < tile.gravitation_strength * 2) { + living.hurt(Registry.DAMAGE_EVENT_HORIZON, (float) (tile.gravitation_strength / distance)); + } + } + + for (var item : tile.level.getEntitiesOfClass(ItemEntity.class, tile.affected_bounds_aabb)) { + final double distance = item.position().distanceTo(center); + tile.setDeltaMovement(item, center, distance); + + if (distance < tile.gravitation_strength * 2) { + if (item.hurt(Registry.DAMAGE_EVENT_HORIZON, (float) (tile.gravitation_strength / distance)) && item.isRemoved()) { + var mass = MatterRegistry.getMatterValue(item.getItem()); + + if (mass.compareTo(BigDecimal.ZERO) > 0) + tile.addMass(mass); + } + } + } + } + } +} diff --git a/src/main/resources/assets/overdrive_that_matters/lang/en_us.json b/src/main/resources/assets/overdrive_that_matters/lang/en_us.json index 70bd836b3..b106bc513 100644 --- a/src/main/resources/assets/overdrive_that_matters/lang/en_us.json +++ b/src/main/resources/assets/overdrive_that_matters/lang/en_us.json @@ -111,6 +111,14 @@ "otm.suffix.zepto": "%s z%s", "otm.suffix.yocto": "%s y%s", + "death.attack.otm_become_android": "%1$s lost their humanity", + "death.attack.otm_become_humane": "%1$s gained their humanity", + "death.attack.otm_event_horizon": "%1$s never crossed event horizon", + + "death.attack.otm_become_android.player": "%1$s lost their humanity whilst %2$s tried to reason with them", + "death.attack.otm_become_humane.player": "%1$s gained their humanity whilst %2$s tried to reason with them", + "death.attack.otm_event_horizon.player": "%1$s tried to cross event horizon whilst trying to escape %2$s", + "block.overdrive_that_matters.android_station": "Android station", "block.overdrive_that_matters.battery_bank": "Battery bank", "block.overdrive_that_matters.matter_decomposer": "Matter decomposer", @@ -122,6 +130,7 @@ "block.overdrive_that_matters.matter_replicator": "Matter replicator", "block.overdrive_that_matters.matter_bottler": "Matter bottler", "block.overdrive_that_matters.drive_viewer": "Drive viewer", + "block.overdrive_that_matters.black_hole": "Miniature Event Horizon", "otm.container.matter_panel.number_input": "Input replication task count", diff --git a/src/main/resources/assets/overdrive_that_matters/textures/block/black_hole.png b/src/main/resources/assets/overdrive_that_matters/textures/block/black_hole.png new file mode 100644 index 0000000000000000000000000000000000000000..4c095632b5ca176c0a5c222af07f415a78e1e279 GIT binary patch literal 141 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sDEfH31!Z9ZwBppvJHV~B-+a!NwN5B?)5%xW6GW)h|eR~Q6< e1O^6Q2S)i4YrR=O)eN4lelF{r5}E)%cOa$! literal 0 HcmV?d00001