Files
snowy/modules/mod-blocks.nix
2026-08-12 07:09:10 -05:00

319 lines
11 KiB
Nix

{ lib, config, ... }:
with lib;
{
options = {
blocks = mkOption {
type = types.attrsOf (
types.submodule {
options = {
displayName = mkOption {
type = types.str;
description = "The display name of the block.";
};
properties = {
## Mining properties
# .strength(float destroyTime, ?)
hardness = mkOption {
type = types.float;
default = 1.0;
description = "The block's destroy time (hardness).";
};
# .strength(?, float explosionResistance)
resistance = mkOption {
type = types.nullOr types.float;
default = null;
description = "The block's explosion resistance.";
};
# requiresCorrectToolForDrops()
requiresTool = mkOption {
type = types.bool;
default = false;
description = "Whether the block requires the correct tool to drop.";
};
## Physical properties
# noCollision()
noCollision = mkOption {
type = types.bool;
default = false;
description = "Whether the block has no collision.";
};
# noOcclusion()
noOcclusion = mkOption {
type = types.bool;
default = false;
description = "Whether the block has no occlusion.";
};
# dynamicShape()
dynamicShape = mkOption {
type = types.bool;
default = false;
description = "Whether the block has a dynamic shape.";
};
# forceSolidOn()
forceSolidOn = mkOption {
type = types.bool;
default = false;
description = "Whether the block is forced to be solid on all sides.";
};
# pushReaction(PushReaction)
pushReaction = mkOption {
type = types.enum [ "NORMAL" "DESTROY" "BLOCK" "IGNORE" "PUSH_ONLY" ];
default = "NORMAL";
description = "The block's reaction to being pushed by pistons.";
};
# offsetType(BlockBehaviour.OffsetType)
offsetType = mkOption {
type = types.enum [ "NONE" "XYZ" "XZ" ];
default = "NONE";
description = "The block's offset type.";
};
# replaceable()
replaceable = mkOption {
type = types.bool;
default = false;
description = "Whether the block is replaceable; e.g. like tall grass or snow layers.";
};
## Movement
# friction(float)
friction = mkOption {
type = types.float;
default = 0.6;
description = "The block's friction.";
};
# speedFactor(float)
speedFactor = mkOption {
type = types.float;
default = 1.0;
description = "The block's speed factor.";
};
#jumpFactor(float)
jumpFactor = mkOption {
type = types.float;
default = 1.0;
description = "The block's jump factor.";
};
## Visual
#mapColor(MapColor) & mapColor(DyeColor) & mapColor(closure)
mapColor = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
The block's map color. For dyecolor, use dye:<color>, map:<color>.
To specify a closure, use closure:<fqdn>. For example, to reference the GetMapColor(<block>) method of a class, use closure:com.example.MyBlock::getMapColor.
'';
};
#lightLevel(ToIntFunction<BlockState>)
lightLevel = mkOption {
type = types.nullOr (types.oneOf [ types.int types.str ]);
default = null;
description = ''
The block's light level. Can be a constant integer (0-15), or a path method with (BlockState) -> Integer, e.g. closure:com.example.MyBlock::getLightLevel.
'';
};
#noTerrainParticles()
noTerrainParticles = mkOption {
type = types.bool;
default = false;
description = "Whether the block has no terrain particles.";
};
#isViewBlocking(BlockBehaviour.StatePredicate)
isViewBlocking = mkOption {
type = types.nullOr types.str;
default = null;
description = "Whether the block is view blocking. Provide a path to a BlockBehavior.StatePredicate method, e.g. closure:com.example.MyBlock::isViewBlocking.";
};
#isSuffocating(BlockBehaviour.StatePredicate)
isSuffocating = mkOption {
type = types.nullOr types.str;
default = null;
description = "Whether the block is suffocating. Provide a path to a BlockBehavior.StatePredicate method, e.g. closure:com.example.MyBlock::isSuffocating.";
};
## Sound
sounds = mkOption {
type = types.str;
default = "STONE";
description = "The name of the vanilla SoundType constant to use.";
};
instruments = mkOption {
type = types.nullOr types.str;
default = null;
description = "The name of the vanilla Instrument constant to use.";
};
## Loot
noLootTable = mkOption {
type = types.bool;
default = false;
description = "Whether the block has no loot table.";
};
lootFrom = mkOption {
type = types.nullOr types.str;
default = null;
description = "A Supplier<? extends Block> that provides the loot table for this block. Provide a path to a method that returns a Block, e.g. closure:com.example.MyBlock::getLootTable.";
};
## Miscellaneous
randomTicks = mkOption {
type = types.bool;
default = false;
description = "Whether the block receives random ticks.";
};
ignitesByLava = mkOption {
type = types.bool;
default = false;
description = "Whether the block can be ignited by lava.";
};
liquid = mkOption {
type = types.bool;
default = false;
description = "Whether the block is a liquid.";
};
isValidSpawn = mkOption {
type = types.nullOr types.str;
default = null;
description = "BlockBehaviour.StateArgumentPredicate<EntityType<?>> that determines whether this block is a valid spawn point for the given entity type. Provide a path to a method that returns a boolean, e.g. closure:com.example.MyBlock::isValidSpawn.";
};
air = mkOption {
type = types.bool;
default = false;
description = "Whether the block is air-like";
};
requiredFeatures = mkOption {
type = types.listOf types.str;
default = [ ];
description = "A list of required features for this block to be available. Written as <modid>:<feature>.";
};
};
customClass = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
Path (relative to this mod.nix) to a hand-written Java file whose
public class extends the generated "Base''${className}" block class.
When unset, snowy generates one flat, concrete block class instead.
'';
};
/*
states = {
"Integer:power" = {
min = 0;
max = 15;
default = 0;
};
"Boolean:lit" = {
default = true;
}
"Enum:facing" = {
enum = "net.minecraft.world.level.block.state.properties.BlockStateProperties.FACING"
}
}
*/
states = mkOption {
type = types.attrsOf {
};
};
customModel = mkOption {
type = types.bool;
default = false;
description = ''
When true, snowy still generates this block's blockstates.json
(pointing at "<namespace>:block/<path>" as usual), but expects a
hand-written models/block/<path>.json to already exist there
instead of generating one from textures. Mutually exclusive with
textures.
'';
};
item = {
enabled = mkOption {
type = types.bool;
default = true;
description = "Whether to register a BlockItem for this block.";
};
texture = mkOption {
type = types.nullOr types.str;
default = null;
description = ''
Flat 2D item icon (relative to textures/item/, no extension).
When unset, the item renders the block's 3D model instead.
Mutually exclusive with item.customModel.
'';
};
customModel = mkOption {
type = types.bool;
default = false;
description = ''
Like the block-level customModel: expect a hand-written
models/item/<path>.json instead of generating one.
Mutually exclusive with item.texture.
'';
};
};
blockEntity = mkOption {
type = types.nullOr (
types.submodule {
options = {
class = mkOption {
type = types.str;
description = ''
Path (relative to this mod.nix) to a hand-written Java file
whose public class extends the generated
"Base''${className}Entity" block entity class.
'';
};
ticker = {
client = mkOption {
type = types.bool;
default = false;
description = "Whether this block entity ticks on the client.";
};
server = mkOption {
type = types.bool;
default = false;
description = "Whether this block entity ticks on the server.";
};
};
};
}
);
default = null;
description = "The block entity attached to this block, if any.";
};
};
}
);
default = { };
description = ''
The blocks this mod registers, keyed by full registry name
("<modid>:<path>").
'';
};
};
}