First version of composting mode.

This commit is contained in:
SFENCE 2021-09-16 21:12:46 +02:00
commit 4382dc3c7e
30 changed files with 54740 additions and 0 deletions

38
LICENSE Normal file
View File

@ -0,0 +1,38 @@
License of source code
======================
The MIT License (MIT)
---------------------
Copyright (C) 2021 SFENCE
Permission is hereby granted, free of charge, to any person obtaining a copy of this
software and associated documentation files (the "Software"), to deal in the Software
without restriction, including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software, and to permit
persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
For more details:
https://opensource.org/licenses/MIT
Licenses of media
=================
Attribution-ShareAlike 4.0 International (CC BY-SA 4.0)
-------------------------------------------------------
This work is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License. To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/4.0/ or send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.
Attribution-SharedAlike 3.0 Unported (CC BY-SA 3.0)
------------------------------------
This work is licensed under the Creative Commons Attribution-ShareAlike 3.0 Unported License. To view a copy of this license, visit http://creativecommons.org/licenses/by-sa/3.0/ or send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.

34
README.md Normal file
View File

@ -0,0 +1,34 @@
Minetest Game mod: composting
=============================
This mod provide composting functionality into minetest game.
It includes composter, composting and garden soil.
See LICENSE for license information.
Source code copyright
----------------------
Copyright (c) 2021 SFENCE
MIT - check LICENSE file
Media copyright
---------------
Textures
--------
CC BY-SA 4.0: Copyright (c) 2021 SFENCE
* others
Models
------
CC BY-SA 4.0: Copyright (c) 2021 SFENCE
* all
Sounds
------

7
TODO Normal file
View File

@ -0,0 +1,7 @@
TODO
====
Add electrical composter powered by appliances mod.
Electrical composter should include support for tubes.

208
composter.lua Normal file
View File

