API

Generazione tramite codice

Quando dichiarare non basta, il tuo codice costruisce il mondo man mano che i chunk vengono creati: feature, strutture intere, la forma del terreno, quale bioma va dove, grotte e i blocchi del suolo.

Che cosa puoi scrivere

Che cosa Kotlin CraftScript
Feature: spuntoni, piante sui muri, massi, qualsiasi cosa piazzata in un chunk feature("…") { place { } } No
Strutture costruite dal codice, estese su più chunk structure("…") { plan { }; build { } } Sì (plan, build)
Il terreno di una dimensione terrain("minecraft:the_end") { } Sì (terrain)
Quale bioma va dove biomes("minecraft:the_end") { } Sì (biome_map)
Carver: grotte e tunnel carver("…", chance) { } Sì (carver)
Il suolo di un bioma, blocco per blocco biome("…") { surface { } } Sì (surface: fun(at))
Tag di blocchi, oggetti e biomi tag(TagKind.BIOME, "…", …) No

Tutto ciò che è in questa pagina viene eseguito mentre i chunk vengono creati, su più thread contemporaneamente. Usa i numeri casuali che ti vengono dati (o il seed del mondo) invece dei tuoi: così lo stesso seed crea lo stesso mondo. In Kotlin, mantieni sicuro per i thread tutto ciò che il tuo codice condivide tra le chiamate. In CraftScript i membri di random non sono disponibili; prendi la varietà dalle coordinate, dal seed o dal sito della struttura.

Feature

Una feature è qualcosa che il tuo codice costruisce in un chunk in via di creazione, nei biomi che indichi. Crafting sceglie i punti, come fa il gioco per le sue feature; la tua funzione place costruisce lì e restituisce true quando ha costruito qualcosa. Le feature si scrivono in Kotlin.

content/CrystalSpikes.ktKotlin
object CrystalSpikes {
    private val CRYSTAL = Identifier.parse("mymod:crystal")

    fun register(content: ContentBuilder) {
        content.feature("crystal_spike") {
            biome("#minecraft:is_overworld")
            count = 2..5
            heights = -54..30
            on = FeatureOn.FLOOR
            underground = true
            step = FeatureStep.UNDERGROUND
            place { world ->
                val (x, y, z) = Triple(world.x, world.y, world.z)
                if (!world.isIn(x, y - 1, z, "#minecraft:base_stone_overworld")) return@place false
                val height = 2 + world.random.nextInt(4)
                for (dy in 0 until height) {
                    if (!world.isAir(x, y + dy, z)) break
                    world.set(x, y + dy, z, CRYSTAL)
                }
                true
            }
        }
    }
}
Opzione Valori Predefinito
biome(…) biomi in cui viene piazzata, per id o #tag; almeno uno —
count tentativi per chunk, un intervallo: 2..5 1..1
chance solo in un chunk ogni tanti 1
heights ogni tentativo a un’altezza casuale in questo intervallo null: il fondo del mondo (il tuo codice trova il suo posto)
spread true: ogni tentativo in una colonna casuale del chunk; false: nell’angolo del chunk, e il tuo codice sceglie da lì true
on FeatureOn.FLOOR o CEILING: dalla sua altezza, si sposta nell’aria accanto al pavimento o al soffitto più vicino, fino a 32 blocchi di distanza; se non ce ne sono, non viene piazzata null
underground true: solo sotto la superficie, ad almeno 2 blocchi sotto di essa (nelle grotte) false
everyFloor true: su ogni pavimento di ogni colonna scelta, la superficie e il pavimento di ogni grotta; count sono le colonne per pavimento false
tries con everyFloor, quante volte si passano in rassegna i pavimenti: ogni volta count colonne in più su ogni pavimento, così un terreno profondo si raggiunge con più tentativi 1..1
step quando viene creata, tra le fasi che riempiono un chunk: LAKES, SURFACE, ORES, UNDERGROUND (ciò che decora le grotte), VEGETATION VEGETATION

Con everyFloor, chance, heights e spread non vengono usati: i punti sono i pavimenti stessi.

