Skip to content

Commit ca2c1cf

Browse files
feat: way more tree stuff
1 parent c15231d commit ca2c1cf

12 files changed

Lines changed: 329 additions & 15 deletions

File tree

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package net.swofty.type.hub;
2+
3+
import net.minestom.server.instance.SharedInstance;
4+
import net.swofty.type.skyblockgeneric.structure.tree.SpawnableTree;
5+
6+
public class ForestTreePlacement {
7+
public static final long DEFAULT_BASE_SEED = 12345L;
8+
9+
public static void placeTrees(SharedInstance instance) {
10+
placeTrees(instance, DEFAULT_BASE_SEED);
11+
}
12+
13+
public static void placeTrees(SharedInstance instance, long baseSeed) {
14+
long seed = baseSeed;
15+
SpawnableTree.LARGE_OAK.createAndRegister(-114, 74, -42, seed++, instance);
16+
SpawnableTree.LARGE_OAK.createAndRegister(-124, 73, -46, seed++, instance);
17+
SpawnableTree.LARGE_OAK.createAndRegister(-100, 72, -41, seed++, instance);
18+
SpawnableTree.MEDIUM_OAK.createAndRegister(-122, 73, -20, seed++, instance);
19+
SpawnableTree.LARGE_OAK.createAndRegister(-133, 73, -16, seed++, instance);
20+
SpawnableTree.LARGE_OAK.createAndRegister(-129, 72, -4, seed++, instance);
21+
SpawnableTree.LARGE_OAK.createAndRegister(-130, 71, 7, seed++, instance);
22+
SpawnableTree.MEDIUM_OAK.createAndRegister(-141, 71, 3, seed++, instance);
23+
SpawnableTree.LARGE_OAK.createAndRegister(-142, 69, 15, seed++, instance);
24+
SpawnableTree.GIANT_OAK.createAndRegister(-149, 70, 9, seed++, instance);
25+
SpawnableTree.MEDIUM_OAK.createAndRegister(-157, 72, 0, seed++, instance);
26+
SpawnableTree.LARGE_OAK.createAndRegister(-143, 73, -7, seed++, instance);
27+
SpawnableTree.LARGE_OAK.createAndRegister(-160, 73, -13, seed++, instance);
28+
SpawnableTree.LARGE_OAK.createAndRegister(-168, 72, -1, seed++, instance);
29+
SpawnableTree.LARGE_OAK.createAndRegister(-175, 74, -13, seed++, instance);
30+
SpawnableTree.LARGE_OAK.createAndRegister(-185, 74, -8, seed++, instance);
31+
SpawnableTree.LARGE_OAK.createAndRegister(-196, 74, -6, seed++, instance);
32+
SpawnableTree.LARGE_OAK.createAndRegister(-203, 74, -14, seed++, instance);
33+
SpawnableTree.LARGE_OAK.createAndRegister(-213, 74, -11, seed++, instance);
34+
SpawnableTree.LARGE_OAK.createAndRegister(-214, 73, -24, seed++, instance);
35+
SpawnableTree.LARGE_OAK.createAndRegister(-200, 74, -29, seed++, instance);
36+
SpawnableTree.LARGE_OAK.createAndRegister(-186, 74, -16, seed++, instance);
37+
SpawnableTree.LARGE_OAK.createAndRegister(-190, 75, -29, seed++, instance);
38+
SpawnableTree.GIANT_OAK.createAndRegister(-198, 74, -40, seed++, instance);
39+
SpawnableTree.LARGE_OAK.createAndRegister(-191, 76, -39, seed++, instance);
40+
SpawnableTree.LARGE_OAK.createAndRegister(-179, 74, -27, seed++, instance);
41+
SpawnableTree.MEDIUM_OAK.createAndRegister(-167, 74, -27, seed++, instance);
42+
SpawnableTree.LARGE_OAK.createAndRegister(-162, 73, -33, seed++, instance);
43+
SpawnableTree.LARGE_OAK.createAndRegister(-164, 76, -45, seed++, instance);
44+
SpawnableTree.LARGE_OAK.createAndRegister(-173, 77, -40, seed++, instance);
45+
SpawnableTree.LARGE_OAK.createAndRegister(-182, 77, -44, seed++, instance);
46+
SpawnableTree.LARGE_OAK.createAndRegister(-193, 75, -56, seed++, instance);
47+
SpawnableTree.LARGE_OAK.createAndRegister(-203, 74, -74, seed++, instance);
48+
SpawnableTree.LARGE_OAK.createAndRegister(-193, 75, -68, seed++, instance);
49+
SpawnableTree.LARGE_OAK.createAndRegister(-179, 75, -70, seed++, instance);
50+
SpawnableTree.LARGE_OAK.createAndRegister(-182, 76, -57, seed++, instance);
51+
SpawnableTree.LARGE_OAK.createAndRegister(-169, 76, -54, seed++, instance);
52+
SpawnableTree.LARGE_OAK.createAndRegister(-165, 75, -66, seed++, instance);
53+
SpawnableTree.LARGE_OAK.createAndRegister(-158, 75, -57, seed++, instance);
54+
SpawnableTree.LARGE_OAK.createAndRegister(-155, 75, -70, seed++, instance);
55+
SpawnableTree.GIANT_OAK.createAndRegister(-143, 76, -64, seed++, instance);
56+
SpawnableTree.LARGE_OAK.createAndRegister(-147, 74, -52, seed, instance);
57+
}
58+
}