@ -0,0 +1,208 @@
local S = composting.translator;
local amount_limit = composting.settings.amount_limit;
local produced_per_clod = composting.settings.clod_cost;
local time_divider = composting.settings.composting_time_divider;
local base_production_time = ((365*24*3600)/amount_limit)/time_divider;
local function composter_update_node(pos, node)
local meta = minetest.get_meta(pos)
local amount = meta:get_int("amount");
local produced = meta:get_int("produced");
if (amount+produced)>0 then
local C = meta:get_int("C") or 0;
local N = meta:get_int("N") or 0;
local line = math.floor(3.75*math.log(C/N) - 5 + 0.5);
if (line<0) then
line = 0;
elseif (line>15) then
line = 15;
end
local done = 0;
if (produced>0) then
done = math.floor(15*produced/(produced+amount)+0.5);
end
node.name = "composting:composter_filled";
node.param2 = line*16+done;
minetest.swap_node(pos, node);
local timer = minetest.get_node_timer(pos);
if not timer:is_started() then
timer:start(base_production_time);
end
if (produced>=produced_per_clod) then
meta:set_string("infotext", S("Compost clod."));
elseif (amount==0) then
meta:set_string("infotext", S("More biomase needed."));
else
meta:set_string("infotext", S("Composting in progress."));
end
else
node.name = "composting:composter";
minetest.set_node(pos, node);
end
end
local function composter_on_punch(pos, node, puncher, pointed_thing)
if puncher then
local wield_item = puncher:get_wielded_item();
local item_name = wield_item:get_name();
local shovel = minetest.get_item_group(item_name, "shovel");
if (shovel==0) then
-- try to add biomase
local item_def = wield_item:get_definition();
if item_def and item_def._compost then
local meta = minetest.get_meta(pos);
local amount = meta:get_int("amount");
local C = meta:get_int("C");
local N = meta:get_int("N");
amount = amount + item_def._compost.amount;
if (amount<amount_limit) then
C = C + item_def._compost.C;
N = N + item_def._compost.N;
meta:set_int("amount", amount);
meta:set_int("C", C);
meta:set_int("N", N);
composter_update_node(pos, node, meta);
wield_item:take_item();
puncher:set_wielded_item(wield_item);
end
end
else
-- try to get compost clod
local meta = minetest.get_meta(pos);
local produced = meta:get_int("produced");
if (produced>=produced_per_clod) then
local amount = meta:get_int("amount");
local C = meta:get_int("C");
local N = meta:get_int("N");
local part = produced_per_clod/(amount+produced);
meta:set_int("C", math.floor(C*part+0.5));
meta:set_int("N", math.floor(N*part+0.5));
meta:set_int("produced", produced-produced_per_clod);
local inv = puncher:get_inventory();
local clod = ItemStack("composting:compost_clod");
if (inv:room_for_item("main", clod)) then
inv:add_item("main", clod);
else
minetest.add_item(pos, clod);
end
end
end
end
end
function composter_on_timer(pos, elapsed)
local node = minetest.get_node(pos);
local meta = minetest.get_meta(pos);
local amount = meta:get_int("amount");
local C = meta:get_int("C");
local N = meta:get_int("N");
local produced = meta:get_int("produced");
if (amount>0) then
amount = amount - 1;
produced = produced + 1;
meta:set_int("amount", amount);
meta:set_int("produced", produced);
end
if C==0 then
C = 1;
end
if N==0 then
N = 1;
end
local ratio = C/N;
local speed = 1/(((amount+produced)/amount_limit)^4);
if ratio>32.5 then
speed = speed*math.exp(ratio/500);
else
speed = speed*math.exp(ratio/25);
end
if (amount>0) then
local timer = minetest.get_node_timer(pos);
timer:set(base_production_time*speed, 0);
end
composter_update_node(pos, node, meta);
return amount>0;
end
local short_desc = S("Composter");
local desc = short_desc;
local tt_help = S("Punch me with water bucket/wateringcan to make me more wet.");
if (minetest.get_modpath("tt")==nil) then
desc = desc.."\n"..tt_help;
end
local node_sounds = nil
if minetest.get_modpath("default") then
node_sounds = default.node_sound_wood_defaults();
end
if minetest.get_modpath("hades_sounds") then
node_sounds = hades_sounds.node_sound_wood_defaults();
end
if minetest.get_modpath("sounds") then
node_sounds = sounds.node_wood();
end
minetest.register_node("composting:composter", {
short_description = short_desc,
description = desc,
_tt_help = tt_help,
paramtype = "light",
groups = {cracky = 2},
is_ground_content = false,
sounds = node_sounds,
drawtype = "mesh",
mesh = "composting_composter.obj",
tiles = {
"default_wood.png"
},
on_punch = composter_on_punch,
on_construct = function (pos)
local meta = minetest.get_meta(pos);
print("on_construct: "..dump(meta:to_table()))
meta:set_int("amount", 0)
meta:set_int("N", 0)
meta:set_int("C", 0)
meta:set_int("production", 0)
end,
})
minetest.register_node("composting:composter_filled", {
description = S("Filled Composter"),
paramtype2 = "color",
groups = {cracky = 2, not_in_creative_inventory = 1},
legacy_facedir_simple = true,
is_ground_content = false,
sounds = node_sounds,
drawtype = "mesh",
mesh = "composting_composter_filled.obj",
use_texture_alpha = "blend",
tiles = {
{name="default_wood.png",color="white"},
"composting_composter_filled.png",
},
palette = "composting_composter_palette.png",
on_punch = composter_on_punch,
on_timer = composter_on_timer,
})

19
crafting.lua Normal file
View File

@ -0,0 +1,19 @@
minetest.register_craft({
output = "composting:garden_soil",
recipe = {
{"composting:compost_clod", "default:dirt", "composting:compost_clod"},
}
})
local wood = "group:wood";
minetest.register_craft({
output = "composting:composter",
recipe = {
{wood, "", wood},
{wood, "", wood},
{wood, "", wood}
}
})

8
craftitems.lua Normal file
View File

@ -0,0 +1,8 @@
local S = composting.translator
minetest.register_craftitem("composting:compost_clod", {
description = S("Compost Clod"),
inventory_image = "composting_compost_clod.png",
})

14727
data/composter.obj Normal file

File diff suppressed because it is too large Load Diff

1
data/composter.we Normal file

File diff suppressed because one or more lines are too long

8081
data/composter_fill.obj Normal file

File diff suppressed because it is too large Load Diff

1
data/composter_fill.we Normal file

File diff suppressed because one or more lines are too long

11217
data/electric_composter.obj Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

192
garden_soil.lua Normal file
View File