content/GlowTufts.ktKotlin
content.feature("glow_tufts") {
    biome("mymod:crystal_glade")
    everyFloor = true
    count = 4..8
    tries = 1..3
    place { world ->
        val tuft = Identifier.parse("mymod:glow_tuft")
        if (world.structures(world.x, world.z).isNotEmpty()) return@place false
        world.canStay(world.x, world.y, world.z, tuft) && world.set(world.x, world.y, world.z, tuft)
    }
}

Che cosa vede una feature

place riceve il mondo in creazione, visto da dove la feature viene piazzata. Può leggere e scrivere il chunk in creazione e i chunk intorno; ciò che viene scritto fuori da essi viene saltato. In CraftScript (nel build di una struttura) gli stessi membri si scrivono in snake_case.

Membro Che cos’è
x, y, z dove viene piazzata
random i suoi numeri casuali: lo stesso seed crea la stessa feature
seed il seed del mondo, per un rumore che è lo stesso in tutto il mondo
minY, maxY le altezze del mondo; i blocchi vanno da minY fino a subito sotto maxY
block(x, y, z), state(x, y, z, name), isAir(x, y, z) il blocco che c’è lì, il valore di uno dei suoi stati, se è aria
isIn(x, y, z, tag) se il blocco che c’è lì è in un tag ("#minecraft:leaves") o è quel blocco ("minecraft:end_stone")
canStay(x, y, z, block, states) se un blocco, con quegli stati, potrebbe stare lì: una pianta sul suo terreno, un muschio su un muro rivolto lontano da esso. states si può omettere
set(x, y, z, block, states) piazza un blocco, con stati come mapOf("facing" to "north"); false quando è fuori da ciò che si può scrivere, o non è un blocco qui
inside(x, z) se la colonna è una che può leggere e scrivere
surface(x, z) il primo blocco sopra il più alto che non è aria, come era il terreno prima delle feature
top(x, z) lo stesso, ora, con ciò che le feature hanno piazzato da allora
flow(x, y, z) fa scorrere l’acqua (o la lava, o un blocco che contiene acqua) lì quando il chunk è nel mondo. L’acqua piazzata mentre un chunk viene creato resta ferma, e senza questo resterebbe sospesa accanto all’aria
structures(x, z) le strutture (id, come minecraft:end_city) la cui area comprende il chunk di quella colonna, per tenere una feature fuori da esse
placeFile(file, x, y, z, rotation, mirror, pivotX, pivotZ) piazza un file di struttura; vedi File di struttura dal codice

Un blocco che la tua feature nomina e che non esiste (di una mod non installata) viene saltato, con un avviso nel log; set restituisce false per quel blocco.

Strutture costruite dal codice

Per una struttura che non è fatta di file, o che deve seguire il terreno, il tuo codice decide dove si trova e la costruisce. Usa due funzioni:

  • plan viene interpellata, in ogni chunk dove il mondo ne piazzerebbe una (in base a biomes, spacing e separation, come per qualsiasi struttura), per sapere se è lì, prima che i blocchi del chunk vengano creati. Restituisce il suo sito (site): il suo centro, fin dove arriva e ciò che serve alla costruzione; oppure niente, e lì non ce n’è nessuna.
  • build costruisce la parte del sito in ogni chunk che esso copre, un chunk alla volta. Ogni chunk riceve lo stesso sito e gli stessi numeri casuali, così ognuno costruisce il proprio pezzo della stessa struttura. Il sito viene conservato insieme al mondo, quindi i chunk creati dopo un riavvio combaciano ancora.
