Document StringParser because otherwise I'd have no idea what it's doing in two months.

master
Auri 2021-09-23 14:08:58 -07:00
parent 29a082aa9c
commit 05af3222ee
8 changed files with 354 additions and 122 deletions

View File

@ -60,7 +60,7 @@ TextureAtlas::TextureAtlas(uvec2 size) :
* @param tex - The string of the texture to convert.
*/
parser.addLiteralFnCtx<string>([](TexParser::Ctx& ctx, string tex) {
parser.addLiteralFnCtx([](TexParser::Ctx& ctx, string tex) {
return TexParser::Data { std::make_shared<RawTexData>(ctx.atlas.getBytesOfTex(tex)) };
});

View File

@ -13,7 +13,17 @@
#include "game/atlas/ServerDefinitionAtlas.h"
namespace RegisterBlock {
namespace {
struct TintParserData {
string tex;
optional<u32> tint;
optional<string> mask;
};
using TintParser = StringParser<TintParserData>;
TintParser parser {};
bool parserReady = false;
/**
* Takes a lua selection box table list, and returns a vector of selection boxes.
@ -37,44 +47,6 @@ namespace RegisterBlock {
return boxes;
}
/**
* Given a textures string, attempts to find a tint() texture modifier and modifies the inputted parameters
* to describe it. Does nothing to the passed in variables if there is tint() is not used.
*
* @param texture - The texture string, will be replaced with the inner string if tint() is used.
* @param blendInd - A variable reference that will be assigned the index of the tint blend, if there is one.
* @param blendMask - A variable reference to the blend mask texture, which will be assigned if one is found.
*/
static inline void getMeshPartTexture(std::string& texture, unsigned int& blendInd, std::string& blendMask) {
if (strncmp(texture.data(), "tint(", 5) == 0 && texture.find_last_of(')') != std::string::npos) {
// Biome tinting time
texture.erase(std::remove_if(texture.begin(), texture.end(), isspace), texture.end());
std::string::size_type paramsBegin = texture.find_first_of('(');
std::string::size_type paramsEnd = texture.find_last_of(')');
std::string paramsString = texture.substr(paramsBegin + 1, paramsEnd - paramsBegin - 1);
std::vector<std::string> params;
std::string::size_type pos;
while ((pos = paramsString.find(',')) != std::string::npos) {
params.push_back(paramsString.substr(0, pos));
paramsString.erase(0, pos + 1);
}
params.push_back(paramsString);
if (params.size() < 2)
throw std::runtime_error("Invalid biome tint values. Must have at least 2 params.");
texture = params[1];
blendInd = atoi(params[0].data()) + 1; //TODO: support multiple blend colors
blendMask = (params.size() >= 3 ? params[2] : "");
}
}
/**
* Creates near and far models for a block based on the passed in parameters.
*
@ -141,7 +113,7 @@ namespace RegisterBlock {
}
}
// Parse through all of the parts and add them to the model
// Parse through all the parts and add them to the model
auto partsOpt = modelTable.get<sol::optional<sol::table>>("parts");
if (!partsOpt) throw std::runtime_error("blockmodel is missing parts table");
partsOpt->for_each([&](sol::object key, sol::object value) {
@ -183,20 +155,17 @@ namespace RegisterBlock {
// Get the part's texture
int tex = std::max(static_cast<int>(meshPartTable.get_or<float>("tex", 1)), 1);
auto texture = textures[std::min(tex - 1, (int) textures.size() - 1)];
unsigned int blendInd = 0;
std::string blendMask = "";
getMeshPartTexture(texture, blendInd, blendMask);
let data = parser.parse(textures[std::min(tex - 1, (int) textures.size() - 1)]);
u32 blendInd = data.tint ? *data.tint + 1 : 0;
// Add texture refs to blockModel if the textures table is provided
std::shared_ptr<AtlasRef> textureRef = nullptr, blendMaskRef = nullptr;
if (atlas) {
textureRef = (*atlas)[texture];
textureRef = (*atlas)[data.tex];
model.textureRefs.insert(textureRef);
if (blendInd && !blendMask.empty()) {
blendMaskRef = (*atlas)[blendMask];
if (blendInd && data.mask) {
blendMaskRef = (*atlas)[*data.mask];
model.textureRefs.insert(blendMaskRef);
}
}
@ -259,14 +228,12 @@ namespace RegisterBlock {
std::vector<std::shared_ptr<AtlasRef>> blendMaskRefs;
for (auto i = 0; i < lowdef_textures.size(); i++) {
std::string texture = lowdef_textures[i];
unsigned int blendInd = 0;
std::string blendMask = "";
getMeshPartTexture(texture, blendInd, blendMask);
let data = parser.parse(lowdef_textures[i]);
u32 blendInd = data.tint ? *data.tint + 1 : 0;
textureRefs.push_back((*atlas)[texture]);
textureRefs.push_back((*atlas)[data.tex]);
blendInds.push_back(blendInd);
blendMaskRefs.push_back(blendMask != "" ? (*atlas)[blendMask] : nullptr);
blendMaskRefs.push_back(data.mask ? (*atlas)[*data.mask] : nullptr);
}
farModel = BlockModel::createCube(textureRefs, blendInds, blendMaskRefs);
@ -413,6 +380,27 @@ namespace RegisterBlock {
}
}
/**
* Initializes the parser with the necessary parse functions.
* In the future, this could be cleaned up, but as it is it's totally fine.
*/
static void initParser() {
if (parserReady) return;
parser.setUnknownFnsAreLiteral(true);
parser.addFn<u32, TintParserData, optional<TintParserData>>("tint",
[](u32 tint, TintParserData tex, optional<TintParserData> mask) {
return TintParserData { tex.tex, tint, mask ? optional(mask->tex) : std::nullopt };
});
parser.addLiteralFn([](string tex) {
return TintParserData { tex, std::nullopt, std::nullopt };
});
parserReady = true;
}
/**
* Server method to register a block. Calls registerBlock with the necessary parameters.
@ -424,11 +412,11 @@ namespace RegisterBlock {
*/
static void server(sol::table& core, ServerSubgame& game, const std::string& identifier) {
initParser();
registerBlock(core["registered_blocks"], core["registered_blockmodels"],
identifier, game.getDefs(), nullptr);
}
/**
* Client method to register a block. Calls registerBlock with the necessary parameters.
* Registers a block to the DefinitionAtlas.
@ -439,6 +427,7 @@ namespace RegisterBlock {
*/
static void client(sol::table& core, LocalSubgame& game, const std::string& identifier) {
initParser();
registerBlock(core["registered_blocks"], core["registered_blockmodels"],
identifier, game.getDefs(), &game.textures);
}

View File

@ -5,102 +5,193 @@
#include "util/Types.h"
/** SFINAE Helpers. */
namespace {
/** Struct used for identifying variants in SFINAE. */
template<typename T> struct is_variant : std::false_type {};
template<typename... Args>
struct is_variant<std::variant<Args...>> : std::true_type {};
/** Const bool used for identifying variants in SFINAE. */
template<typename T>
inline constexpr bool is_variant_v = is_variant<T>::value;
/** Struct used for identifying optionals in SFINAE. */
template<typename T> struct is_optional : std::false_type {};
template<typename... Args>
struct is_optional<std::optional<Args...>> : std::true_type {};
/** Const bool used for identifying optionals in SFINAE. */
template<typename T>
inline constexpr bool is_optional_v = is_optional<T>::value;
}
/**
* Exposes a method to parse a string containing recursive functions,
* e.g: "tint(0, crop(0, 0, 16, 16, my_texture))".
* Functions are defined using template parameters and coerced from the string,
* executed in the order that they are used in the string and used to create an output.
*
* @tparam R - The type that parse() and all declared functions will return.
* @tparam C - An optional context type, which an instance of can be passed to parse() and accessed in functions.
*/
template <typename R, typename C = nullptr_t>
class StringParser {
public:
typedef R Data;
typedef C Ctx;
class Fn;
using EXEC_ARGS = const vec<std::string_view>&;
using EXEC_FN = std::function<R(C&, EXEC_ARGS)>;
struct Fn {
Fn(EXEC_FN exec): exec(exec) {};
R operator()(C& ctx, EXEC_ARGS args) const {
return exec(ctx, args);
}
private:
EXEC_FN exec;
};
/** A type alias for a vector of string parameters that will be passed into a Fn. */
using STR_ARGS = const vec<std::string_view>&;
/** A wrapped function that will accept an STR_ARGS and a Context, and execute a defined function with them. */
using PARSE_FN = std::function<R(C&, STR_ARGS)>;
public:
/** The data type of this parser. */
typedef R Data;
/** The context type of this parser. Will be nullptr_t if no context type is specified. */
typedef C Ctx;
explicit StringParser() = default;
/** If set to true, when an unknown function is read it will be passed to the literal function as a string. */
void setUnknownFnsAreLiteral(bool state) {
unknownFnsAreLiteral = state;
}
/**
* Adds a function to the functions map.
* For a function to be valid, it must only have parameters that are
* integral, floating point, strings, the Data type, or optionals & variants of them.
* It must also return a Data type. The Args type parameter must match the function's types.
* If the function needs access to the Context, use addFnCtx() instead.
*
* @tparam Args - The argument types of the function.
* @param name - The name of the function.
* @param fn - The function lambda.
*/
template <typename... Args, typename Func>
void addFn(const string& name, const Func& fn) {
functions.emplace(name, [=, this](C& ctx, EXEC_ARGS strArgs) {
functions.emplace(name, [=, this](C& ctx, STR_ARGS strArgs) {
std::tuple<Args...> args = {};
parseStrArgs<std::tuple<Args...>>(strArgs, args, ctx);
return std::apply(fn, args);
});
}
/**
* Adds a function to the functions map that has access to the Ctx.
* The same restrictions for a function apply, but the function
* must also accept a reference to a Ctx before all other arguments.
* This parameter should not be specified in Args.
*
* @tparam Args - The argument types of the function.
* @param name - The name of the function.
* @param fn - The function lambda.
*/
template <typename... Args, typename Func>
void addFnCtx(const string& name, const Func& fn) {
functions.emplace(name, [=, this](C& ctx, EXEC_ARGS strArgs) {
functions.emplace(name, [=, this](C& ctx, STR_ARGS strArgs) {
std::tuple<Args...> args = {};
parseStrArgs<std::tuple<Args...>>(strArgs, args, ctx);
return std::apply(fn, std::tuple_cat(std::tuple<C&>(ctx), args));
});
}
template <typename... Args, typename Func>
void addLiteralFn(const Func& fn) {
addFn<Args...>("_", fn);
/**
* Shortcut to add the literal function, which is called when a string literal is found.
* This is shorthand for calling addFn<string>("_", ...).
* The literal function must only accept a single string parameter.
*
* @param fn - The literal function lambda.
*/
template <typename Func>
inline void addLiteralFn(const Func& fn) {
addFn<string>("_", fn);
}
template <typename... Args, typename Func>
void addLiteralFnCtx(const Func& fn) {
addFnCtx<Args...>("_", fn);
/**
* Shortcut to add the literal function that has access to the Ctx.
* The same restrictions for a literal function apply, but the function
* must also accept a reference to a Ctx before the string parameter.
*
* @param fn - The literal function lambda.
*/
template <typename Func>
inline void addLiteralFnCtx(const Func& fn) {
addFnCtx<string>("_", fn);
}
/**
* Parses a string using the functions defined.
* This method may only be called if no context was specified.
*
* @param str - The string to parse.
* @returns the parsed result.
*/
const R parse(string str) const {
const nullptr_t ctx {};
str.erase(std::remove_if(str.begin(), str.end(), isspace), str.end());
return std::move(parseRaw(str, const_cast<C&>(ctx)));
}
/**
* Parses a string using the functions defined.
*
* @param str - The string to parse.
* @param ctx - The context to parse with, passed to all functions that use Ctx.
* @returns the parsed result.
*/
const R parse(string str, C& ctx) const {
str.erase(std::remove_if(str.begin(), str.end(), isspace), str.end());
return std::move(parseRaw(str, ctx));
}
private:
const R parseRaw(const std::string_view str, C& ctx = {}) const {
/**
* Parses a the string segment provided as a function or string literal.
*
* @param str - The string segment to parse.
* @param ctx - The context to parse with.
* @returns the parsed result.
*/
inline const R parseRaw(const std::string_view str, C& ctx) const {
if (strIsFunction(str)) {
let func = parseFunction(str);
const let f = functions.find(string(func.first));
if (f == functions.end()) throw std::invalid_argument("Unknown function '" + string(func.first) + "'!");
return f->second(ctx, func.second);
}
else {
const let& f = functions.find("_");
if (f == functions.end()) throw std::invalid_argument("No default function handler!");
return f->second(ctx, { str });
if (f == functions.end()) {
if (!unknownFnsAreLiteral) throw std::invalid_argument(
"Unknown function '" + string(func.first) + "'.");
}
else return f->second(ctx, func.second);
}
const let& f = functions.find("_");
if (f == functions.end()) throw std::invalid_argument("Literal specified with no literal function.");
return f->second(ctx, { str });
}
/**
* Given a string segment starting and a start position pointing to an open parenthesis,
* finds the index of matching closing parenthesis.
*
* @param str - The string segment to search through.
* @param start - The position of the opening parenthesis to start at.
* @returns the index of the start parenthesis' closing parenthesis.
* @throws if the parentheses are unbalanced.
*/
const usize findClosingParen(const std::string_view& str, usize start) const {
usize levels = 0;
for (usize i = start + 1; i < str.size(); i++) {
@ -114,17 +205,29 @@ private:
throw std::invalid_argument("Mismatched parentheses.");
}
/**
* Checks if the string segment provided is a function call.
*
* @param str - the string segment to check.
* @returns a boolean indicating if the string segment is a function call.
*/
const bool strIsFunction(const std::string_view& str) const {
return str.find_first_of('(') != string::npos;
}
/**
* Parses a function and finds its name and parameters.
* Assumes that the string is a function, which should be checked beforehand with strIsFunction.
*
* @param str - The string to parse as a function.
* @returns a pair containing the function name, and a vector of parameter strings.
*/
const std::pair<std::string_view, vec<std::string_view>> parseFunction(const std::string_view& str) const {
let nextParen = str.find_first_of('(');
let nextComma = str.find_first_of(',');
if (nextParen == string::npos || (nextComma != string::npos && nextComma < nextParen))
throw std::invalid_argument("Not a function");
let name = str.substr(0, nextParen);
vec<std::string_view> args {};
@ -144,26 +247,87 @@ private:
return { name, args };
}
/**
* Parses a string argument from an STR_ARGS.
*
* @tparam T - std::string
* @tparam I - The index of the parameter to parse in the args vector.
* @param args - An STR_ARGS to pull the parameter from.
* @returns the string value.
* @throws if the parameter cannot be interpreted as a string, or if there were not enough parameters.
*/
template <typename T, usize I, std::enable_if_t<std::is_same_v<T, string>, bool> = true>
T parseStrArg(EXEC_ARGS args, C&) {
T parseStrArg(STR_ARGS args, C&) {
if (I >= args.size()) throw std::invalid_argument("Not enough parameters.");
return string(args[I]);
}
/**
* Parses an integral argument from an STR_ARGS.
*
* @tparam T - An integral type, e.g u8, i32, usize...
* @tparam I - The index of the parameter to parse in the args vector.
* @param args - An STR_ARGS to pull the parameter from.
* @returns the integral value.
* @throws if the parameter cannot be interpreted as an integer, or if there were not enough parameters.
*/
template <typename T, usize I, std::enable_if_t<std::is_integral_v<T>, bool> = true>
T parseStrArg(EXEC_ARGS args, C&) {
T parseStrArg(STR_ARGS args, C&) {
if (I >= args.size()) throw std::invalid_argument("Not enough parameters.");
return std::stoi(args[I].data());
}
/**
* Parses a floating point argument from an STR_ARGS.
*
* @tparam T - A floating point type, i.e f32 or f64.
* @tparam I - The index of the parameter to parse in the args vector.
* @param args - An STR_ARGS to pull the parameter from.
* @returns the floating point value.
* @throws if the parameter cannot be interpreted as a floating point, or if there were not enough parameters.
*/
template <typename T, usize I, std::enable_if_t<std::is_floating_point_v<T>, bool> = true>
T parseStrArg(EXEC_ARGS args, C&) {
T parseStrArg(STR_ARGS args, C&) {
if (I >= args.size()) throw std::invalid_argument("Not enough parameters.");
return std::stod(args[I].data());
}
/**
* Parses a Data type argument from an STR_ARGS.
* A parse function will have a Data type argument if it wants to accept the output from another function.
*
* @tparam T - The Data type.
* @tparam I - The index of the parameter to parse in the args vector.
* @param args - An STR_ARGS to pull the parameter from.
* @param ctx - The context to pass into the parse functions.
* @returns the Data result from whatever inner functions were executed.
* @throws if the parameter cannot be parsed as a Data type, or if there were not enough parameters.
*/
template <typename T, usize I, std::enable_if_t<std::is_same_v<T, R>, bool> = true>
T parseStrArg(STR_ARGS args, C& ctx) {
if (I >= args.size()) throw std::invalid_argument("Not enough parameters.");
return parseRaw(parseStrArg<string, I>(args, ctx), ctx);
}
/**
* Compile-time iterates over a variant type, checking if the parameter specified
* matches any of the types in it. If one matches, it is parsed and returned.
*
* @tparam T - A variant type containing other parsable types.
* @tparam I - The index of the parameter to parse in the **args vector.**
* @tparam VI - The index of the variant to try to parse.
* @param args - An STR_ARGS to pull the parameter from.
* @param ctx - The context to pass into the parse functions.
* @returns a variant containing the first type that the parameter can be parsed as.
* @throws if the parameter cannot be parsed as any of the variant types.
*/
template <typename T, usize I, usize VI = 0>
T parseStrVariantArg(EXEC_ARGS args, C& ctx) {
inline T parseStrVariantArg(STR_ARGS args, C& ctx) {
try {
return parseStrArg<std::variant_alternative_t<VI, T>, I>(args, ctx);
}
@ -176,29 +340,63 @@ private:
}
}
/**
* Parses a variant argument from an STR_ARGS.
* The variant can contain any other parsable types.
*
* @tparam T - The variant type.
* @tparam I - The index of the parameter to parse in the args vector.
* @param args - An STR_ARGS to pull the parameter from.
* @param ctx - The context to pass into the parse functions.
* @returns a variant containing the first type that the parameter can be parsed as.
* @throws if the parameter cannot be parsed as any of the variant types, or if there were not enough parameters.
*/
template<typename T, usize I, std::enable_if_t<is_variant_v<T>, bool> = true>
T parseStrArg(EXEC_ARGS args, C& ctx) {
T parseStrArg(STR_ARGS args, C& ctx) {
if (I >= args.size()) throw std::invalid_argument("Not enough parameters.");
return parseStrVariantArg<T, I>(args, ctx);
}
/**
* Parses an optional argument from an STR_ARGS.
* The optional can contain any parsable type, including variants and other optionals.*
* * but why the hell would you do that?
*
* @tparam T - The optional type.
* @tparam I - The index of the parameter to parse in the args vector.
* @param args - An STR_ARGS to pull the parameter from.
* @param ctx - The context to pass into the parse function.
* @returns an optional that may contain a value.
*/
template<typename T, usize I, std::enable_if_t<is_optional_v<T>, bool> = true>
T parseStrArg(EXEC_ARGS args, C& ctx) {
T parseStrArg(STR_ARGS args, C& ctx) {
if (I >= args.size() || args[I].empty()) return {};
return parseStrArg<typename T::value_type, I>(args, ctx);
}
template <typename T, usize I, std::enable_if_t<std::is_same_v<T, R>, bool> = true>
T parseStrArg(EXEC_ARGS args, C& ctx) {
if (I >= args.size()) throw std::invalid_argument("Not enough parameters.");
return parseRaw(parseStrArg<string, I>(args, ctx), ctx);
}
/**
* Compile-time iterates over a tuple of the argument types and parses them,
* inserting the parsed parameters into the args tuple referenced.
*
* @tparam T - The tuple type of the arguments.
* @tparam I - The index of the current parameter being parsed. Used for iterating.
* @param strArgs - The STR_ARGS storing the string parameters.
* @param args - The args tuple to insert the parsed arguments into.
* @param ctx - The context to pass into parse functions.
* @throws if any of the parameters cannot be parsed, see parseStrArg implementations for details.
*/
template <typename T, usize I = 0>
void parseStrArgs(EXEC_ARGS strArgs, T& args, C& ctx) {
inline void parseStrArgs(STR_ARGS strArgs, T& args, C& ctx) {
std::get<I>(args) = parseStrArg<std::tuple_element_t<I, T>, I>(strArgs, ctx);
if constexpr (I + 1 < std::tuple_size_v<T>) parseStrArgs<T, I + 1>(strArgs, args, ctx);
}
std::unordered_map<string, Fn> functions {};
/** A map of parse functions, indexed by their name. */
std::unordered_map<string, PARSE_FN> functions {};
/** If true, unknown functions will be parsed as string literals, if false, an error will be thrown. */
bool unknownFnsAreLiteral = false;
};

View File

@ -1,5 +1,4 @@
zepha.bind('new_player', function(player)
print('added inventories :)')
local inv = player:get_inventory()
inv:add_list('hot_wheel_1', 5, 5)
inv:add_list('hot_wheel_2', 5, 5)

View File

@ -41,9 +41,13 @@ zepha.register_block("zeus:flowers:clover", {
solid = false,
model = "zeus:flowers:hash",
textures = {
-- "tint(0, crop(0, 0, 16, 16, zeus:flowers:clover))",
-- "tint(0, crop(16, 0, 16, 16, zeus:flowers:clover))",
-- "tint(0, crop(32, 0, 16, 16, zeus:flowers:clover))"
"tint(0, crop(0, 0, 16, 16, zeus:flowers:clover))",
"tint(0, crop(16, 0, 16, 16, zeus:flowers:clover))",
"tint(0, crop(32, 0, 16, 16, zeus:flowers:clover))",
"tint(0, crop(0, 16, 16, 8, zeus:flowers:clover))",
"tint(0, crop(16, 16, 16, 8, zeus:flowers:clover))",
"tint(0, crop(0, 24, 16, 8, zeus:flowers:clover))",
"tint(0, crop(16, 24, 16, 8, zeus:flowers:clover))"
},
light_propagates = true,
lowdef_render = false,

View File

@ -3,7 +3,20 @@
-- with multiple layers of topfaces.
--
local offset_amp = 0.2
local amp = 0.025
zepha.register_blockmodel('zeus:flowers:hash', {
mesh_mods = {
{
type = 'offset_x',
amplitude = offset_amp,
},
{
type = 'offset_z',
amplitude = offset_amp,
}
},
parts = {
{
face = 'nocull',
@ -13,6 +26,10 @@ zepha.register_blockmodel('zeus:flowers:hash', {
4/16, 0, 1, 1, 1,
4/16, 0.5, 1, 1, 0,
4/16, 0.5, 0, 0, 0
},
shader_mod = {
type = 'sway_attached',
amplitude = amp
}
}, {
face = 'nocull',
@ -22,6 +39,10 @@ zepha.register_blockmodel('zeus:flowers:hash', {
12/16, 0, 1, 0, 1,
12/16, 0, 0, 1, 1,
12/16, 0.5, 0, 1, 0
},
shader_mod = {
type = 'sway_attached',
amplitude = amp
}
}, {
face = 'nocull',
@ -31,6 +52,10 @@ zepha.register_blockmodel('zeus:flowers:hash', {
1, 0, 12/16, 1, 1,
1, 0.5, 12/16, 1, 0,
0, 0.5, 12/16, 0, 0
},
shader_mod = {
type = 'sway_attached',
amplitude = amp
}
}, {
face = 'nocull',
@ -40,6 +65,10 @@ zepha.register_blockmodel('zeus:flowers:hash', {
0, 0.5, 4/16, 1, 0,
1, 0.5, 4/16, 0, 0,
1, 0, 4/16, 0, 1
},
shader_mod = {
type = 'sway_attached',
amplitude = amp
}
}, {
face = 'nocull',
@ -49,6 +78,10 @@ zepha.register_blockmodel('zeus:flowers:hash', {
0, 4/16, 1, 0, 1,
1, 4/16, 1, 1, 1,
1, 4/16, 0, 1, 0
},
shader_mod = {
type = 'sway_full_block',
amplitude = amp
}
},
{
@ -59,7 +92,11 @@ zepha.register_blockmodel('zeus:flowers:hash', {
0, 3/16, 1, 0, 1,
1, 3/16, 1, 1, 1,
1, 3/16, 0, 1, 0
}
},
shader_mod = {
type = 'sway_full_block',
amplitude = amp
}
},
{
face = 'nocull',
@ -69,6 +106,10 @@ zepha.register_blockmodel('zeus:flowers:hash', {
0, 2/16, 1, 0, 1,
1, 2/16, 1, 1, 1,
1, 2/16, 0, 1, 0
},
shader_mod = {
type = 'sway_full_block',
amplitude = amp
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

View File

@ -21,7 +21,7 @@ local structures = {}
table.insert(structures, zepha.create_structure({
origin = V(1),
probability = 0.0025,
probability = 0.00125,
layout = {{
{ none, none, none },
{ none, wood, none },
@ -91,7 +91,7 @@ local leaf_layer_3 = {
table.insert(structures, zepha.create_structure({
origin = V(2, 2, 2),
probability = 0.0005,
probability = 0.00025,
layout = {
-- trunk_layer_0,
-- trunk_layer_0,
@ -121,28 +121,28 @@ table.insert(structures, zepha.create_structure({
}
}))
-- for i = 1, 5 do
-- table.insert(structures, zepha.create_structure({
-- origin = V(),
-- probability = 0.1,
-- layout = {{{ "zeus:default:tall_grass_" .. tostring(i) }}}
-- }))
-- end
for i = 1, 5 do
table.insert(structures, zepha.create_structure({
origin = V(),
probability = 0.025,
layout = {{{ "zeus:default:tall_grass_" .. tostring(i) }}}
}))
end
table.insert(structures, zepha.create_structure({
origin = V(),
probability = 0.1,
probability = 0.35,
layout = {{{ "zeus:flowers:clover" }}}
}))
table.insert(structures, zepha.create_structure({
origin = V(),
probability = 0.025,
probability = 0.0125,
layout = {{{ "zeus:flowers:flower_geranium" }}}
}))
table.insert(structures, zepha.create_structure({
origin = V(),
probability = 0.025,
probability = 0.0125,
layout = {{{ "zeus:flowers:flower_white_dandelion" }}}
}))
@ -207,6 +207,7 @@ zepha.register_biome(identifier, {
tags = { natural = 1, default = 1 },
structures = structures,
biome_tint = "#46cfc0",
-- biome_tint = "#aaed45",
noise = noise
})