diff --git a/src/game/atlas/TextureAtlas.cpp b/src/game/atlas/TextureAtlas.cpp index 27ecc18e..774b2b2a 100644 --- a/src/game/atlas/TextureAtlas.cpp +++ b/src/game/atlas/TextureAtlas.cpp @@ -60,7 +60,7 @@ TextureAtlas::TextureAtlas(uvec2 size) : * @param tex - The string of the texture to convert. */ - parser.addLiteralFnCtx([](TexParser::Ctx& ctx, string tex) { + parser.addLiteralFnCtx([](TexParser::Ctx& ctx, string tex) { return TexParser::Data { std::make_shared(ctx.atlas.getBytesOfTex(tex)) }; }); diff --git a/src/lua/register/RegisterBlock.h b/src/lua/register/RegisterBlock.h index 238af2c2..6e1ffbb5 100644 --- a/src/lua/register/RegisterBlock.h +++ b/src/lua/register/RegisterBlock.h @@ -13,7 +13,17 @@ #include "game/atlas/ServerDefinitionAtlas.h" namespace RegisterBlock { + namespace { + struct TintParserData { + string tex; + optional tint; + optional mask; + }; + + using TintParser = StringParser; + 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 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>("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(meshPartTable.get_or("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 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> 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>("tint", + [](u32 tint, TintParserData tex, optional 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); } diff --git a/src/util/StringParser.h b/src/util/StringParser.h index d228e527..c9ab29d4 100644 --- a/src/util/StringParser.h +++ b/src/util/StringParser.h @@ -5,102 +5,193 @@ #include "util/Types.h" +/** SFINAE Helpers. */ namespace { + /** Struct used for identifying variants in SFINAE. */ template struct is_variant : std::false_type {}; template struct is_variant> : std::true_type {}; + /** Const bool used for identifying variants in SFINAE. */ template inline constexpr bool is_variant_v = is_variant::value; + /** Struct used for identifying optionals in SFINAE. */ template struct is_optional : std::false_type {}; template struct is_optional> : std::true_type {}; + /** Const bool used for identifying optionals in SFINAE. */ template inline constexpr bool is_optional_v = is_optional::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 class StringParser { -public: - typedef R Data; - typedef C Ctx; - class Fn; - - using EXEC_ARGS = const vec&; - using EXEC_FN = std::function; - - 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&; + + /** A wrapped function that will accept an STR_ARGS and a Context, and execute a defined function with them. */ + using PARSE_FN = std::function; + +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 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 = {}; parseStrArgs>(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 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 = {}; parseStrArgs>(strArgs, args, ctx); return std::apply(fn, std::tuple_cat(std::tuple(ctx), args)); }); } - template - void addLiteralFn(const Func& fn) { - addFn("_", fn); + /** + * Shortcut to add the literal function, which is called when a string literal is found. + * This is shorthand for calling addFn("_", ...). + * The literal function must only accept a single string parameter. + * + * @param fn - The literal function lambda. + */ + + template + inline void addLiteralFn(const Func& fn) { + addFn("_", fn); } - template - void addLiteralFnCtx(const Func& fn) { - addFnCtx("_", 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 + inline void addLiteralFnCtx(const Func& fn) { + addFnCtx("_", 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(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> 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 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 , 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 , 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 , 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 , bool> = true> + T parseStrArg(STR_ARGS args, C& ctx) { + if (I >= args.size()) throw std::invalid_argument("Not enough parameters."); + return parseRaw(parseStrArg(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 - T parseStrVariantArg(EXEC_ARGS args, C& ctx) { + inline T parseStrVariantArg(STR_ARGS args, C& ctx) { try { return parseStrArg, 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, 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(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, 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(args, ctx); } - template , bool> = true> - T parseStrArg(EXEC_ARGS args, C& ctx) { - if (I >= args.size()) throw std::invalid_argument("Not enough parameters."); - return parseRaw(parseStrArg(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 - void parseStrArgs(EXEC_ARGS strArgs, T& args, C& ctx) { + inline void parseStrArgs(STR_ARGS strArgs, T& args, C& ctx) { std::get(args) = parseStrArg, I>(strArgs, ctx); if constexpr (I + 1 < std::tuple_size_v) parseStrArgs(strArgs, args, ctx); } - std::unordered_map functions {}; + /** A map of parse functions, indexed by their name. */ + std::unordered_map functions {}; + + /** If true, unknown functions will be parsed as string literals, if false, an error will be thrown. */ + bool unknownFnsAreLiteral = false; }; \ No newline at end of file diff --git a/subgames/zeus/mods/auri_hot_wheel/script/server/main.lua b/subgames/zeus/mods/auri_hot_wheel/script/server/main.lua index 8ca3c07e..bd62515f 100644 --- a/subgames/zeus/mods/auri_hot_wheel/script/server/main.lua +++ b/subgames/zeus/mods/auri_hot_wheel/script/server/main.lua @@ -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) diff --git a/subgames/zeus/mods/zeus_flowers/script/flowers.lua b/subgames/zeus/mods/zeus_flowers/script/flowers.lua index 52e70742..d81a7747 100644 --- a/subgames/zeus/mods/zeus_flowers/script/flowers.lua +++ b/subgames/zeus/mods/zeus_flowers/script/flowers.lua @@ -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, diff --git a/subgames/zeus/mods/zeus_flowers/script/models/hash.lua b/subgames/zeus/mods/zeus_flowers/script/models/hash.lua index 546b655a..3d3e66a1 100644 --- a/subgames/zeus/mods/zeus_flowers/script/models/hash.lua +++ b/subgames/zeus/mods/zeus_flowers/script/models/hash.lua @@ -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 } } } diff --git a/subgames/zeus/mods/zeus_flowers/textures/clover.png b/subgames/zeus/mods/zeus_flowers/textures/clover.png new file mode 100644 index 00000000..7999a133 Binary files /dev/null and b/subgames/zeus/mods/zeus_flowers/textures/clover.png differ diff --git a/subgames/zeus/mods/zeus_world/script/biomes/plains.lua b/subgames/zeus/mods/zeus_world/script/biomes/plains.lua index 914dfd87..40386d69 100644 --- a/subgames/zeus/mods/zeus_world/script/biomes/plains.lua +++ b/subgames/zeus/mods/zeus_world/script/biomes/plains.lua @@ -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 })