content/stone_ring.craftCraftScript
structure "stone_ring" {
    biomes: ["#minecraft:is_overworld"]
    spacing: 32
    separation: 12
    plan: fun(p) {
        let x = p.chunk_x * 16 + 8
        let z = p.chunk_z * 16 + 8
        let y = p.height(x, z)
        return { x: x, y: y, z: z, radius: 10, min_y: y - 8, max_y: y + 12 }
    }
    build: fun(world, site) {
        for i in 0..15 {
            let a = i * pi() / 8
            let x = site.x + round(cos(a) * 10)
            let z = site.z + round(sin(a) * 10)
            if world.inside(x, z) {
                let ground = world.surface(x, z)
                for dy in 0..2 { world.set(x, ground + dy, z, "minecraft:mossy_cobblestone") }
            }
        }
    }
}
content/StoneRing.ktKotlin
content.structure("stone_ring") {
    biome("#minecraft:is_overworld")
    spacing = 32
    separation = 12
    plan { p ->
        val x = p.chunkX * 16 + 8
        val z = p.chunkZ * 16 + 8
        val y = p.height(x, z)
        StructureSite(x, y, z, radius = 10, minY = y - 8, maxY = y + 12,
            data = mapOf("tall" to p.random.nextBoolean()))
    }
    build { world, site ->
        val stone = Identifier.parse("minecraft:mossy_cobblestone")
        val top = if (site.data["tall"] == true) 4 else 2
        for (i in 0 until 16) {
            val x = site.x + (cos(i * PI / 8) * 10).roundToInt()
            val z = site.z + (sin(i * PI / 8) * 10).roundToInt()
            if (!world.inside(x, z)) continue
            val ground = world.surface(x, z)
            for (dy in 0..top) world.set(x, ground + dy, z, stone)
        }
    }
}

Il sito (StructureSite in Kotlin, una mappa in CraftScript):

Campo Che cos’è Predefinito in CraftScript
x, y, z il suo centro 0
radius quanti blocchi intorno al centro raggiunge: i chunk che copre 8
min_y, max_y (minY, maxY) le altezze che copre y meno e più radius
data ciò che serve alla costruzione per costruire lo stesso in ogni chunk: numeri, testo, vero o falso vuoto

In Kotlin, un sito ha anche un seed ricavato dalla sua posizione. I numeri in data tornano come decimali dopo un riavvio: leggili con (site.data["n"] as Number).toInt().

Ciò che riceve plan, prima che venga creato qualsiasi blocco del chunk:

Membro Che cos’è
chunkX, chunkZ il chunk per cui viene interpellata (blocco / 16)
seed, random il seed del mondo, e i numeri casuali propri di questo punto
height(x, z) l’altezza che avrà la superficie lì (il primo blocco sopra di essa)
biome(x, y, z) il bioma che c’è lì
column(x, z), minY i blocchi che avrà il terreno in quella colonna, da minY fino alla cima del mondo, prima del suo suolo, delle sue grotte e di qualsiasi costruzione (nell’End: pietra dell’End e aria)
fileSize(file) la dimensione di un file di struttura (x, y, z), o null se non esiste

build riceve lo stesso mondo di una feature, ma può piazzare solo blocchi del chunk in costruzione, e il suo random deriva dal sito. Le altre opzioni di una struttura che valgono qui sono biomes, spacing, separation e, in Kotlin, salt. Una struttura di codice richiede sia plan sia build. Trovala con /locate structure, come qualsiasi altra.

File di struttura dal codice

placeFile piazza un file di struttura dove dice il tuo codice, ruotato e specchiato: una torre dei tuoi file dentro una struttura di codice, o una rovina che una feature lascia in una grotta. Il file è il structures/<percorso>.nbt della mod ("mymod:watchtower") o uno del gioco. Viene piazzata solo la parte che sta dentro ciò che il mondo permette di scrivere, quindi una struttura che copre più chunk piazza il suo file da ciascuno di essi. I blocchi di struttura nel file non vengono piazzati. Restituisce false se quel file non esiste.

content/Watchtower.ktKotlin
val TOWER = Identifier.parse("mymod:watchtower")