@ -0,0 +1,192 @@
local S = composting.translator
local time_divider = composting.settings.soil_time_divider;
composting.watering_cans = {}
if minetest.get_modpath("bucket") then
-- bucket mod
local function empty_bucket(puncher, itemstack)
return ItemStack("bucket:bucket_empty");
end
composting.watering_cans["bucket:bucket_water"] = empty_bucket;
composting.watering_cans["bucket:bucket_river_water"] = empty_bucket;
end
if minetest.get_modpath("hades_bucket") then
local function hades_empty_bucket(puncher, itemstack)
return ItemStack("hades_bucket:bucket_empty");
end
composting.watering_cans["hades_bucket:bucket_water"] = hades_empty_bucket;
composting.watering_cans["hades_bucket:bucket_river_water"] = hades_empty_bucket;
end
if minetest.get_modpath("wateringcan") then
local function empty_wateringcan(puncher, itemstack)
if puncher then
minetest.sound_play({name = "wateringcan_pour", gain = 0.25, max_hear_distance = 10}, { pos = puncher:get_pos() }, true)
end
itemstack:add_wear(2849); -- 24 uses
if (itemstack:get_count()==0) then
return ItemStack("wateringcan:wateringcan_empty");
else
return itemstack;
end
end
composting.watering_cans["wateringcan:wateringcan_water"] = empty_wateringcan;
end
local garden_soil_tiles = {"composting_garden_soil.png"};
local garden_soil_wet_tiles = {"composting_garden_soil_wet.png"};
if minetest.get_modpath("farming") then
garden_soil_tiles = {"composting_garden_soil.png^farming_soil.png", "composting_garden_soil.png"};
garden_soil_tiles = {"composting_garden_soil_wet.png^farming_soil_wet.png", "composting_garden_soil.png^farming_soil_wet_side.png"};
end
if minetest.get_modpath("hades_farming") then
garden_soil_tiles = {"composting_garden_soil.png^hades_farming_soil.png", "composting_garden_soil.png"};
garden_soil_tiles = {"composting_garden_soil_wet.png^hades_farming_soil_wet.png", "composting_garden_soil.png^hades_farming_soil_wet_side.png"};
end
-- garden soil
local function effect_of_flora(pos)
local pos = vector.add(pos, vector.new(0,1,0));
local node = minetest.get_node(pos);
local flora = minetest.get_item_group(node.name, "flora");
if (flora>0) then
return 2;
else
return 1;
end
end
local short_desc = S("Garden Soil");
local desc = short_desc;
local tt_help = S("Punch me with water bucket/wateringcan to make me wet.");
if (minetest.get_modpath("tt")==nil) then
desc = desc.."\n"..tt_help;
end
local node_sounds = nil
if minetest.get_modpath("default") then
node_sounds = default.node_sound_dirt_defaults();
end
if minetest.get_modpath("hades_sounds") then
node_sounds = hades_sounds.node_sound_dirt_defaults();
end
if minetest.get_modpath("sounds") then
node_sounds = sounds.node_dirt();
end
minetest.register_node("composting:garden_soil", {
short_description = short_desc,
description = desc,
_tt_help = tt_help,
tiles = {"composting_garden_soil.png"},
-- soil have to be 2, because farming code detect wet soil via soil value
groups = {crumbly = 3, soil = 2, grassland = 1},
drop = "default:dirt",
sounds = node_sounds,
on_construct = function(pos)
local node = minetest.get_node(pos);
node.param1 = 255;
minetest.swap_node(pos, node);
local timer = minetest.get_node_timer(pos);
timer:start((3422/time_divider)/effect_of_flora(pos));
end,
on_timer = function(pos, elapsed)
local node = minetest.get_node(pos);
if (node.param1>0) then
node.param1 = node.param1-1;
if minetest.find_node_near(pos, 3, {"group:water"}) then
node.name = "composting:garden_soil_wet";
end
minetest.swap_node(pos, node);
local timer = minetest.get_node_timer(pos);
timer:start((3422/time_divider)/effect_of_flora(pos));
return true;
else
minetest.set_node(pos, {name="farming:soil"});
end
return false;
end,
on_punch = function(pos, node, puncher)
if puncher then
local item = puncher:get_wielded_item();
local watering_can = composting.watering_cans[item:get_name()];
if watering_can then
node.name = "composting:garden_soil_wet"
node.param2 = 4;
minetest.swap_node(pos, node);
puncher:set_wielded_item(watering_can(puncher, item));
end
end
end,
})
local short_desc = S("Wet Garden Soil");
local desc = short_desc;
local tt_help = S("Punch me with water bucket/wateringcan to make me more wet.");
if (minetest.get_modpath("tt")==nil) then
desc = desc.."\n"..tt_help;
end
minetest.register_node("composting:garden_soil_wet", {
short_description = short_desc,
description = desc,
_tt_help = tt_help,
tiles = {"composting_garden_soil_wet.png"},
groups = {crumbly = 3, soil = 5, grassland = 1, wet = 1, not_in_creative_inventory = 1},
drop = "default:dirt",
sounds = node_sounds,
on_construct = function(pos)
local node = minetest.get_node(pos);
node.param1 = 255;
node.param2 = 4;
minetest.swap_node(pos, node);
local timer = minetest.get_node_timer(pos);
timer:start((1711/time_divider)/effect_of_flora(pos));
end,
on_timer = function(pos, elapsed)
local node = minetest.get_node(pos);
if (node.param1>0) then
local timer_const = 3422;
node.param1 = node.param1-1;
if minetest.find_node_near(pos, 3, {"group:water"}) then
node.param2 = 4;
end
if (node.param2>0) then
node.param2 = node.param2-1;
else
if not minetest.find_node_near(pos, 3, {"ignore"}) then
node.name = "composting:garden_soil";
timer_const = 1711;
end
end
minetest.swap_node(pos, node);
local timer = minetest.get_node_timer(pos);
timer:start((timer_const/time_divider)/effect_of_flora(pos));
return true;
elseif (node.param2>0) then
minetest.set_node(pos, {name="farming:soil_wet"});
else
minetest.set_node(pos, {name="farming:soil"});
end
return false;
end,
on_punch = function(pos, node, puncher)
if puncher then
local item = puncher:get_wielded_item();
local watering_can = composting.watering_cans[item:get_name()];
if watering_can then
node.param2 = node.param2+4;
if (node.param2>5) then
node.param2 = 5;
end
minetest.swap_node(pos, node);
puncher:set_wielded_item(watering_can(puncher, item));
end
end
end,
})