type.hub/src/main/java/net/swofty/type/hub/TypeHubLoader.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,9 @@ public void afterInitialize(MinecraftServer server) {
122122
// Register callback to refresh Sirius NPC and Dark Auction display when state changes
123123
DarkAuctionHandler.setOnStateChangeCallback(darkAuctionDisplay::update);
124124

125+
// Place forest trees
126+
ForestTreePlacement.placeTrees(HypixelConst.getInstanceContainer());
127+
125128
HubMap hubMap = new HubMap();
126129
hubMap.placeItemFrames(HypixelConst.getInstanceContainer());
127130
}

type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/commands/GenerateTreeCommand.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66
import net.minestom.server.instance.SharedInstance;
77
import net.swofty.type.generic.command.CommandParameters;
88
import net.swofty.type.generic.command.HypixelCommand;
9-
import net.swofty.type.skyblockgeneric.tree.SkyBlockTree;
10-
import net.swofty.type.skyblockgeneric.tree.TreeConfig;
11-
import net.swofty.type.skyblockgeneric.tree.TreeType;
9+
import net.swofty.type.skyblockgeneric.structure.tree.SkyBlockTree;
10+
import net.swofty.type.skyblockgeneric.structure.tree.TreeConfig;
11+
import net.swofty.type.skyblockgeneric.structure.tree.TreeType;
1212
import net.swofty.type.skyblockgeneric.user.SkyBlockPlayer;
1313
import net.swofty.type.generic.user.categories.Rank;
1414

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package net.swofty.type.skyblockgeneric.commands;
2+
3+
import net.minestom.server.command.builder.arguments.ArgumentType;
4+
import net.minestom.server.command.builder.arguments.number.ArgumentInteger;
5+
import net.minestom.server.command.builder.arguments.number.ArgumentLong;
6+
import net.minestom.server.instance.SharedInstance;
7+
import net.minestom.server.instance.block.Block;
8+
import net.swofty.type.generic.command.CommandParameters;
9+
import net.swofty.type.generic.command.HypixelCommand;
10+
import net.swofty.type.skyblockgeneric.structure.tree.TreeRegistry;
11+
import net.swofty.type.skyblockgeneric.structure.tree.TreeRegistry.RegisteredTree;
12+
import net.swofty.type.skyblockgeneric.structure.tree.TreeRegistry.BlockEntry;
13+
import net.swofty.type.skyblockgeneric.user.SkyBlockPlayer;
14+
import net.swofty.type.generic.user.categories.Rank;
15+
16+
import java.util.List;
17+
18+
@CommandParameters(
19+
aliases = "regrowtrees",
20+
description = "Respawns trees in an area with a new seed",
21+
usage = "/respawntreesinarea <distance> <seed>",
22+
permission = Rank.ADMIN,
23+
allowsConsole = false
24+
)
25+
public class RespawnTreesInAreaCommand extends HypixelCommand {
26+
@Override
27+
public void registerUsage(MinestomCommand command) {
28+
ArgumentInteger distanceArg = ArgumentType.Integer("distance");
29+
ArgumentLong seedArg = ArgumentType.Long("seed");
30+
31+
command.addSyntax((sender, context) -> {
32+
if (!permissionCheck(sender)) return;
33+
34+
SkyBlockPlayer player = (SkyBlockPlayer) sender;
35+
int distance = context.get(distanceArg);
36+
long newSeed = context.get(seedArg);
37+
38+
if (distance <= 0 || distance > 200) {
39+
player.sendMessage("§cDistance must be between 1 and 200 blocks!");
40+
return;
41+
}
42+
43+
SharedInstance instance = (SharedInstance) player.getInstance();
44+
int playerX = player.getPosition().blockX();
45+
int playerY = player.getPosition().blockY();
46+
int playerZ = player.getPosition().blockZ();
47+
48+
// Find trees where at least one block is in range
49+
List<RegisteredTree> treesInRange = TreeRegistry.getTreesInRange(
50+
instance, playerX, playerY, playerZ, distance
51+
);
52+
53+
if (treesInRange.isEmpty()) {
54+
player.sendMessage("§cNo registered trees found within " + distance + " blocks!");
55+
return;
56+
}
57+
58+
int treesRespawned = 0;
59+
int blocksRemoved = 0;
60+
long currentSeed = newSeed;
61+
62+
for (RegisteredTree tree : treesInRange) {
63+
// Step 1: Verify and remove existing blocks
64+
for (BlockEntry block : tree.allBlocks()) {
65+
Block currentBlock = instance.getBlock(
66+
block.worldX(), block.worldY(), block.worldZ()
67+
);
68+
69+
// Only remove if block matches expected type
70+
if (currentBlock.compare(block.expectedBlock())) {
71+
instance.setBlock(
72+
block.worldX(), block.worldY(), block.worldZ(),
73+
Block.AIR
74+
);
75+
blocksRemoved++;
76+
}
77+
}
78+
79+
// Step 2: Unregister old tree
80+
TreeRegistry.unregisterTree(instance, tree);
81+
82+
// Step 3: Regenerate with new seed (incrementing per tree)
83+
tree.spawnableTree().createAndRegister(
84+
tree.baseX(),
85+
tree.baseY(),
86+
tree.baseZ(),
87+
currentSeed++,
88+
instance
89+
);
90+
91+
treesRespawned++;
92+
}
93+
94+
player.sendMessage("§aRespawned §e" + treesRespawned + "§a tree(s)!");
95+
player.sendMessage("§7Removed §e" + blocksRemoved + "§7 blocks, regenerated with seeds §e" + newSeed + "§7-§e" + (newSeed + treesRespawned - 1));
96+
}, distanceArg, seedArg);
97+
}
98+
}