content.structure("watchtower") {
    biome("minecraft:plains")
    spacing = 40
    separation = 10
    plan { p ->
        val size = p.fileSize(TOWER) ?: return@plan null
        val x = p.chunkX * 16 + 8
        val z = p.chunkZ * 16 + 8
        val y = p.height(x, z)
        StructureSite(x, y, z, radius = maxOf(size.x, size.z), minY = y, maxY = y + size.y,
            data = mapOf("turns" to p.random.nextInt(4), "half_x" to size.x / 2, "half_z" to size.z / 2))
    }
    build { world, site ->
        val hx = (site.data["half_x"] as Number).toInt()
        val hz = (site.data["half_z"] as Number).toInt()
        // Its middle on the site, turned about its middle.
        world.placeFile(TOWER, site.x - hx, site.y, site.z - hz,
            rotation = (site.data["turns"] as Number).toInt(), pivotX = hx, pivotZ = hz)
    }
}
Parametro Valori Predefinito
file l’id del file —
x, y, z dove va il suo angolo, prima della rotazione —
rotation quarti di giro in senso orario 0
mirror Mirror.NONE, LEFT_RIGHT (lungo z) o FRONT_BACK (lungo x) NONE
pivotX, pivotZ il blocco del file attorno al quale ruota 0, 0

Il terreno di una dimensione

Puoi dare la forma del terreno di una delle dimensioni del gioco (minecraft:overworld, minecraft:the_nether o minecraft:the_end): isole fluttuanti, colline dolci, un mondo di pilastri. La tua forma dice quanto è solido ogni punto: sopra 0 è terra, sotto 0 è aria. Il terreno è fatto della pietra abituale della dimensione, e il suolo dei suoi biomi ci va sopra.

content/hills.craftCraftScript
// Rolling hills over the whole overworld.
terrain "minecraft:overworld" {
    column: fun(x, z, ys, world) {
        let ground = 72 + 14 * sin(x / 60) * cos(z / 60)
        return ys.map(fun(y) { return (ground - y) / 8 })
    }
    has_land: fun(x, z, world) { return true }
}
content/FloatingIslands.ktKotlin
// Round, flattened islands in the End, one every 96 blocks.
content.terrain("minecraft:the_end") { world ->
    val shift = kotlin.random.Random(world.seed).nextInt(96)
    object : TerrainShape {
        fun across(x: Int, z: Int): Double {
            val dx = (x + shift).mod(96) - 48
            val dz = (z + shift).mod(96) - 48
            return sqrt((dx * dx + dz * dz).toDouble())
        }
        override fun column(x: Int, z: Int, ys: IntArray, out: DoubleArray) {
            val flat = across(x, z)
            for (i in ys.indices) {
                val up = (ys[i] - 64) * 2.5
                out[i] = (24 - sqrt(flat * flat + up * up)) / 24
            }
        }
        override fun hasLand(x: Int, z: Int) = across(x, z) < 24
    }
}
Membro Che cos’è
column(x, z, ys, out) quanto è solida la colonna x, z a ogni altezza di ys, in out nello stesso ordine. In CraftScript, column(x, z, ys, world) restituisce l’elenco
hasLand(x, z) (has_land) se la colonna ha della terra. Nell’End decide tra i biomi di terra e quelli del vuoto
world.seed il seed del mondo
world.minY, world.height le altezze in cui viene creato il terreno: da minY, height blocchi verso l’alto
world.biome(x, y, z) il bioma di un blocco

Il gioco interpella la tua forma negli angoli di celle larghe pochi blocchi e sfuma tra di esse, quindi non deve essere esatta blocco per blocco. In Kotlin la tua funzione viene chiamata una volta per mondo con il suo seed, e restituisce la forma. La tua forma crea tutto il terreno: le grotte di rumore del gioco non vengono create (aggiungi un carver), e nell’End anche l’isola centrale tocca a te crearla. Solo una mod può dare forma a una dimensione. Una forma che fallisce viene segnalata una volta nel log e non lascia terra lì, senza far crashare il gioco.

Le dimensioni tutte tue si dichiarano nella pagina Dimensioni con uno dei terreni del gioco.

Quale bioma va dove

Per impostazione predefinita, un bioma prende una parte (share) dei punti di quello che sostituisce (vedi Generazione del mondo). Per una mappa tutta tua, il tuo codice sceglie il bioma di ogni punto. Per ora questo vale per l’End (minecraft:the_end).

content/end_map.craftCraftScript
biome_map "minecraft:the_end" {
    biome: fun(x, y, z, land, world) {
        if x * x + z * z < 65536 { return "minecraft:the_end" }      // the central island
        if land == "minecraft:end_highlands" and (floor(x / 64) + floor(z / 64)) % 2 == 0 {
            return "mymod:crystal_glade"
        }
        return land
    }
}
content/EndMap.ktKotlin
content.tag(TagKind.BIOME, "glades", "crystal_glade", "ember_glade")