19
init.lua Normal file
View File

@ -0,0 +1,19 @@
composting = {
translator = minetest.get_translator("composting"),
}
local modpath = minetest.get_modpath(minetest.get_current_modname())
dofile(modpath.."/settings.lua")
dofile(modpath.."/composter.lua")
--dofile(modpath.."/electric_composter.lua")
dofile(modpath.."/integration.lua")
dofile(modpath.."/garden_soil.lua")
dofile(modpath.."/craftitems.lua")
dofile(modpath.."/crafting.lua")

110
integration.lua Normal file
View File

@ -0,0 +1,110 @@
function composting.add_composting_data(item_name, compost_data)
if minetest.registered_items[item_name] then
minetest.override_item(item_name, {
_compost = compost_data
})
else
minetest.log("error", "Adding composting data to item "..item_name.." failed. Item doesn't exist.");
end
end
local leaves = {};
local needles = {};
local stems = {};
local grass_1 = {};
local grass_3 = {};
local grass_5 = {};
local dry_grass_5 = {};
if minetest.get_modpath("default") then
-- leaves
table.insert(leaves, "default:bush_leaves");
table.insert(leaves, "default:acacia_bush_leaves");
table.insert(leaves, "default:blueberry_bush_leaves");
table.insert(leaves, "default:acacia_bush_leaves");
table.insert(leaves, "default:leaves");
table.insert(leaves, "default:jungleleaves");
table.insert(leaves, "default:acacia_leaves");
table.insert(leaves, "default:aspen_leaves");
-- needles
table.insert(needles, "default:pine_bush_needles");
table.insert(needles, "default:pine_needles");
-- stems
table.insert(stems, "default:dry_shrub");
table.insert(stems, "default:bush_stem");
table.insert(stems, "default:acacia_bush_stem");
table.insert(stems, "default:pine_bush_stem");
-- grass 1
table.insert(grass_1, "default:junglegrass");
-- grass 3
table.insert(grass_3, "default:fern_");
table.insert(grass_3, "default:marram_grass_");
-- grass 5
table.insert(grass_5, "default:grass_");
table.insert(dry_grass_5, "default:dry_grass_");
end
-- leaves
for _,item_name in pairs(leaves) do
composting.add_composting_data(item_name, {
amount = 15,
C = 14921, -- 190:1 branches with leaves
N = 79,
})
end
-- needles
for _,item_name in pairs(needles) do
composting.add_composting_data(item_name, {
amount = 15,
C = 14925, -- 200:1 branches with needles
N = 75,
})
end
-- stems
for _,item_name in pairs(stems) do
composting.add_composting_data(item_name, {
amount = 7,
C = 6969, -- 226:1 wood mass
N = 31,
})
end
-- grass 1
for _,item_name in pairs(stems) do
composting.add_composting_data("default:junglegrass", {
amount = 3,
C = math.floor(2833), -- 17:1
N = math.floor(167),
})
end
-- grass 3
for i=1,3 do
local part = i/3;
for _,item_name in pairs(grass_3) do
composting.add_composting_data(item_name..i, {
amount = 1+math.floor(2*part),
C = math.floor(2833*part), -- 17:1
N = math.floor(167*part),
})
end
end
-- grass 5
for i=1,5 do
local part = i/5;
for _,item_name in pairs(grass_5) do
composting.add_composting_data(item_name..i, {
amount = 1+math.floor(2*part),
C = math.floor(2833*part), -- 17:1
N = math.floor(167*part),
})
end
for _,item_name in pairs(dry_grass_5) do
composting.add_composting_data(item_name..i, {
amount = 1+math.floor(2*part),
C = math.floor(2885*part), -- 25:1
N = math.floor(115*part),
})
end
end

