diff --git a/src/main/java/github/kasuminova/stellarcore/common/config/category/Features.java b/src/main/java/github/kasuminova/stellarcore/common/config/category/Features.java index 140bdb0..997e7a3 100644 --- a/src/main/java/github/kasuminova/stellarcore/common/config/category/Features.java +++ b/src/main/java/github/kasuminova/stellarcore/common/config/category/Features.java @@ -70,6 +70,10 @@ public class Features { @Config.Name("MoreElectricTools") public final MoreElectricTools moreElectricTools = new MoreElectricTools(); + + @Config.Name("DraconicEvolution") + public final DraconicEvolution draconicEvolution = new DraconicEvolution(); + public static class Vanilla { @Config.Comment("(Server) Allows CriterionProgress to be serialized in multiple threads.") @@ -275,4 +279,12 @@ public static class MoreElectricTools { } + public static class DraconicEvolution { + + @Config.Comment("This option modifies the way Chaos Islands are generated") + @Config.Name("ChaosIslandChunks") + public boolean ChaosIslandChunks = true; + + } + } diff --git a/src/main/java/github/kasuminova/stellarcore/mixin/StellarCoreLateMixinLoader.java b/src/main/java/github/kasuminova/stellarcore/mixin/StellarCoreLateMixinLoader.java index 65d672d..758e1bc 100644 --- a/src/main/java/github/kasuminova/stellarcore/mixin/StellarCoreLateMixinLoader.java +++ b/src/main/java/github/kasuminova/stellarcore/mixin/StellarCoreLateMixinLoader.java @@ -35,6 +35,7 @@ public class StellarCoreLateMixinLoader implements ILateMixinLoader { addModdedMixinCFG("mixins.stellar_core_customstartinggear.json", "customstartinggear"); addModdedMixinCFG("mixins.stellar_core_deepmoblearing.json", "deepmoblearning"); addModdedMixinCFG("mixins.stellar_core_draconicevolution.json", "draconicevolution"); + addModdedMixinCFG("mixins.stellar_core_draconicevolution_chunk.json","draconicevolution", () -> StellarCoreConfig.FEATURES.draconicEvolution.ChaosIslandChunks); addModdedMixinCFG("mixins.stellar_core_ebwizardry.json", "ebwizardry"); addModdedMixinCFG("mixins.stellar_core_endercore.json", "endercore"); addModdedMixinCFG("mixins.stellar_core_enderio.json", "enderio"); diff --git a/src/main/java/github/kasuminova/stellarcore/mixin/draconicevolution/MixinChaosWorldGenHandler.java b/src/main/java/github/kasuminova/stellarcore/mixin/draconicevolution/MixinChaosWorldGenHandler.java new file mode 100644 index 0000000..efe4676 --- /dev/null +++ b/src/main/java/github/kasuminova/stellarcore/mixin/draconicevolution/MixinChaosWorldGenHandler.java @@ -0,0 +1,168 @@ +package github.kasuminova.stellarcore.mixin.draconicevolution; + +import com.brandon3055.brandonscore.lib.PairXZ; +import com.brandon3055.brandonscore.registry.ModFeatureParser; +import com.brandon3055.brandonscore.utils.SimplexNoise; +import com.brandon3055.brandonscore.utils.Utils; +import com.brandon3055.draconicevolution.DEConfig; +import com.brandon3055.draconicevolution.DEFeatures; +import com.brandon3055.draconicevolution.blocks.tileentity.TileChaosCrystal; +import com.brandon3055.draconicevolution.world.ChaosWorldGenHandler; +import net.minecraft.block.Block; +import net.minecraft.init.Blocks; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; + +import java.util.Random; + +import static com.brandon3055.draconicevolution.world.ChaosWorldGenHandler.getClosestChaosSpawn; + +@Mixin(ChaosWorldGenHandler.class) +public class MixinChaosWorldGenHandler { + + /** + * @author sddsd2332 + * @reason 区块生成 与高版本一致 + */ + @Overwrite(remap = false) + public static void generateChunk(World world, int chunkX, int chunkZ, PairXZ islandCenter, Random random) { + PairXZ closestSpawn = islandCenter == null ? getClosestChaosSpawn(chunkX, chunkZ) : islandCenter; + if (closestSpawn.x == 0 && closestSpawn.z == 0) { + return; + } + int posX = chunkX * 16; + int posZ = chunkZ * 16; + int copyStartDistance = 180; + if (Math.abs(posX - closestSpawn.x) > copyStartDistance || Math.abs(posZ - closestSpawn.z) > copyStartDistance) { + return; + } + + + if (closestSpawn.x > posX && closestSpawn.x <= posX + 16 && closestSpawn.z > posZ && closestSpawn.z <= posZ + 16) { + generateStructures(world, closestSpawn, random); + } + + if (!DEConfig.chaosIslandVoidMode) { + for (int trueX = posX; trueX < posX + 16; trueX++) { + for (int y = 0; y < 255; y++) { + for (int trueZ = posZ; trueZ < posZ + 16; trueZ++) { + int x = trueX - closestSpawn.x; + int z = trueZ - closestSpawn.z; + int size = 80; + double dist = Math.sqrt(x * x + (y - 16) * (y - 16) + z * z); + double xd, yd, zd; + double density, centerFalloff, plateauFalloff, heightMapFalloff; + + xd = (double) x / size; + yd = (double) y / (32); + zd = (double) z / size; + + //Calculate Center Falloff + centerFalloff = 1D / (dist * 0.05D); + if (centerFalloff < 0) centerFalloff = 0; + + //Calculate Plateau Falloff + if (yd < 0.4D) { + plateauFalloff = yd * 2.5D; + } else if (yd <= 0.6D) { + plateauFalloff = 1D; + } else if (yd > 0.6D && yd < 1D) { + plateauFalloff = 1D - (yd - 0.6D) * 2.5D; + } else { + plateauFalloff = 0; + } + + //Trim Further calculations + if (plateauFalloff == 0 || centerFalloff == 0) { + continue; + } + + //Calculate heightMapFalloff + heightMapFalloff = 0; + for (int octave = 1; octave < 5; octave++) { + heightMapFalloff += ((SimplexNoise.noise(xd * octave + closestSpawn.x, zd * octave + closestSpawn.z) + 1) * 0.5D) * 0.01D * (octave * 10D * 1 - (dist * 0.001D)); + } + if (heightMapFalloff <= 0) { + heightMapFalloff = 0; + } + heightMapFalloff += ((0.5D - Math.abs(yd - 0.5D)) * 0.15D); + if (heightMapFalloff == 0) { + continue; + } + + density = centerFalloff * plateauFalloff * heightMapFalloff; + + BlockPos pos = new BlockPos(x + closestSpawn.x, y + 64 + DEConfig.chaosIslandYOffset, z + closestSpawn.z); + if (density > 0.1 && (world.isAirBlock(pos) && world.getBlockState(pos).getBlock() != DEFeatures.chaosShardAtmos)) { + world.setBlockState(pos, (dist > 60 || dist > random.nextInt(60)) ? Blocks.END_STONE.getDefaultState() : Blocks.OBSIDIAN.getDefaultState()); + } + } + } + } + } + } + + /** + * @author sddsd2332 + * @reason 生成核心部分 + */ + @Overwrite(remap = false) + public static void generateStructures(World world, PairXZ islandCenter, Random random) { + int outerRadius = 330; + + //Gen Chaos Cavern + int shardY = 80 + DEConfig.chaosIslandYOffset; + int coreHeight = 10; + int coreWidth = 20; + for (int y = shardY - coreHeight; y <= shardY + coreHeight; y++) { + int h = Math.abs(y - shardY); + int inRadius = h - 3; + double yp = (coreHeight - h) / (double) coreHeight; + int outRadius = (int) (yp * coreWidth); + outRadius -= (outRadius * outRadius) / 100; + + genCoreSlice(world, islandCenter.x, y, islandCenter.z, inRadius, shardY, coreWidth, true, random); + genCoreSlice(world, islandCenter.x, y, islandCenter.z, outRadius, shardY, coreWidth, false, random); + } + BlockPos center = new BlockPos(islandCenter.x, shardY, islandCenter.z); + + if (ModFeatureParser.isEnabled(DEFeatures.chaosCrystal)) { + world.setBlockState(center, DEFeatures.chaosCrystal.getDefaultState()); + TileChaosCrystal tileChaosShard = (TileChaosCrystal) world.getTileEntity(center); + tileChaosShard.setLockPos(); + } + + } + + /** + * @author sddsd2332 + * @reason 生成核心外部黑曜石 + */ + @Overwrite(remap = false) + public static void genCoreSlice(World world, int xi, int yi, int zi, int ringRadius, int yc, int coreRadious, boolean fillIn, Random rand) { + if (DEConfig.chaosIslandVoidMode) return; + for (int x = xi - coreRadious; x <= xi + coreRadious; x++) { + for (int z = zi - coreRadious; z <= zi + coreRadious; z++) { + double dist = Utils.getDistanceAtoB(x, yi, z, xi, yc, zi); + double oRad = coreRadious - (Math.abs(yc - yi) * Math.abs(yc - yi)) / 10; + if (dist > oRad - 3D && rand.nextDouble() * 3D < dist - (oRad - 3D)) { + continue; + } + if (fillIn && (int) (Utils.getDistanceAtoB(x, z, xi, zi)) <= ringRadius) { + if ((int) dist < 9) + world.setBlockState(new BlockPos(x, yi, z), DEFeatures.infusedObsidian.getDefaultState()); + else world.setBlockState(new BlockPos(x, yi, z), Blocks.OBSIDIAN.getDefaultState()); + } else if (!fillIn && (int) (Utils.getDistanceAtoB(x, z, xi, zi)) >= ringRadius) { + world.setBlockState(new BlockPos(x, yi, z), Blocks.OBSIDIAN.getDefaultState()); + } else if (!fillIn && (int) Utils.getDistanceAtoB(x, z, xi, zi) <= ringRadius) { + Block b = world.getBlockState(new BlockPos(x, yi, z)).getBlock(); + if (b == Blocks.AIR || b == Blocks.END_STONE || b == Blocks.OBSIDIAN) + world.setBlockState(new BlockPos(x, yi, z), DEFeatures.chaosShardAtmos.getDefaultState()); + } + + } + } + } +} diff --git a/src/main/java/github/kasuminova/stellarcore/mixin/draconicevolution/MixinTileChaosCrystal.java b/src/main/java/github/kasuminova/stellarcore/mixin/draconicevolution/MixinTileChaosCrystal.java new file mode 100644 index 0000000..91743cb --- /dev/null +++ b/src/main/java/github/kasuminova/stellarcore/mixin/draconicevolution/MixinTileChaosCrystal.java @@ -0,0 +1,269 @@ +package github.kasuminova.stellarcore.mixin.draconicevolution; + +import com.brandon3055.brandonscore.blocks.TileBCBase; +import com.brandon3055.brandonscore.lib.datamanager.ManagedBool; +import com.brandon3055.brandonscore.lib.datamanager.ManagedInt; +import com.brandon3055.brandonscore.lib.datamanager.ManagedLong; +import com.brandon3055.brandonscore.utils.Utils; +import com.brandon3055.draconicevolution.DEConfig; +import com.brandon3055.draconicevolution.DEFeatures; +import com.brandon3055.draconicevolution.blocks.tileentity.TileChaosCrystal; +import com.brandon3055.draconicevolution.entity.EntityChaosGuardian; +import com.brandon3055.draconicevolution.entity.EntityGuardianCrystal; +import com.brandon3055.draconicevolution.lib.DESoundHandler; +import net.minecraft.entity.Entity; +import net.minecraft.entity.effect.EntityLightningBolt; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.Blocks; +import net.minecraft.util.ITickable; +import net.minecraft.util.SoundCategory; +import net.minecraft.util.math.AxisAlignedBB; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import org.spongepowered.asm.mixin.*; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +@Mixin(TileChaosCrystal.class) +public abstract class MixinTileChaosCrystal extends TileBCBase implements ITickable { + + @Shadow(remap = false) + public int tick; + @Final + @Shadow(remap = false) + private ManagedLong posLock; + + @Final + @Shadow(remap = false) + private ManagedInt dimLock; + + @Final + @Shadow(remap = false) + public ManagedBool guardianDefeated; + + @Shadow(remap = false) + boolean validateOldHash; + @Shadow(remap = false) + int oldhash; + @Shadow(remap = false) + private int soundTimer; + + @Unique + public boolean isChunkGenerated = false; + @Unique + public boolean isChunkGeneratedOK = false; + + /** + * @author sddsd2332 + * @reason 添加区块生成判断 + */ + @Overwrite(remap = false) + public void setLockPos() { + posLock.value = pos.toLong(); + dimLock.value = world.provider.getDimension(); + isChunkGenerated = true; + } + + /** + * @author sddsd2332 + * @reason 覆写原来的更新,用于添加混沌水晶和混沌龙 + */ + @Override + @Overwrite(remap = false) + public void update() { + tick++; + + //Prevent existing crystals breaking due to update + if (validateOldHash) { + int hash = (pos.toString() + String.valueOf(world.provider.getDimension())).hashCode(); + if (hash == oldhash) { + setLockPos(); + } else { + world.setBlockToAir(pos); + } + validateOldHash = false; + } + + if (isChunkGenerated && !isChunkGeneratedOK) { + isChunkGeneratedOK = true; + } + + + if (tick > 1 && !world.isRemote && hasBeenMoved()) { + world.setBlockToAir(pos); + } + + if (world.isRemote && soundTimer-- <= 0) { + soundTimer = 3600 + world.rand.nextInt(1200); + world.playSound(pos.getX() + 0.5D, pos.getY() + 0.5D, pos.getZ() + 0.5D, DESoundHandler.chaosChamberAmbient, SoundCategory.AMBIENT, 1.5F, world.rand.nextFloat() * 0.4F + 0.8F, false); + } + + if (!world.isRemote && guardianDefeated.value && world.rand.nextInt(50) == 0) { + int x = 5 - world.rand.nextInt(11); + int z = 5 - world.rand.nextInt(11); + EntityLightningBolt bolt = new EntityLightningBolt(world, pos.getX() + x, world.getTopSolidOrLiquidBlock(pos.add(x, 0, z)).getY(), pos.getZ() + z, false); + bolt.ignoreFrustumCheck = true; + world.addWeatherEffect(bolt); + } + + if (!world.isRemote && isChunkGeneratedOK) { + if (tick % 20 == 0 && !checkRangePlayer) { + isCheckRangePlayer(); + } + + if (checkRangePlayer && !SummoningPillars) { + isSummoningPillars(); + } + if (SummoningPillars && !SummoningDragon) { + EntityChaosGuardian guardian = new EntityChaosGuardian(world); + guardian.setPosition(getPos().getX(), getPos().getY(), getPos().getZ()); + guardian.homeY = getPos().getY(); + world.spawnEntity(guardian); + SummoningDragon = true; + } + } + + } + + @Unique + public boolean SummoningDragon = false; + + @Unique + public boolean checkRangePlayer = false; + + //检查范围内是否有玩家 + @Unique + public void isCheckRangePlayer() { + BlockPos pos1 = new BlockPos(getPos().up(15).getX() + 15, getPos().up(15).getY() + 15, getPos().up(15).getZ() + 15); + BlockPos pos2 = new BlockPos(getPos().down(15).getX() - 15, getPos().down(15).getY() - 15, getPos().down(15).getZ() - 15); + AxisAlignedBB entitys = new AxisAlignedBB(pos1, pos2); + + List entitiesToDie = getWorld().getEntitiesWithinAABB(Entity.class, entitys); + for (Entity entity : entitiesToDie) { + if (entity instanceof EntityPlayer player) { + if (pos.up(15).distanceSq(player.posX, player.posY, player.posZ) <= 15 * 15) { + checkRangePlayer = true; + break; + } + } + } + } + + + @Unique + public boolean SummoningPillars = false; + + @Unique + private int respawnStateTicks; + + //生成混沌水晶柱子 + @Unique + public void isSummoningPillars() { + int spawnRate = 15; + int i = this.respawnStateTicks++; + boolean spawn = i % spawnRate == 0; + if (spawn) { + BlockPos nextSpawn = getNextCrystalPos(i == 0); + if (nextSpawn != null) { + for (BlockPos blockpos : BlockPos.getAllInBox(nextSpawn.add(-10, -10, -10), nextSpawn.add(10, 10, 10))) { + world.setBlockToAir(blockpos); + } + generateObelisk(world, nextSpawn, world.rand); + world.setBlockState(nextSpawn, DEFeatures.infusedObsidian.getDefaultState()); + EntityGuardianCrystal crystal = new EntityGuardianCrystal(world); + crystal.setPosition(nextSpawn.getX() + 0.5, nextSpawn.getY() + 1, nextSpawn.getZ() + 0.5); + world.spawnEntity(crystal); + } else { + SummoningPillars = true; + } + } + } + + + @Unique + private List crystalSpawnList; + + @Unique + private List crystalsPosCache; + + + @Unique + public List getCrystalPositions() { + if (crystalsPosCache == null) { + crystalsPosCache = new ArrayList<>(); + for (int i = 0; i < 7; i++) { + double rotation = i * 0.9D; + int sX = getPos().getX() + (int) (Math.sin(rotation) * 45); + int sZ = getPos().getZ() + (int) (Math.cos(rotation) * 45); + crystalsPosCache.add(new BlockPos(sX, getPos().getY() + 40, sZ)); + } + for (int i = 0; i < 14; i++) { + double rotation = i * 0.45D; + int sX = getPos().getX() + (int) (Math.sin(rotation) * 90); + int sZ = getPos().getZ() + (int) (Math.cos(rotation) * 90); + crystalsPosCache.add(new BlockPos(sX, getPos().getY() + 45, sZ)); + } + + } + return crystalsPosCache; + } + + @Unique + public BlockPos getNextCrystalPos(boolean initial) { + if (initial) { + crystalSpawnList = new ArrayList<>(getCrystalPositions()); + } + if (crystalSpawnList.isEmpty()) { + return null; + } + return crystalSpawnList.remove(0); + } + + @Unique + private static void generateObelisk(World world, BlockPos genPos, Random rand) { + for (int i = 0; i < 20; i += 3) { + EntityLightningBolt entity = new EntityLightningBolt(world, genPos.getX() - 2 + rand.nextInt(5), genPos.getY() - rand.nextInt(20), genPos.getZ() - 2 + rand.nextInt(5), false); + world.addWeatherEffect(entity); + } + if (DEConfig.chaosIslandVoidMode) return; + + int r = 3; + BlockPos.getAllInBox(genPos.add(-r, -25, -r), genPos.add(r, 4, r)).forEach(pos -> { + if (pos.getY() < genPos.getY()) { + double pct = (double) (genPos.getY() - pos.getY()) / 25D; + if (Utils.getDistanceAtoB(pos.getX(), pos.getZ(), genPos.getX(), genPos.getZ()) <= r + 0.5) { + if (1D - pct > rand.nextDouble()) { + float block = rand.nextFloat(); + if (block < 0.1) { + world.setBlockState(new BlockPos(pos), DEFeatures.infusedObsidian.getDefaultState()); + } else if (block < 0.4) { + world.setBlockState(new BlockPos(pos), Blocks.NETHER_BRICK.getDefaultState()); + } else { + world.setBlockState(new BlockPos(pos), Blocks.OBSIDIAN.getDefaultState()); + } + } + } + } + int relY = pos.getY() - genPos.getY(); + int absRelX = Math.abs(pos.getX() - genPos.getX()); + int absRelZ = Math.abs(pos.getZ() - genPos.getZ()); + if ((absRelX == 2 || absRelZ == 2) && absRelX <= 2 && absRelZ <= 2 && relY < 4 && relY > -1) { + world.setBlockState(pos, Blocks.IRON_BARS.getDefaultState()); + } + if (relY == 4 && absRelX <= 2 && absRelZ <= 2) { + world.setBlockState(pos, Blocks.STONE_SLAB.getStateFromMeta(6)); + } + }); + + } + + + @Shadow(remap = false) + private boolean hasBeenMoved() { + return posLock.value != pos.toLong() || dimLock.value != world.provider.getDimension(); + } + + +} diff --git a/src/main/resources/mixins.stellar_core_draconicevolution_chunk.json b/src/main/resources/mixins.stellar_core_draconicevolution_chunk.json new file mode 100644 index 0000000..d53ad23 --- /dev/null +++ b/src/main/resources/mixins.stellar_core_draconicevolution_chunk.json @@ -0,0 +1,11 @@ +{ + "package": "github.kasuminova.stellarcore.mixin.draconicevolution", + "refmap": "mixins.stellar_core.refmap.json", + "target": "@env(DEFAULT)", + "minVersion": "0.8", + "compatibilityLevel": "JAVA_8", + "mixins": [ + "MixinTileChaosCrystal", + "MixinChaosWorldGenHandler" + ] +} \ No newline at end of file