type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/structure/SkyBlockStructure.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ protected void fill(Instance instance, int x1, int y1, int z1, int x2, int y2, i
6565
set(instance, x, y, z, block);
6666
}
6767

68-
protected int rotateValue(int value, int difference, CoordinateType type) {
68+
public int rotateValue(int value, int difference, CoordinateType type) {
6969
switch (type) {
7070
case X:
7171
switch (rotation) {

type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/tree/BranchDirection.java renamed to type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/structure/tree/BranchDirection.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package net.swofty.type.skyblockgeneric.tree;
1+
package net.swofty.type.skyblockgeneric.structure.tree;
22

33
import lombok.Getter;
44

type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/tree/SkyBlockTree.java renamed to type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/structure/tree/SkyBlockTree.java

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package net.swofty.type.skyblockgeneric.tree;
1+
package net.swofty.type.skyblockgeneric.structure.tree;
22

33
import lombok.Getter;
44
import lombok.Setter;
@@ -16,6 +16,7 @@ public class SkyBlockTree extends SkyBlockStructure {
1616
private Random random;
1717

1818
private final Set<LogPosition> placedLogs = new HashSet<>();
19+
private final Set<LogPosition> placedLeaves = new HashSet<>();
1920
private final List<LogPosition> branchEndpoints = new ArrayList<>();
2021

2122
// Calculated per-tree
@@ -48,6 +49,7 @@ public SkyBlockTree(int rotation, int x, int y, int z, TreeType treeType, TreeCo
4849
@Override
4950
public void setBlocks(Instance instance) {
5051
placedLogs.clear();
52+
placedLeaves.clear();
5153
branchEndpoints.clear();
5254
random = new Random(seed);
5355

@@ -249,12 +251,32 @@ private void generateUnifiedCanopy(Instance instance) {
249251
int centerY = sumY / branchEndpoints.size();
250252
int centerZ = sumZ / branchEndpoints.size();
251253

252-
// Canopy radius based on target width
253-
int radius = Math.max(config.leafRadius(), targetWidth);
254+
// Calculate required radius based on furthest log from centroid
255+
// This ensures all logs are covered by leaves
256+
double maxLogDistance = 0;
257+
for (LogPosition pos : placedLogs) {
258+
double dx = pos.x() - centerX;
259+
double dz = pos.z() - centerZ;
260+
double dist = Math.sqrt(dx * dx + dz * dz);
261+
maxLogDistance = Math.max(maxLogDistance, dist);
262+
}
263+
264+
// 20% of trees are "bulbous" with fuller leaves, 80% are sparser
265+
boolean isBulbous = random.nextDouble() < 0.2;
266+
267+
// Radius buffer: bulbous trees get +2, sparse trees get +1
268+
int radiusBuffer = isBulbous ? 2 : 1;
269+
int radius = Math.max((int) Math.ceil(maxLogDistance) + radiusBuffer, config.leafRadius());
270+
271+
// Extra skip chance for sparse trees
272+
double sparseSkipBonus = isBulbous ? 0.0 : 0.15;
254273

255274
// Generate the unified canopy
275+
// Leaves don't hang as low: only go down 1-2 blocks max instead of radius/2
276+
int minDy = isBulbous ? -2 : -1;
277+
256278
for (int dx = -radius; dx <= radius; dx++) {
257-
for (int dy = -radius / 2 - 1; dy <= radius; dy++) {
279+
for (int dy = minDy; dy <= radius; dy++) {
258280
for (int dz = -radius; dz <= radius; dz++) {
259281
int leafX = centerX + dx;
260282
int leafY = centerY + dy;
@@ -269,9 +291,10 @@ private void generateUnifiedCanopy(Instance instance) {
269291
if (totalDist <= threshold) {
270292
LogPosition pos = new LogPosition(leafX, leafY, leafZ);
271293
if (!placedLogs.contains(pos)) {
272-
double skipChance = calculateLeafSkipChance(horizontalDist, dy, radius, totalDist);
294+
double skipChance = calculateLeafSkipChance(horizontalDist, dy, radius, totalDist) + sparseSkipBonus;
273295
if (random.nextDouble() > skipChance) {
274296
set(instance, leafX, leafY, leafZ, treeType.getLeavesBlock());
297+
placedLeaves.add(pos);
275298
}
276299
}
277300
}
@@ -323,5 +346,13 @@ public List<StructureHologram> getHolograms() {
323346
return List.of();
324347
}
325348

326-
private record LogPosition(int x, int y, int z) {}
349+
public Set<LogPosition> getPlacedLogs() {
350+
return Collections.unmodifiableSet(placedLogs);
351+
}
352+
353+
public Set<LogPosition> getPlacedLeaves() {
354+
return Collections.unmodifiableSet(placedLeaves);
355+
}
356+
357+
public record LogPosition(int x, int y, int z) {}
327358
}

type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/tree/SpawnableTree.java renamed to type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/structure/tree/SpawnableTree.java

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
1-
package net.swofty.type.skyblockgeneric.tree;
1+
package net.swofty.type.skyblockgeneric.structure.tree;
22

33
import lombok.Getter;
4+
import net.minestom.server.instance.SharedInstance;
45
import net.minestom.server.instance.block.Block;
6+
import net.swofty.type.skyblockgeneric.structure.SkyBlockStructure;
7+
8+
import java.util.ArrayList;
59

610
@Getter
711
public enum SpawnableTree {
@@ -80,4 +84,37 @@ public SkyBlockTree createAt(int x, int y, int z, long seed) {
8084
public SkyBlockTree createAt(int rotation, int x, int y, int z) {
8185
return new SkyBlockTree(rotation, x, y, z, treeType, config);
8286
}
87+
88+
/**
89+
* Create a SkyBlockTree, build it, and register it in the TreeRegistry.
90+
* This allows the tree to be tracked for later respawning.
91+
*/
92+
public SkyBlockTree createAndRegister(int x, int y, int z, long seed, SharedInstance instance) {
93+
SkyBlockTree tree = new SkyBlockTree(0, x, y, z, treeType, config, seed);
94+
tree.build(instance);
95+
96+
// Convert local coordinates to world coordinates and build block list
97+
var blocks = new ArrayList<TreeRegistry.BlockEntry>();
98+
99+
for (SkyBlockTree.LogPosition log : tree.getPlacedLogs()) {
100+
int worldX = tree.rotateValue(x, log.x(), SkyBlockStructure.CoordinateType.X);
101+
int worldY = y + log.y();
102+
int worldZ = tree.rotateValue(z, log.z(), SkyBlockStructure.CoordinateType.Z);
103+
blocks.add(new TreeRegistry.BlockEntry(worldX, worldY, worldZ, treeType.getLogBlock()));
104+
}
105+
106+
for (SkyBlockTree.LogPosition leaf : tree.getPlacedLeaves()) {
107+
int worldX = tree.rotateValue(x, leaf.x(), SkyBlockStructure.CoordinateType.X);
108+
int worldY = y + leaf.y();
109+
int worldZ = tree.rotateValue(z, leaf.z(), SkyBlockStructure.CoordinateType.Z);
110+
blocks.add(new TreeRegistry.BlockEntry(worldX, worldY, worldZ, treeType.getLeavesBlock()));
111+
}
112+
113+
TreeRegistry.RegisteredTree registeredTree = new TreeRegistry.RegisteredTree(
114+
x, y, z, this, seed, blocks
115+
);
116+
TreeRegistry.registerTree(instance, registeredTree);
117+
118+
return tree;
119+
}
83120
}

type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/tree/TreeBranch.java renamed to type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/structure/tree/TreeBranch.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package net.swofty.type.skyblockgeneric.tree;
1+
package net.swofty.type.skyblockgeneric.structure.tree;
22

33
public record TreeBranch(
44
int x,

type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/tree/TreeConfig.java renamed to type.skyblockgeneric/src/main/java/net/swofty/type/skyblockgeneric/structure/tree/TreeConfig.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package net.swofty.type.skyblockgeneric.tree;
1+
package net.swofty.type.skyblockgeneric.structure.tree;
22

33
public record TreeConfig(
44
int minHeight,

0 commit comments

Comments
 (0)