4
mod.conf Normal file
View File

@ -0,0 +1,4 @@
name = composting
description = Compost and composter machines
depends =
optional_depends = default, farming, bucket, hades_farming, hades_bucket, wateringcan, sounds, hades_sounds, hades_core

View File

@ -0,0 +1,12 @@
# Blender MTL File: 'None'
# Material Count: 1
newmtl default:wood
Ns 225.000000
Ka 1.000000 1.000000 1.000000
Kd 0.800000 0.800000 0.800000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.450000
d 1.000000
illum 2

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,22 @@
# Blender MTL File: 'None'
# Material Count: 2
newmtl default:dirt
Ns 225.000000
Ka 1.000000 1.000000 1.000000
Kd 0.800000 0.800000 0.800000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.450000
d 1.000000
illum 2
newmtl default:wood
Ns 225.000000
Ka 1.000000 1.000000 1.000000
Kd 0.800000 0.800000 0.800000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.450000
d 1.000000
illum 2

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,26 @@
from PIL import Image
def add_pixel(palette, color):
palette.append(color)
def add_pixels(palette, color1, color2, steps):
for n in range(steps):
#print(n)
part = n/(steps-1)
color = [0,0,0,255]
for c in range(3):
color[c] = int(color1[c]+(color2[c]-color1[c])*part)
palette.append(tuple(color))
img = Image.new(mode="RGBA",size=(16,16),color=(0,0,0,255));
#add_pixels(palette, (38,127,0,255), (48,48,48,255), 256)
#add_pixels(palette, (131,106,88,255), (38,127,0,255), 128)
#add_pixels(palette, (38,127,0,255), (48,48,48,255), 128)
help_colors = []
add_pixels(help_colors, (38,127,0,255), (131,106,88,255), 16)
palette = []
for i in range(16):
add_pixels(palette, help_colors[i], (48,48,48,255), 16)
img.putdata(palette)
img.save("palette.png")

BIN
scripts/palette.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 367 B

20
settings.lua Normal file
View File

@ -0,0 +1,20 @@
local function settings_get_int(key, default)
local value = minetest.settings:get(key);
if value then
value = tonumber(value)
else
value = default
end
return value
end
local settings = {
amount_limit = settings_get_int("composting_amount_per_composter", 200),
clod_cost = settings_get_int("composting_clod_cost", 100),
composting_time_divider = settings_get_int("composting_time_divider", 72),
soil_time_divider = settings_get_int("composting_soil_time_divider", 72),
}
composting.settings = settings;

9
settingtypes.txt Normal file
View File

@ -0,0 +1,9 @@
composting_amount_per_composter (Maximum amount of biomase per composter) int 200
composting_clod_cost (How much amount of compost cost compost clod) int 100
composting_time_divider (Time divider for composting speed) int 72
composting_soil_time_divider (Time divider for garden soil timer) int 72

Binary file not shown.

After

Width:  |  Height:  |  Size: 381 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 666 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 383 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 752 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 739 B

BIN
textures/default_dirt.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 272 B