content.biomes("minecraft:the_end") { world ->
    val glades = world.tagged("mymod:glades")
    val theEnd = Identifier.parse("minecraft:the_end")
    BiomeMap { x, y, z, land ->
        when {
            x.toLong() * x + z.toLong() * z < 256L * 256 -> theEnd            // within 1024 blocks
            land.path != "end_highlands" || glades.isEmpty() -> land
            else -> glades[Math.floorMod((x shr 6) * 31 + (z shr 6), glades.size)]
        }
    }
}

La funzione riceve x, y, z in quarti (il gioco conserva un bioma ogni 4×4×4 blocchi: un quarto è blocco / 4) e land, il tipo di terra che il rumore del gioco crea lì, come bioma: minecraft:end_highlands, end_midlands, end_barrens o small_end_islands. Con una forma tua, end_highlands sulla sua terra e small_end_islands fuori da essa. Ricevi land anche sull’isola centrale, che la tua mappa piazza da sé: il gioco mette minecraft:the_end entro 1024 blocchi dal centro.

La risposta deve essere un bioma dell’End: del gioco, o uno tuo dichiarato con replaces impostato su un bioma dell’End. Qualsiasi altra risposta, o una mappa che fallisce, ripiega sulla scelta del gioco (segnalato una volta nel log). Il world della mappa ha il seed e tagged(tag), i biomi di un tag di biomi. Solo una mod può scegliere i biomi di una dimensione.

Carver

Un carver scava grotte nel terreno una volta creato, prima del suo suolo e delle feature. Per ogni chunk in creazione, il gioco passa in rassegna i chunk intorno (8 per lato); in ciascuno, ogni carver del bioma di quel chunk parte con la sua chance (0–1), e la tua funzione segna ciò che scava del chunk in creazione. I blocchi scavati diventano aria (o acqua sotto una falda), e il suolo viene rifatto sopra di essi. Un bioma usa un carver nominandolo in carvers.

content/round_caves.craftCraftScript
// A round room, 6 blocks across, in some chunks.
carver "round_caves" {
    chance: 0.05
    carve: fun(ctx) {
        let cx = ctx.start_x * 16 + 8
        let cz = ctx.start_z * 16 + 8
        let cy = 20
        for x in cx - 6..cx + 6 {
            for y in cy - 6..cy + 6 {
                for z in cz - 6..cz + 6 {
                    if (x - cx) * (x - cx) + (y - cy) * (y - cy) + (z - cz) * (z - cz) <= 36 { ctx.carve(x, y, z) }
                }
            }
        }
    }
}

biome "hollow_hills" {
    replaces: "minecraft:meadow"
    carvers: ["round_caves"]
}
content/RoundCaves.ktKotlin
content.carver("round_caves", chance = 0.05) { ctx ->
    val r = ctx.random
    val cx = ctx.startX * 16 + r.nextInt(16)
    val cz = ctx.startZ * 16 + r.nextInt(16)
    val cy = r.nextInt(ctx.minY + 10, 40)
    val radius = r.nextInt(4, 9)
    for (x in cx - radius..cx + radius) for (y in cy - radius..cy + radius) for (z in cz - radius..cz + radius) {
        val (dx, dy, dz) = Triple(x - cx, y - cy, z - cz)
        if (dx * dx + dy * dy * 2 + dz * dz <= radius * radius) ctx.carve(x, y, z)
    }
}

content.biome("hollow_hills") {
    replaces = "minecraft:meadow"
    carver("round_caves")
}
Membro Che cos’è
startX, startZ il chunk da cui parte questa grotta (coordinate di chunk: blocco / 16)
chunkX, chunkZ il chunk in creazione
minY, maxY le altezze che può scavare, entrambe incluse
seed, random il seed del mondo, e i numeri casuali di questa partenza: si ripetono uguali in ogni chunk per cui viene interpellata la partenza, così una grotta che attraversa più chunk combacia
carve(x, y, z) scava quel blocco, in coordinate del mondo; i blocchi fuori dal chunk in creazione vengono saltati

