187 lines
7.8 KiB
Java
187 lines
7.8 KiB
Java
package com.hypixel.hytale.server.worldgen.climate;
|
|
|
|
import com.hypixel.hytale.math.vector.Vector2i;
|
|
import com.hypixel.hytale.server.worldgen.util.LogUtil;
|
|
import com.hypixel.hytale.server.worldgen.zone.Zone;
|
|
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.concurrent.CompletableFuture;
|
|
import java.util.logging.Level;
|
|
import javax.annotation.Nonnull;
|
|
import javax.annotation.Nullable;
|
|
|
|
public class UniqueClimateGenerator {
|
|
public static final UniqueClimateGenerator EMPTY = new UniqueClimateGenerator(
|
|
UniqueClimateGenerator.Entry.EMPTY_ARRAY, UniqueClimateGenerator.Unique.EMPTY_ARRAY
|
|
);
|
|
private static final int[] EMPTY_PARENTS = new int[0];
|
|
private static final int MAX_PARENT_DEPTH = 10;
|
|
private static final Vector2i DEFAULT_ORIGIN = new Vector2i(0, 0);
|
|
private static final Vector2i[] EMPTY_POSITIONS = new Vector2i[0];
|
|
protected final UniqueClimateGenerator.Entry[] entries;
|
|
protected final UniqueClimateGenerator.Unique[] zones;
|
|
|
|
public UniqueClimateGenerator(@Nonnull UniqueClimateGenerator.Entry[] entries) {
|
|
this(entries, UniqueClimateGenerator.Unique.EMPTY_ARRAY);
|
|
}
|
|
|
|
public UniqueClimateGenerator(@Nonnull UniqueClimateGenerator.Entry[] entries, @Nonnull UniqueClimateGenerator.Unique[] zones) {
|
|
this.entries = entries;
|
|
this.zones = zones;
|
|
}
|
|
|
|
public UniqueClimateGenerator.Entry[] entries() {
|
|
return this.entries;
|
|
}
|
|
|
|
public UniqueClimateGenerator.Unique[] zones() {
|
|
return this.zones;
|
|
}
|
|
|
|
public int generate(int x, int y) {
|
|
for (int i = 0; i < this.zones.length; i++) {
|
|
if (this.zones[i].contains(x, y)) {
|
|
return this.zones[i].color;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
public Zone.UniqueCandidate[] getCandidates(Map<String, Zone> zoneLookup) {
|
|
if (this.entries.length == 0) {
|
|
return Zone.UniqueCandidate.EMPTY_ARRAY;
|
|
} else {
|
|
Zone.UniqueCandidate[] candidates = new Zone.UniqueCandidate[this.entries.length];
|
|
|
|
for (int i = 0; i < this.entries.length; i++) {
|
|
UniqueClimateGenerator.Entry entry = this.entries[i];
|
|
Zone zone = zoneLookup.get(entry.zone);
|
|
if (zone == null) {
|
|
throw new Error("Could not find zone: " + entry.zone);
|
|
}
|
|
|
|
candidates[i] = new Zone.UniqueCandidate(new Zone.UniqueEntry(zone, entry.color, EMPTY_PARENTS, entry.radius, 0), EMPTY_POSITIONS);
|
|
}
|
|
|
|
return candidates;
|
|
}
|
|
}
|
|
|
|
public UniqueClimateGenerator apply(
|
|
int seed, @Nonnull Zone.UniqueCandidate[] candidates, @Nonnull ClimateNoise noise, @Nonnull ClimateGraph graph, @Nonnull List<Zone.Unique> collector
|
|
) {
|
|
if (candidates.length != this.entries.length) {
|
|
LogUtil.getLogger()
|
|
.at(Level.WARNING)
|
|
.log("Mismatched unique climate generator candidates: expected %d, got %d", this.entries.length, candidates.length);
|
|
return this;
|
|
} else {
|
|
UniqueClimateGenerator.Unique[] unique = new UniqueClimateGenerator.Unique[candidates.length];
|
|
Object2ObjectOpenHashMap<String, UniqueClimateGenerator.Unique> lookup = new Object2ObjectOpenHashMap();
|
|
|
|
for (int it = 0; it < 10 && lookup.size() < unique.length; it++) {
|
|
for (int i = 0; i < this.entries.length; i++) {
|
|
if (unique[i] == null) {
|
|
UniqueClimateGenerator.Entry entry = this.entries[i];
|
|
UniqueClimateGenerator.Unique parent = (UniqueClimateGenerator.Unique)lookup.get(entry.parent);
|
|
if (entry.parent.isEmpty() || parent != null) {
|
|
CompletableFuture<Vector2i> position = findZonePosition(seed, DEFAULT_ORIGIN, entry, parent, noise, graph);
|
|
unique[i] = new UniqueClimateGenerator.Unique(entry.color, entry.radius, position);
|
|
collector.add(new Zone.Unique(candidates[i].zone().zone(), position));
|
|
lookup.put(entry.zone, unique[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return new UniqueClimateGenerator(this.entries, unique);
|
|
}
|
|
}
|
|
|
|
public UniqueClimateGenerator apply(int seed, @Nonnull ClimateNoise noise, @Nonnull ClimateGraph graph) {
|
|
UniqueClimateGenerator.Unique[] unique = new UniqueClimateGenerator.Unique[this.entries.length];
|
|
Object2ObjectOpenHashMap<String, UniqueClimateGenerator.Unique> lookup = new Object2ObjectOpenHashMap();
|
|
|
|
for (int it = 0; it < 10 && lookup.size() < unique.length; it++) {
|
|
for (int i = 0; i < this.entries.length; i++) {
|
|
if (unique[i] == null) {
|
|
UniqueClimateGenerator.Entry entry = this.entries[i];
|
|
UniqueClimateGenerator.Unique parent = (UniqueClimateGenerator.Unique)lookup.get(entry.parent);
|
|
if (entry.parent.isEmpty() || parent != null) {
|
|
CompletableFuture<Vector2i> position = findZonePosition(seed, DEFAULT_ORIGIN, entry, parent, noise, graph);
|
|
unique[i] = new UniqueClimateGenerator.Unique(entry.color, entry.radius, position);
|
|
lookup.put(entry.zone, unique[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (lookup.size() < unique.length) {
|
|
LogUtil.getLogger().at(Level.WARNING).log("Could not resolve all unique climate zones, resolved %d out of %d", lookup.size(), unique.length);
|
|
UniqueClimateGenerator.Entry[] newEntries = new UniqueClimateGenerator.Entry[lookup.size()];
|
|
UniqueClimateGenerator.Unique[] newUnique = new UniqueClimateGenerator.Unique[lookup.size()];
|
|
int index = 0;
|
|
|
|
for (int ix = 0; ix < unique.length; ix++) {
|
|
if (unique[ix] != null) {
|
|
newEntries[index] = this.entries[ix];
|
|
newUnique[index] = unique[ix];
|
|
index++;
|
|
}
|
|
}
|
|
|
|
return new UniqueClimateGenerator(newEntries, newUnique);
|
|
} else {
|
|
return new UniqueClimateGenerator(this.entries, unique);
|
|
}
|
|
}
|
|
|
|
protected static CompletableFuture<Vector2i> findZonePosition(
|
|
int seed,
|
|
Vector2i origin,
|
|
@Nonnull UniqueClimateGenerator.Entry entry,
|
|
@Nullable UniqueClimateGenerator.Unique parent,
|
|
@Nonnull ClimateNoise noise,
|
|
@Nonnull ClimateGraph graph
|
|
) {
|
|
return parent != null
|
|
? parent.position.thenCompose(pos -> findZonePosition(seed, pos, entry, null, noise, graph))
|
|
: ClimateSearch.search(seed, origin.x + entry.origin.x, origin.y + entry.origin.y, entry.minDistance, entry.maxDistance, entry.rule, noise, graph)
|
|
.thenApply(result -> {
|
|
LogUtil.getLogger().at(Level.INFO).log("Found location for unique zone '%s' -> %s", entry.zone, result.pretty());
|
|
return result.position();
|
|
});
|
|
}
|
|
|
|
public record Entry(
|
|
@Nonnull String zone,
|
|
@Nonnull String parent,
|
|
int color,
|
|
int radius,
|
|
@Nonnull Vector2i origin,
|
|
int minDistance,
|
|
int maxDistance,
|
|
@Nonnull ClimateSearch.Rule rule
|
|
) {
|
|
public static final UniqueClimateGenerator.Entry[] EMPTY_ARRAY = new UniqueClimateGenerator.Entry[0];
|
|
public static final String DEFAULT_PARENT = "";
|
|
}
|
|
|
|
public record Unique(int color, int radius, int radius2, @Nonnull CompletableFuture<Vector2i> position) {
|
|
public static final UniqueClimateGenerator.Unique[] EMPTY_ARRAY = new UniqueClimateGenerator.Unique[0];
|
|
|
|
public Unique(int color, int radius, @Nonnull CompletableFuture<Vector2i> position) {
|
|
this(color, radius, radius * radius, position);
|
|
}
|
|
|
|
public boolean contains(int x, int y) {
|
|
Vector2i pos = this.position.join();
|
|
int dx = x - pos.x;
|
|
int dy = y - pos.y;
|
|
return dx >= -this.radius && dy >= -this.radius && dx <= this.radius && dy <= this.radius && dx * dx + dy * dy <= this.radius2;
|
|
}
|
|
}
|
|
}
|