- Версия(и) Minecraft
- 1.12.2
Содержание
- Генерация биомов (часть 1)
- Генерация биомов (часть 2)
Предисловие
К данному руководству прикреплены исходники, а также картинки и массивы всех использованных слоев GenLayer. В руководстве для описания слоев GenLayer использовалось 3 разных чанка из Обычного мира (без добавления новых биомов), на которых сгенерировались столовые горы, грибной остров и подсолнечное поле. Сиды и координаты чанков можно найти в папках.Оглавление
- Генерация биомов в мире (часть 1) - в данном разделе я постараюсь наиболее наглядно и понятно объяснить по какому принципу работает генератор биомов.
- Собственный биом - в данном разделе я расскажу, как добавить генерацию биома (Лазуритовая земля), который будет обладать небольшой холмистой границей (Окраина лазуритовой земли) и иметь редкую вариацию (Лазуритовая долина).
1 Генерация биомов в мире (часть 1)
1.1 GenLayer
Биомы генерируются с помощью различных слоев GenLayer – подклассов, которые генерируют массив псевдослучайных чисел в зависимости от их родителя. Исключениями являются слой GenLayerIsland, который генерирует массив не имея родителя, и GenLayerHills, который имеет два родителя. Метод 1.1 GenLayer
GenLayer.getInts(…)
возвращает массив целых чисел, сгенерированных слоем. Они могут быть интерпретированы как океаны или поверхности, температуры или осадки, идентификаторы биомов или же просто как какие-то определенные значения, которые пригодятся в будущем. Это зависит от конкретного подкласса GenLayer.GenlayerIsland – возвращает массив нулей и единиц. Пока в массиве нет идентификаторов биомов можно считать, что: 0 – это океан, 1 – теплый биом (но до распределения по типам биомов данная цифра обозначает поверхность), 2 – умеренный биом, 3 – холодный биом, 4 – снежный биом.
Пример генерации в виде массива и в виде картинки.
GenLayerFuzzyZoom – увеличивает масштаб области.
GenLayerZoom – увеличивает масштаб области, но на границах различающихся чисел предпочтение отдается большинству.
GenLayerAddIsland – рядом с поверхностью и океаном добавляет дополнительную поверхность. Всегда копирует снежные биомы и с высоким шансом копирует умеренный и холодный биомы.
Пример использования данного слоя три раза подряд.
GenLayerRemoveTooMuchOcean – с невысоким шансом генерирует поверхность на месте океана при условии, что поблизости нет другой поверхности.
GenLayerAddSnow – с малым шансом генерирует рядом с океаном холодные и снежные биомы.
GenLayerEdge – генерирует на поверхности умеренные, холодные и снежные биомы в зависимости от мода слоя GenLayerEdge.Mode:
- COOL_WARM – генерирует умеренные биомы рядом с холодными или снежными биомами;
- HEAT_ICE – генерирует холодные биомы рядом с умеренными биомами или поверхностью;
- SPECIAL – с маленьким шансом добавляет гарантированную генерацию определенного биома вместо условных биомов.
- Теплый биом: MESACLEARROCK (Столовые горы Плато) или MESA_ROCK (Столовые горы Плато Л);
- Умеренный биом: JUNGLE (Джунгли);
- Холодный биом: REDWOOD_TAIGA (Мегатайга);
- Снежный биом: нет гарантированной генерации.
GenLayerAddMushroomIsland – с маленьким шансом добавляет MUSHROOM_ISLAND (Грибной остров) при условии, что рядом нет поверхности. В данном слое цифры все еще обозначают условные биомы и океан, но при этом в массиве уже может содержаться идентификатор грибного острова.
GenLayerDeepOcean – преобразует океан в глубокий океан. Также, как и в предыдущем слое, возвращает массив, содержащий одновременно и условные биомы, и идентификаторы биомов.
GenLayerBiome – заменяет все условные биомы на идентификаторы биомов. Биомы выбираются с учетом своего «веса». Чем он больше, тем выше шанс генерации именно этого биома. Если в массиве присутствуют гарантированные биомы, то они в любом случае сгенерируются какой бы «вес» не имели остальные биомы.
Пример с гарантированной генерацией теплых биомов.
GenLayerBiomeEdge – изменяет биомы в зависимости от температуры соседних биомов, а также создает границу вокруг некоторых биомов.
- EXTREME_HILLS (Горы) изменяется на EXTREME_HILLS_EDGE (Окраина гор), если рядом есть биомы, не подходящие по температуре;
- MESA_ROCK (Столовые горы Плато Л) и MESA_CLEAR_ROCK (Столовые горы Плато) изменяются на границе на MESA (Столовые горы);
- REDWOOD_TAIGA (Мегатайга) изменяется на границе на TAIGA (Тайга);
- DESERT (Пустыня) изменяется на EXTREME_HILLS_WITH_TREES (Горы+), если рядом есть ICE_PLAINS (Тундра);
- SWAMPLAND(Болото) изменяется на:
- PLAINS (Равнина), если рядом есть DESERT (Пустыня), COLD_TAIGA (Холодная тайга) или ICE_PLAINS (Тундра);
- JUNGLE_EDGE (Окраина джунглей), если рядом нет DESERT (Пустыня), COLD_TAIGA (Холодная тайга) и ICE_PLAINS (Тундра), но есть JUNGLE (Джунгли).
GenLayerHills – изменяет биомы, с невысоким шансом заменяя их в основном горными аналогами, а также с низким шансом изменяет биом на редкую вариацию этого биома.
GenLayerRareBiome – с низким шансом изменяет PLAINS (Равнина) на MUTATED_PLAINS (Подсолнечное поле).
GenLayerShore – создает береговую линию или границу, но не такую большую, как с помощью GenLayerBiomeEdge:
- MUSHROOM_ISLAND (Грибной остров) изменяется на MUSHROOM_ISLAND_SHORE (Берег грибного острова), если рядом есть океан;
- Любой биом класса
BiomeJungle.class
(включая дочерние) изменяется на:- JUNGLE_EDGE(Окраина джунглей), если рядом есть другой биом, кроме:
- Биомов класса
BiomeJungle.class
; - Биомов FOREST (Лес) и TAIGA (Тайга);
- Океанов;
- Биомов класса
- BEACH (Пляж), если выполнены предыдущие два условия и рядом есть океан;
- JUNGLE_EDGE(Окраина джунглей), если рядом есть другой биом, кроме:
- EXTREME_HILLS (Горы), EXTREME_HILLS_WITH_TREES (Горы+) и EXTREME_HILLS_EDGE (Окраина гор) изменяется на STONE_BEACH (Каменный пляж), если рядом есть океан;
- Снежные биомы (Biome.enableSnow = true) изменяются на COLD_BEACH (Холодный пляж), если рядом есть океан;
- MESA (Столовые горы) и MESA_ROCK (Столовые горы Плато Л) изменяются на DESERT (Пустыня), если рядом есть отличный от Столовых гор биом, не являющийся океаном (это не береговая линия);
- Оставшиеся биомы кроме SWAMPLAND изменяет на BEACH (Пляж), если рядом есть океан.
GenLayerSmooth – сглаживает массив биомов.
GenLayerRiverInit – заполняет массив псевдослучайными числами.
GenLayerRiver – создает на границах различающихся цифр реку.
GenLayerRiverMix – накладывает массив с рекой поверх массива с биомами с некоторым ограничениями:
- Река не накладывается на OCEAN (Океан) и DEEP_OCEAN (Глубокий океан);
- Если река накладывается на ICEPLAINS (Тундра), то биом заменяется на FROZERRIVER (Замерзшая река);
- Если река накладывается на MUSHROOM_ISLAND (Грибной остров), то биом заменяется на MUSHROOM_ISLAND_SHORE (Берег грибного острова).
GenLayerVoronoiZoom – растягивает весь массив, делая его больше (не изменяя масштаба) и заполняя пустые ячейки.
\begin{bmatrix}7&7&37&37&37&37\\7&7&37&37&37&37\\37&7&7&2&37&37\\2&7&7&2&2&2\\2&7&7&2&2&2\\2&2&7&7&2&2\\\end{bmatrix}\begin{bmatrix}7&7&7&7&7&37&37&37&37&37&37&37&37&37&37&37\\7&7&7&7&7&37&37&37&37&37&37&37&37&37&37&37\\7&7&7&7&37&37&37&37&37&37&37&37&37&37&37&37\\37&7&7&7&37&37&37&37&2&2&2&37&37&37&37&37\\37&7&7&7&7&37&37&2&2&2&2&2&2&37&37&37\\37&7&7&7&7&7&7&2&2&2&2&2&2&37&37&37\\37&37&7&7&7&7&7&7&2&2&2&2&37&37&37&37\\7&7&7&7&7&7&7&7&2&2&2&2&37&37&37&37\\7&7&7&7&7&7&7&7&2&2&2&2&2&2&37&37\\7&7&7&7&7&7&7&7&2&2&2&2&2&2&2&2\\7&7&7&7&7&7&7&7&7&2&2&2&2&2&2&2\\7&7&7&7&7&7&7&7&7&7&2&2&2&2&2&2\\7&7&7&7&7&7&7&7&2&2&2&2&2&2&2&2\\7&7&7&7&7&7&7&7&2&2&2&2&2&2&2&2\\7&7&7&7&7&7&7&7&7&2&2&2&2&2&2&2\\7&7&7&2&2&7&7&7&7&7&2&2&2&2&2&2\\\end{bmatrix}
1.2 Алгоритм генерации
Алгоритм, по которому происходит генерация биомов в Обычном мире, можно посмотреть в методе GenLayer.initializeAllBiomeGenerators(…)
:
Java:
public static GenLayer[] initializeAllBiomeGenerators(long seed, WorldType worldType, ChunkGeneratorSettings settings)
{
GenLayer genlayer = new GenLayerIsland(1L);
genlayer = new GenLayerFuzzyZoom(2000L, genlayer);
GenLayer genlayeraddisland = new GenLayerAddIsland(1L, genlayer);
GenLayer genlayerzoom = new GenLayerZoom(2001L, genlayeraddisland);
GenLayer genlayeraddisland1 = new GenLayerAddIsland(2L, genlayerzoom);
genlayeraddisland1 = new GenLayerAddIsland(50L, genlayeraddisland1);
genlayeraddisland1 = new GenLayerAddIsland(70L, genlayeraddisland1);
GenLayer genlayerremovetoomuchocean = new GenLayerRemoveTooMuchOcean(2L, genlayeraddisland1);
GenLayer genlayeraddsnow = new GenLayerAddSnow(2L, genlayerremovetoomuchocean);
GenLayer genlayeraddisland2 = new GenLayerAddIsland(3L, genlayeraddsnow);
GenLayer genlayeredge = new GenLayerEdge(2L, genlayeraddisland2, GenLayerEdge.Mode.COOL_WARM);
genlayeredge = new GenLayerEdge(2L, genlayeredge, GenLayerEdge.Mode.HEAT_ICE);
genlayeredge = new GenLayerEdge(3L, genlayeredge, GenLayerEdge.Mode.SPECIAL);
GenLayer genlayerzoom1 = new GenLayerZoom(2002L, genlayeredge);
genlayerzoom1 = new GenLayerZoom(2003L, genlayerzoom1);
GenLayer genlayeraddisland3 = new GenLayerAddIsland(4L, genlayerzoom1);
GenLayer genlayeraddmushroomisland = new GenLayerAddMushroomIsland(5L, genlayeraddisland3);
GenLayer genlayerdeepocean = new GenLayerDeepOcean(4L, genlayeraddmushroomisland);
GenLayer genlayer4 = GenLayerZoom.magnify(1000L, genlayerdeepocean, 0);
int i = 4;
int j = i;
if (settings != null)
{
i = settings.biomeSize;
j = settings.riverSize;
}
if (worldType == WorldType.LARGE_BIOMES)
{
i = 6;
}
i = getModdedBiomeSize(worldType, i);
GenLayer lvt_7_1_ = GenLayerZoom.magnify(1000L, genlayer4, 0);
GenLayer genlayerriverinit = new GenLayerRiverInit(100L, lvt_7_1_);
GenLayer genlayerbiomeedge = worldType.getBiomeLayer(seed, genlayer4, settings);
GenLayer lvt_9_1_ = GenLayerZoom.magnify(1000L, genlayerriverinit, 2);
GenLayer genlayerhills = new GenLayerHills(1000L, genlayerbiomeedge, lvt_9_1_);
GenLayer genlayer5 = GenLayerZoom.magnify(1000L, genlayerriverinit, 2);
genlayer5 = GenLayerZoom.magnify(1000L, genlayer5, j);
GenLayer genlayerriver = new GenLayerRiver(1L, genlayer5);
GenLayer genlayersmooth = new GenLayerSmooth(1000L, genlayerriver);
genlayerhills = new GenLayerRareBiome(1001L, genlayerhills);
for (int k = 0; k < i; ++k)
{
genlayerhills = new GenLayerZoom((long)(1000 + k), genlayerhills);
if (k == 0)
{
genlayerhills = new GenLayerAddIsland(3L, genlayerhills);
}
if (k == 1 || i == 1)
{
genlayerhills = new GenLayerShore(1000L, genlayerhills);
}
}
GenLayer genlayersmooth1 = new GenLayerSmooth(1000L, genlayerhills);
GenLayer genlayerrivermix = new GenLayerRiverMix(100L, genlayersmooth1, genlayersmooth);
GenLayer genlayer3 = new GenLayerVoronoiZoom(10L, genlayerrivermix);
genlayerrivermix.initWorldGenSeed(seed);
genlayer3.initWorldGenSeed(seed);
return new GenLayer[] {genlayerrivermix, genlayer3, genlayerrivermix};
}
GenLayerVoronoiZoom является последним слоем в алгоритме и возвращает именно тот самый одномерный массив, в котором записаны идентификаторы всех биомов в чанке. Данный слой, как и почти все остальные слои, генерирует свой массив данных, используя массив данных своего родителя. Иначе говоря, чтобы возвратить нам массив длиной 16 * 16, состоящий из идентификаторов биомов в данном чанке, нам необходимо сначала определить массив данных слоя GenLayerRiverMix (массив биомов/рек размером 6 * 6), т.к. по стандартному алгоритму этот слой является родителем слоя GenLayerVoronoiZoom. А чтобы GenLayerRiverMix смог определить свой массив данных, ему необходимы массивы данных GenLayerHills (массив биомов размером 6 * 6) и GenLayerRiver (массив рек размером 6 * 6). И так каждый слой зависит от слоя своего родителя вплоть до GenLayerIsland, который ни от кого не зависит.
Метод
GenLayer.getInts(...)
принимает на вход четыре параметра:- areaX и areaY - это смещения по x и z. GenLayerVoronoiZoom принимает значения координат из мира, но эти смещения всегда будут изменяться. Если мы получим массив данных из GenLayerVoronoiZoom, а потом сдвинемся на 1 блок и снова получим массив данных, то нетрудно понять, что во втором массиве все биомы сдвинутся тоже на 1 блок. Но чтобы добиться изменения смещения, к примеру, в самом первом слое GenLayerIsland на 1, нужно пройти куда больше. Это объясняется тем, что изначально чанки представляются в меньших масштабах для более быстрой генерации, после чего много раз масштабируются слоями до необходимых размеров;
- areaWidth - длина, которая отсчитывается по оси, аналогичной оси z в мире;
- areaHeight - длина, которая отсчитывается по оси, аналогичной оси x в мире.
areaWidth * areaHeight
.Информация записывается в массив по формуле
aint[j + i * areaWidth]
, где i и j представляют из себя координаты x и z в данном массиве. В качестве примера разберем метод слоя GenLayerRemoveTooMuchOcean:
Java:
public int[] getInts(int areaX, int areaY, int areaWidth, int areaHeight) {
int i = areaX - 1;
int j = areaY - 1;
int k = areaWidth + 2;
int l = areaHeight + 2;
int[] aint = this.parent.getInts(i, j, k, l);
int[] aint1 = IntCache.getIntCache(areaWidth * areaHeight);
for (int i1 = 0; i1 < areaHeight; ++i1) {
for (int j1 = 0; j1 < areaWidth; ++j1) {
int k1 = aint[j1 + 1 + (i1 + 1 - 1) * (areaWidth + 2)];
int l1 = aint[j1 + 1 + 1 + (i1 + 1) * (areaWidth + 2)];
int i2 = aint[j1 + 1 - 1 + (i1 + 1) * (areaWidth + 2)];
int j2 = aint[j1 + 1 + (i1 + 1 + 1) * (areaWidth + 2)];
int k2 = aint[j1 + 1 + (i1 + 1) * k];
aint1[j1 + i1 * areaWidth] = k2;
this.initChunkSeed((long) (j1 + areaX), (long) (i1 + areaY));
if (k2 == 0 && k1 == 0 && l1 == 0 && i2 == 0 && j2 == 0 && this.nextInt(2) == 0) {
aint1[j1 + i1 * areaWidth] = 1;
}
}
}
return aint1;
}
aint1
необходимо определить массив данных нашего родителя aint
. Размер массива родительского слоя увеличивается на 1 во все стороны.
Java:
int i = areaX - 1;
int j = areaY - 1;
int k = areaWidth + 2;
int l = areaHeight + 2;
int[] aint = this.parent.getInts(i, j, k, l);
Java:
int[] aint1 = IntCache.getIntCache(areaWidth * areaHeight);
Java:
for (int i1 = 0; i1 < areaHeight; ++i1) {
for (int j1 = 0; j1 < areaWidth; ++j1) {
}
}
Java:
int k1 = aint[j1 + 1 + (i1 + 1 - 1) * (areaWidth + 2)];
int l1 = aint[j1 + 1 + 1 + (i1 + 1) * (areaWidth + 2)];
int i2 = aint[j1 + 1 - 1 + (i1 + 1) * (areaWidth + 2)];
int j2 = aint[j1 + 1 + (i1 + 1 + 1) * (areaWidth + 2)];
int k2 = aint[j1 + 1 + (i1 + 1) * k];
Возьмем точку
k2
. Родительский массив во все стороны был увеличен на 1 по сравнению с нашим массивом, поэтому можно сразу догадаться, что если (i1; j1)
- координаты точки в нашем массиве aint1
, то (i1 + 1; j1 + 1)
будут координатами той же самой точки, но в массиве aint
, а k
- длина по оси z массива aint
. Остальные же точки являются "соседями" нашей точки в родительском массиве (их координаты отличаются на +/-1).
Java:
int k1 = aint[j1 + 1 + (i1 + 0) * k];
int l1 = aint[j1 + 2 + (i1 + 1) * k];
int i2 = aint[j1 + 0 + (i1 + 1) * k];
int j2 = aint[j1 + 1 + (i1 + 2) * k];
int k2 = aint[j1 + 1 + (i1 + 1) * k]; // Это наша точка
Java:
aint1[j1 + i1 * areaWidth] = k2;
Java:
this.initChunkSeed((long) (j1 + areaX), (long) (i1 + areaY));
Java:
if (k2 == 0 && k1 == 0 && l1 == 0 && i2 == 0 && j2 == 0 && this.nextInt(2) == 0) {
aint1[j1 + i1 * areaWidth] = 1;
}
1.3 BiomeManager
При генерации биома важную роль играет то, к каким типам он относится. Если биом не имеет ни одного типа, то он не сможет сгенерироваться в Обычном мире с помощью GenLayerBiome. В BiomeManager.BiomeType определены 4 типа биомов:- DESERT- теплый (Пустыня, Саванна, Равнина);
- WARM - умеренный (Лес, Темный лес, Горы, Равнина, Березовый лес, Болото);
- COOL - холодный (Лес, Горы, Тайга, Равнина);
- ICY - снежный (Тундра, Холодная тайга).
1.4 BiomeDictionary
BiomeDictionary упрощает работу с биомами и позволяет взаимодействовать с биомами из других модов.По умолчанию всем биомам, зарегистрированным в BiomeDictionary, присваиваются типы на основе их свойств (температура, растительность, влажность и т.д.). Если вы регистрируете собственный биом, не указав ни одного типа, то биому будут самостоятельно присвоены типы на основе его свойств. Алгоритм можно посмотреть в методе
BiomeDictionary.makeBestGuess(…)
. Перечень всех стандартных типов можно посмотреть в BiomeDictionary.Type.Также можно добавить собственный тип, определив его с помощью метода
BiomeDictionary.Type.getType(…)
. Использование одного и того же типа в разных модах позволит взаимодействовать с чужими биомами и использовать, например, собственные генераторы на территории всех биомов с данным типом.2 Собственный биом
2.1 Регистрация биома
Добавим небольшой интерфейс IBiome, который потребуется для регистрации биомов.2.1 Регистрация биома
Java:
package ddooss.util.interfaces;
public interface IBiome {
/**
* Регистрация биома происходит в этом методе
*/
public void registerBiome();
}
BiomeInit.initBiomes()
вызывать регистрацию.
Java:
package ddooss.init;
import java.util.ArrayList;
import java.util.List;
import ddooss.util.enums.EBiome;
import ddooss.util.interfaces.IBiome;
import ddooss.world.biome.BiomeLapis;
import net.minecraft.world.biome.Biome;
import net.minecraftforge.common.BiomeDictionary;
public class BiomeInit {
/**
* Список наших биомов
*/
public static final List<Biome> BIOMES = new ArrayList<Biome>();
/**
* Метод, в котором вызываем регистрацию всех биомов из BIOMES
*/
public static void initBiomes() {
for (Biome biome : BIOMES) {
if (biome instanceof IBiome) {
((IBiome) biome).registerBiome();
}
}
}
}
BiomeInit.initBiomes()
будем вызывать на стадии преинициализации мода.
Java:
public void preInit(FMLPreInitializationEvent event) {
BiomeInit.initBiomes();
}
Java:
package ddooss.util.enums;
import ddooss.init.BiomeInit;
import net.minecraftforge.common.BiomeDictionary;
import net.minecraftforge.common.BiomeManager;
public enum EBiome {
/**
* Окраина лазуритовой земли, Лазуритовая долина
*/
LAPIS(BiomeInit.LAPIS),
/**
* Лазуритовая земля
*/
LAPISLAND(BiomeManager.BiomeType.WARM, 10, BiomeInit.LAPIS);
private final BiomeManager.BiomeType biomeType;
private final int weight;
private final BiomeDictionary.Type[] types;
EBiome(BiomeDictionary.Type... types) {
this(null, 0, types);
}
EBiome(BiomeManager.BiomeType biomeType, int weight, BiomeDictionary.Type... types) {
this.biomeType = biomeType;
this.weight = weight;
this.types = types;
}
public BiomeManager.BiomeType getBiomeType() {
return this.biomeType;
}
public int getWeight() {
return this.weight;
}
public BiomeDictionary.Type[] getTypes() {
return this.types;
}
}
Java:
package ddooss.world.biome;
import ddooss.init.BiomeInit;
import ddooss.lib.Reference;
import ddooss.util.enums.EBiome;
import ddooss.util.interfaces.IBiome;
import net.minecraft.world.biome.Biome;
import net.minecraftforge.common.BiomeDictionary;
import net.minecraftforge.common.BiomeManager;
import net.minecraftforge.common.BiomeManager.BiomeEntry;
import net.minecraftforge.fml.common.registry.ForgeRegistries;
public abstract class BiomeBase extends Biome implements IBiome {
// Тип BiomeType, определяющий генерацию биома
private final BiomeManager.BiomeType biomeType;
// Вес биома
private final int weight;
// Типы биома
private final BiomeDictionary.Type[] types;
public BiomeBase(String registryName, Biome.BiomeProperties properties, EBiome eBiome) {
super(properties);
this.setRegistryName(Reference.MODID, registryName);
this.biomeType = eBiome.getBiomeType();
this.weight = eBiome.getWeight();
this.types = eBiome.getTypes();
BiomeInit.BIOMES.add(this);
}
@Override
public void registerBiome() {
ForgeRegistries.BIOMES.register(this);
BiomeDictionary.addTypes(this, this.types);
if (this.weight > 0) {
BiomeManager.addBiome(biomeType, new BiomeEntry(this, this.weight));
BiomeManager.addSpawnBiome(this);
}
}
}
- NORMAL – равнина с изредка встречающимися деревьями;
- EDGE – граница, представляющая из себя небольшие горы;
- MUTATED – равнина с большим количеством деревьев.
Java:
package ddooss.world.biome;
import java.util.Random;
import ddooss.util.enums.EBiome;
import net.minecraft.init.Blocks;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.gen.feature.WorldGenAbstractTree;
public class BiomeLapis extends BiomeBase {
private final LapisType type;
public BiomeLapis(String registryName, Biome.BiomeProperties properties, EBiome eBiome, LapisType type) {
super(registryName, properties, eBiome);
this.type = type;
if (this.type == LapisType.EDGE) {
this.decorator.extraTreeChance = 0F;
this.topBlock = Blocks.STONE.getDefaultState();
this.fillerBlock = Blocks.STONE.getDefaultState();
} else if (this.type == LapisType.MUTATED) {
this.decorator.treesPerChunk = 3;
}
}
@Override
public WorldGenAbstractTree getRandomTreeFeature(Random rand) {
return this.type == LapisType.MUTATED ? BIG_TREE_FEATURE : TREE_FEATURE;
}
@Override
public int getGrassColorAtPos(BlockPos pos) {
return this.type == LapisType.EDGE ? super.getGrassColorAtPos(pos) : 0x00ffff;
}
@Override
public int getFoliageColorAtPos(BlockPos pos) {
return this.type == LapisType.EDGE ? super.getFoliageColorAtPos(pos) : 0x00ffff;
}
public static enum LapisType {
NORMAL, EDGE, MUTATED;
}
}
Java:
public static final Biome LAPISLAND = new BiomeLapis("lapisalnd", new Biome.BiomeProperties("Lapisland").setBaseHeight(-0.1F).setHeightVariation(0.1F).setTemperature(0.8F).setRainfall(0.7F), EBiome.LAPISLAND, BiomeLapis.LapisType.NORMAL);
public static final Biome LAPISLAND_EDGE = new BiomeLapis("lapisalnd_edge", new Biome.BiomeProperties("Lapisland Edge").setBaseHeight(0.9F).setHeightVariation(0.1F).setTemperature(0.8F).setRainfall(0.7F), EBiome.LAPIS, BiomeLapis.LapisType.EDGE);
public static final Biome LAPISVALLEY = new BiomeLapis("lapisvalley", new Biome.BiomeProperties("Lapisvalley").setBaseBiome("lapisalnd").setBaseHeight(-0.1F).setHeightVariation(0.1F).setTemperature(0.8F).setRainfall(0.7F), EBiome.LAPIS, BiomeLapis.LapisType.MUTATED);
2.2 Использование собственных GenLayer
На данный момент в Forge нет инструментов, которые могли бы изменять какой-то конкретный слой GenLayer, поэтому придется использовать собственный алгоритм генерации биомов.Добавим класс TerrainHandler и событие InitBiomeGens, в котором укажем свой алгоритм генерации биомов. Данное событие не поддерживает возможность использовать настройки из расширенного типа мира.
Java:
package ddooss.handler;
import ddooss.world.gen.layer.GenLayerCustom;
import net.minecraft.world.gen.layer.GenLayer;
import net.minecraftforge.event.terraingen.WorldTypeEvent.InitBiomeGens;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
public class TerrainHandler {
@SubscribeEvent
public void initializeAllBiomeGenerators(InitBiomeGens event) {
GenLayer[] genlayer = GenLayerCustom.initializeAllBiomeGenerators(event.getSeed(), event.getWorldType());
event.setNewBiomeGens(genlayer);
}
}
Java:
public void preInit(FMLPreInitializationEvent event) {
BiomeInit.initBiomes();
MinecraftForge.TERRAIN_GEN_BUS.register(new TerrainHandler());
}
Абстрактный класс GenLayerCustom:
Java:
package ddooss.world.gen.layer;
import net.minecraft.world.WorldType;
import net.minecraft.world.gen.layer.GenLayer;
import net.minecraft.world.gen.layer.GenLayerAddIsland;
import net.minecraft.world.gen.layer.GenLayerAddMushroomIsland;
import net.minecraft.world.gen.layer.GenLayerAddSnow;
import net.minecraft.world.gen.layer.GenLayerBiome;
import net.minecraft.world.gen.layer.GenLayerBiomeEdge;
import net.minecraft.world.gen.layer.GenLayerDeepOcean;
import net.minecraft.world.gen.layer.GenLayerEdge;
import net.minecraft.world.gen.layer.GenLayerFuzzyZoom;
import net.minecraft.world.gen.layer.GenLayerHills;
import net.minecraft.world.gen.layer.GenLayerIsland;
import net.minecraft.world.gen.layer.GenLayerRareBiome;
import net.minecraft.world.gen.layer.GenLayerRemoveTooMuchOcean;
import net.minecraft.world.gen.layer.GenLayerRiver;
import net.minecraft.world.gen.layer.GenLayerRiverInit;
import net.minecraft.world.gen.layer.GenLayerSmooth;
import net.minecraft.world.gen.layer.GenLayerVoronoiZoom;
import net.minecraft.world.gen.layer.GenLayerZoom;
public abstract class GenLayerCustom extends GenLayer {
public GenLayerCustom(long seed) {
super(seed);
}
public static GenLayer[] initializeAllBiomeGenerators(long seed, WorldType worldType) {
GenLayer genlayer = new GenLayerIsland(1L);
genlayer = new GenLayerFuzzyZoom(2000L, genlayer);
GenLayer genlayeraddisland = new GenLayerAddIsland(1L, genlayer);
GenLayer genlayerzoom = new GenLayerZoom(2001L, genlayeraddisland);
GenLayer genlayeraddisland1 = new GenLayerAddIsland(2L, genlayerzoom);
genlayeraddisland1 = new GenLayerAddIsland(50L, genlayeraddisland1);
genlayeraddisland1 = new GenLayerAddIsland(70L, genlayeraddisland1);
GenLayer genlayerremovetoomuchocean = new GenLayerRemoveTooMuchOcean(2L, genlayeraddisland1);
GenLayer genlayeraddsnow = new GenLayerAddSnow(2L, genlayerremovetoomuchocean);
GenLayer genlayeraddisland2 = new GenLayerAddIsland(3L, genlayeraddsnow);
GenLayer genlayeredge = new GenLayerEdge(2L, genlayeraddisland2, GenLayerEdge.Mode.COOL_WARM);
genlayeredge = new GenLayerEdge(2L, genlayeredge, GenLayerEdge.Mode.HEAT_ICE);
genlayeredge = new GenLayerEdge(3L, genlayeredge, GenLayerEdge.Mode.SPECIAL);
GenLayer genlayerzoom1 = new GenLayerZoom(2002L, genlayeredge);
genlayerzoom1 = new GenLayerZoom(2003L, genlayerzoom1);
GenLayer genlayeraddisland3 = new GenLayerAddIsland(4L, genlayerzoom1);
GenLayer genlayeraddmushroomisland = new GenLayerAddMushroomIsland(5L, genlayeraddisland3);
GenLayer genlayerdeepocean = new GenLayerDeepOcean(4L, genlayeraddmushroomisland);
GenLayer genlayer4 = GenLayerZoom.magnify(1000L, genlayerdeepocean, 0);
int biomeSize = 4;
int riverSize = biomeSize;
if (worldType == WorldType.LARGE_BIOMES) {
biomeSize = 6;
}
biomeSize = GenLayer.getModdedBiomeSize(worldType, biomeSize);
GenLayer lvt_7_1_ = GenLayerZoom.magnify(1000L, genlayer4, 0);
GenLayer genlayerriverinit = new GenLayerRiverInit(100L, lvt_7_1_);
GenLayer genlayerbiome = new GenLayerBiome(200L, genlayer4, worldType, null);
GenLayer ret = GenLayerZoom.magnify(1000L, genlayerbiome, 2);
GenLayer genlayerbiomeedge = new GenLayerBiomeEdge(1000L, ret);
GenLayer lvt_9_1_ = GenLayerZoom.magnify(1000L, genlayerriverinit, 2);
GenLayer genlayerhills = new GenLayerHills(1000L, genlayerbiomeedge, lvt_9_1_);
GenLayer genlayer5 = GenLayerZoom.magnify(1000L, genlayerriverinit, 2);
genlayer5 = GenLayerZoom.magnify(1000L, genlayer5, riverSize);
GenLayer genlayerriver = new GenLayerRiver(1L, genlayer5);
GenLayer genlayersmooth = new GenLayerSmooth(1000L, genlayerriver);
genlayerhills = new GenLayerRareBiome(1001L, genlayerhills);
for (int k = 0; k < biomeSize; k++) {
genlayerhills = new GenLayerZoom((long) (1000 + k), genlayerhills);
if (k == 0) {
genlayerhills = new GenLayerAddIsland(3L, genlayerhills);
}
if (k == 1 || biomeSize == 1) {
genlayerhills = new GenLayerShoreCustom(1000L, genlayerhills); // Новый генератор береговых линий и небольших границ
}
}
GenLayer genlayersmooth1 = new GenLayerSmooth(1000L, genlayerhills);
GenLayer genlayerrivermix = new GenLayerRiverMixCustom(100L, genlayersmooth1, genlayersmooth); // Новый генератор рек
GenLayer genlayer3 = new GenLayerVoronoiZoom(10L, genlayerrivermix);
genlayerrivermix.initWorldGenSeed(seed);
genlayer3.initWorldGenSeed(seed);
return new GenLayer[] { genlayerrivermix, genlayer3, genlayerrivermix };
}
}
Java:
if (k != Biome.getIdForBiome(Biomes.OCEAN) && k != Biome.getIdForBiome(Biomes.DEEP_OCEAN) && k != Biome.getIdForBiome(Biomes.RIVER) && k != Biome.getIdForBiome(Biomes.SWAMPLAND)) {
int l1 = aint[j + 1 + (i + 0) * (areaWidth + 2)];
int k2 = aint[j + 2 + (i + 1) * (areaWidth + 2)];
int j3 = aint[j + 0 + (i + 1) * (areaWidth + 2)];
int i4 = aint[j + 1 + (i + 2) * (areaWidth + 2)];
if (!isBiomeOceanic(l1) && !isBiomeOceanic(k2) && !isBiomeOceanic(j3) && !isBiomeOceanic(i4)) {
aint1[j + i * areaWidth] = k;
} else {
aint1[j + i * areaWidth] = Biome.getIdForBiome(Biomes.BEACH);
}
} else {
aint1[j + i * areaWidth] = k;
}
Java:
if (k != Biome.getIdForBiome(Biomes.OCEAN) && k != Biome.getIdForBiome(Biomes.DEEP_OCEAN) && k != Biome.getIdForBiome(Biomes.RIVER) && k != Biome.getIdForBiome(Biomes.SWAMPLAND)) {
if (!BiomeDictionary.hasType(Biome.getBiomeForId(k), BiomeInit.LAPIS)) {
int l1 = aint[j + 1 + (i + 0) * (areaWidth + 2)];
int k2 = aint[j + 2 + (i + 1) * (areaWidth + 2)];
int j3 = aint[j + 0 + (i + 1) * (areaWidth + 2)];
int i4 = aint[j + 1 + (i + 2) * (areaWidth + 2)];
if (!isBiomeOceanic(l1) && !isBiomeOceanic(k2) && !isBiomeOceanic(j3) && !isBiomeOceanic(i4)) {
aint1[j + i * areaWidth] = k;
} else {
aint1[j + i * areaWidth] = Biome.getIdForBiome(Biomes.BEACH);
}
} else { // Данный код выполняется для наших биомов, которым нужна граница
int l1 = aint[j + 1 + (i + 0) * (areaWidth + 2)];
int i1 = aint[j + 2 + (i + 1) * (areaWidth + 2)];
int j1 = aint[j + 0 + (i + 1) * (areaWidth + 2)];
int k1 = aint[j + 1 + (i + 2) * (areaWidth + 2)];
if (isLapis(l1) && isLapis(i1) && isLapis(j1) && isLapis(k1)) {
aint1[j + i * areaWidth] = k;
} else {
aint1[j + i * areaWidth] = Biome.getIdForBiome(BiomeInit.LAPISLAND_EDGE);
}
}
} else {
aint1[j + i * areaWidth] = k;
}
Java:
/**
* Возвращает true, если биом наследуется от BiomeLapis
*/
private boolean isLapis(int biomeIDA) {
return Biome.getBiome(biomeIDA) instanceof BiomeLapis;
}
Java:
package ddooss.world.gen.layer;
import ddooss.init.BiomeInit;
import ddooss.world.biome.BiomeLapis;
import net.minecraft.init.Biomes;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.biome.BiomeJungle;
import net.minecraft.world.biome.BiomeMesa;
import net.minecraft.world.gen.layer.GenLayer;
import net.minecraft.world.gen.layer.IntCache;
public class GenLayerShoreCustom extends GenLayer {
public GenLayerShoreCustom(long seed, GenLayer parent) {
super(seed);
this.parent = parent;
}
public int[] getInts(int areaX, int areaY, int areaWidth, int areaHeight) {
int[] aint = this.parent.getInts(areaX - 1, areaY - 1, areaWidth + 2, areaHeight + 2);
int[] aint1 = IntCache.getIntCache(areaWidth * areaHeight);
for (int i = 0; i < areaHeight; ++i) {
for (int j = 0; j < areaWidth; ++j) {
this.initChunkSeed((long) (j + areaX), (long) (i + areaY));
int k = aint[j + 1 + (i + 1) * (areaWidth + 2)];
Biome biome = Biome.getBiome(k);
if (k == Biome.getIdForBiome(Biomes.MUSHROOM_ISLAND)) {
int j2 = aint[j + 1 + (i + 0) * (areaWidth + 2)];
int i3 = aint[j + 2 + (i + 1) * (areaWidth + 2)];
int l3 = aint[j + 0 + (i + 1) * (areaWidth + 2)];
int k4 = aint[j + 1 + (i + 2) * (areaWidth + 2)];
if (j2 != Biome.getIdForBiome(Biomes.OCEAN) && i3 != Biome.getIdForBiome(Biomes.OCEAN) && l3 != Biome.getIdForBiome(Biomes.OCEAN) && k4 != Biome.getIdForBiome(Biomes.OCEAN)) {
aint1[j + i * areaWidth] = k;
} else {
aint1[j + i * areaWidth] = Biome.getIdForBiome(Biomes.MUSHROOM_ISLAND_SHORE);
}
} else if (biome != null && biome.getBiomeClass() == BiomeJungle.class) {
int i2 = aint[j + 1 + (i + 0) * (areaWidth + 2)];
int l2 = aint[j + 2 + (i + 1) * (areaWidth + 2)];
int k3 = aint[j + 0 + (i + 1) * (areaWidth + 2)];
int j4 = aint[j + 1 + (i + 2) * (areaWidth + 2)];
if (this.isJungleCompatible(i2) && this.isJungleCompatible(l2) && this.isJungleCompatible(k3) && this.isJungleCompatible(j4)) {
if (!isBiomeOceanic(i2) && !isBiomeOceanic(l2) && !isBiomeOceanic(k3) && !isBiomeOceanic(j4)) {
aint1[j + i * areaWidth] = k;
} else {
aint1[j + i * areaWidth] = Biome.getIdForBiome(Biomes.BEACH);
}
} else {
aint1[j + i * areaWidth] = Biome.getIdForBiome(Biomes.JUNGLE_EDGE);
}
} else if (k != Biome.getIdForBiome(Biomes.EXTREME_HILLS) && k != Biome.getIdForBiome(Biomes.EXTREME_HILLS_WITH_TREES) && k != Biome.getIdForBiome(Biomes.EXTREME_HILLS_EDGE)) {
if (biome != null && biome.isSnowyBiome()) {
this.replaceIfNeighborOcean(aint, aint1, j, i, areaWidth, k, Biome.getIdForBiome(Biomes.COLD_BEACH));
} else if (k != Biome.getIdForBiome(Biomes.MESA) && k != Biome.getIdForBiome(Biomes.MESA_ROCK)) {
if (k != Biome.getIdForBiome(Biomes.OCEAN) && k != Biome.getIdForBiome(Biomes.DEEP_OCEAN) && k != Biome.getIdForBiome(Biomes.RIVER) && k != Biome.getIdForBiome(Biomes.SWAMPLAND)) {
if (!BiomeDictionary.hasType(Biome.getBiomeForId(k), BiomeInit.LAPIS)) {
int l1 = aint[j + 1 + (i + 0) * (areaWidth + 2)];
int k2 = aint[j + 2 + (i + 1) * (areaWidth + 2)];
int j3 = aint[j + 0 + (i + 1) * (areaWidth + 2)];
int i4 = aint[j + 1 + (i + 2) * (areaWidth + 2)];
if (!isBiomeOceanic(l1) && !isBiomeOceanic(k2) && !isBiomeOceanic(j3) && !isBiomeOceanic(i4)) {
aint1[j + i * areaWidth] = k;
} else {
aint1[j + i * areaWidth] = Biome.getIdForBiome(Biomes.BEACH);
}
} else { // Данный код выполняется для наших биомов, которым нужна граница
int l1 = aint[j + 1 + (i + 0) * (areaWidth + 2)];
int i1 = aint[j + 2 + (i + 1) * (areaWidth + 2)];
int j1 = aint[j + 0 + (i + 1) * (areaWidth + 2)];
int k1 = aint[j + 1 + (i + 2) * (areaWidth + 2)];
if (isLapis(l1) && isLapis(i1) && isLapis(j1) && isLapis(k1)) {
aint1[j + i * areaWidth] = k;
} else {
aint1[j + i * areaWidth] = Biome.getIdForBiome(BiomeInit.LAPISLAND_EDGE);
}
}
} else {
aint1[j + i * areaWidth] = k;
}
} else {
int l1 = aint[j + 1 + (i + 0) * (areaWidth + 2)];
int i1 = aint[j + 2 + (i + 1) * (areaWidth + 2)];
int j1 = aint[j + 0 + (i + 1) * (areaWidth + 2)];
int k1 = aint[j + 1 + (i + 2) * (areaWidth + 2)];
if (!isBiomeOceanic(l1) && !isBiomeOceanic(i1) && !isBiomeOceanic(j1) && !isBiomeOceanic(k1)) {
if (this.isMesa(l1) && this.isMesa(i1) && this.isMesa(j1) && this.isMesa(k1)) {
aint1[j + i * areaWidth] = k;
} else {
aint1[j + i * areaWidth] = Biome.getIdForBiome(Biomes.DESERT);
}
} else {
aint1[j + i * areaWidth] = k;
}
}
} else {
this.replaceIfNeighborOcean(aint, aint1, j, i, areaWidth, k, Biome.getIdForBiome(Biomes.STONE_BEACH));
}
}
}
return aint1;
}
private void replaceIfNeighborOcean(int[] aint, int[] aint1, int j, int i, int areaWidth, int k, int biomeIDA) {
if (isBiomeOceanic(k)) {
aint1[j + i * areaWidth] = k;
} else {
int i1 = aint[j + 1 + (i + 0) * (areaWidth + 2)];
int j1 = aint[j + 2 + (i + 1) * (areaWidth + 2)];
int k1 = aint[j + 0 + (i + 1) * (areaWidth + 2)];
int l1 = aint[j + 1 + (i + 2) * (areaWidth + 2)];
if (!isBiomeOceanic(i1) && !isBiomeOceanic(j1) && !isBiomeOceanic(k1) && !isBiomeOceanic(l1)) {
aint1[j + i * areaWidth] = k;
} else {
aint1[j + i * areaWidth] = biomeIDA;
}
}
}
private boolean isJungleCompatible(int biomeIDA) {
if (Biome.getBiome(biomeIDA) != null && Biome.getBiome(biomeIDA).getBiomeClass() == BiomeJungle.class) {
return true;
} else {
return biomeIDA == Biome.getIdForBiome(Biomes.JUNGLE_EDGE) || biomeIDA == Biome.getIdForBiome(Biomes.JUNGLE) || biomeIDA == Biome.getIdForBiome(Biomes.JUNGLE_HILLS) || biomeIDA == Biome.getIdForBiome(Biomes.FOREST) || biomeIDA == Biome.getIdForBiome(Biomes.TAIGA) || isBiomeOceanic(biomeIDA);
}
}
private boolean isMesa(int biomeIDA) {
return Biome.getBiome(biomeIDA) instanceof BiomeMesa;
}
/**
* Возвращает true, если биом наследуется от BiomeLapis
*/
private boolean isLapis(int biomeIDA) {
return Biome.getBiome(biomeIDA) instanceof BiomeLapis;
}
}
Java:
if (aint[i] != Biome.getIdForBiome(Biomes.OCEAN) && aint[i] != Biome.getIdForBiome(Biomes.DEEP_OCEAN)) {
}
Java:
if (aint[i] != Biome.getIdForBiome(Biomes.OCEAN) && aint[i] != Biome.getIdForBiome(Biomes.DEEP_OCEAN) && !BiomeDictionary.hasType(Biome.getBiomeForId(aint[i]), BiomeInit.LAPIS)) { // Добавляем проверку для наших биомов
}
Java:
package ddooss.world.gen.layer;
import ddooss.init.BiomeInit;
import net.minecraft.init.Biomes;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.gen.layer.GenLayer;
import net.minecraft.world.gen.layer.IntCache;
import net.minecraftforge.common.BiomeDictionary;
public class GenLayerRiverMixCustom extends GenLayer {
private final GenLayer biomePatternGeneratorChain;
private final GenLayer riverPatternGeneratorChain;
public GenLayerRiverMixCustom(long seed, GenLayer biome, GenLayer river) {
super(seed);
this.biomePatternGeneratorChain = biome;
this.riverPatternGeneratorChain = river;
}
public void initWorldGenSeed(long seed) {
this.biomePatternGeneratorChain.initWorldGenSeed(seed);
this.riverPatternGeneratorChain.initWorldGenSeed(seed);
super.initWorldGenSeed(seed);
}
public int[] getInts(int areaX, int areaY, int areaWidth, int areaHeight) {
int[] aint = this.biomePatternGeneratorChain.getInts(areaX, areaY, areaWidth, areaHeight);
int[] aint1 = this.riverPatternGeneratorChain.getInts(areaX, areaY, areaWidth, areaHeight);
int[] aint2 = IntCache.getIntCache(areaWidth * areaHeight);
for (int i = 0; i < areaWidth * areaHeight; ++i) {
if (aint[i] != Biome.getIdForBiome(Biomes.OCEAN) && aint[i] != Biome.getIdForBiome(Biomes.DEEP_OCEAN) && !BiomeDictionary.hasType(Biome.getBiomeForId(aint[i]), BiomeInit.LAPIS)) { // Добавляем проверку для наших биомов
if (aint1[i] == Biome.getIdForBiome(Biomes.RIVER)) {
if (aint[i] == Biome.getIdForBiome(Biomes.ICE_PLAINS)) {
aint2[i] = Biome.getIdForBiome(Biomes.FROZEN_RIVER);
} else if (aint[i] != Biome.getIdForBiome(Biomes.MUSHROOM_ISLAND) && aint[i] != Biome.getIdForBiome(Biomes.MUSHROOM_ISLAND_SHORE)) {
aint2[i] = aint1[i] & 255;
} else {
aint2[i] = Biome.getIdForBiome(Biomes.MUSHROOM_ISLAND_SHORE);
}
} else {
aint2[i] = aint[i];
}
} else {
aint2[i] = aint[i];
}
}
return aint2;
}
}