Una grotta può arrivare al massimo a 8 chunk da dove parte. In CraftScript, chance vale 1 per impostazione predefinita. Un carver che fallisce non scava nulla lì, segnalato una volta nel log.

Il suolo di un bioma

surface copre il suolo di un bioma con un blocco. Per strati, macchie o pendii, dagli invece una funzione: mentre un chunk viene creato, il gioco gliela chiede, per ogni blocco del suolo del bioma dall’alto verso il basso, quale blocco va lì. Nessuna risposta lascia quel blocco alla dimensione (la sua pietra).

content/crystal_glade.craftCraftScript
biome "crystal_glade" {
    replaces: "minecraft:end_highlands"
    share: 0.3
    surface: fun(at) {
        if at.depth_above == 1 { return "mymod:crystal_moss" }
        if at.depth_above <= 3 { return "mymod:crystal_soil" }
        return null
    }
}
content/CrystalGlade.ktKotlin
content.biome("crystal_glade") {
    replaces = "minecraft:end_highlands"
    share = 0.3
    surface { at ->
        val patch = at.noise("minecraft:surface", at.x.toDouble(), 0.0, at.z.toDouble()) > 0.2
        when {
            at.depthAbove == 1 && patch -> Identifier.parse("mymod:glow_moss")
            at.depthAbove == 1 -> Identifier.parse("mymod:crystal_moss")
            at.depthAbove <= 3 -> Identifier.parse("mymod:crystal_soil")
            else -> null
        }
    }
}
Membro di at Che cos’è
x, y, z il blocco su cui si chiede
depthAbove (depth_above) blocchi di suolo dall’aria aperta sopra fino a questo: 1 per il blocco in cima al suolo o al pavimento di una grotta
depthBelow (depth_below) blocchi di suolo da questo fino all’aria aperta sotto: 1 per il più basso sopra una grotta o il vuoto
surfaceDepth (surface_depth) quanto in profondità arrivano gli strati di superficie di questa colonna, come li fa variare il gioco
steep se il suolo è inclinato quanto dove le montagne del gioco mostrano la pietra nuda
noise(id, x, y, z) uno dei rumori del mondo per nome ("minecraft:surface"), come lo crea il seed

La funzione ha la precedenza su surface: dove non risponde nulla, un blocco di surface indicato comunque continua a coprire la cima. Una funzione che fallisce viene segnalata una volta, e dove fallisce si usa il suolo della dimensione.

Tag

I tag raggruppano blocchi, oggetti o biomi sotto un solo nome, così una feature può verificare isIn(x, y, z, "#mymod:crystal_ground") e un elenco di biomi può dire "#mymod:glades". Una mod aggiunge ai propri tag o a quelli di chiunque, come quelli del gioco:

content/Tags.ktKotlin
// Blocks a crystal can grow on, yours and the game's.
content.tag(TagKind.BLOCK, "crystal_ground", "crystal_moss", "crystal_soil", "minecraft:end_stone")
// Biomes a feature or structure can name as "#mymod:glades".
content.tag(TagKind.BIOME, "glades", "crystal_glade", "ember_glade")
// Adding to the game's: a pickaxe mines it faster.
content.tag(TagKind.BLOCK, "minecraft:mineable/pickaxe", "crystal_soil")

TagKind è BLOCK, ITEM o BIOME. I valori sono id o #tag; i nomi senza namespace sono della mod. Più mod possono aggiungere allo stesso tag, e un valore che non esiste viene saltato, non è un errore.

Buono a sapersi

  • Solo i chunk nuovi ricevono ciò che crea il tuo codice, come tutto in Generazione del mondo.
  • Gli errori non fermano il mondo. Il codice che lancia un’eccezione lascia fuori ciò che stava creando lì, e l’errore viene scritto nel log.
  • Sii veloce. Questo codice viene eseguito per ogni chunk, migliaia di volte mentre i giocatori esplorano; evita lavori pesanti al suo interno, e in CraftScript tieni presente il limite di passi del riferimento.