forked from MTSR/techage_modpack
Compare commits
No commits in common. "submodules" and "mtsr_release" have entirely different histories.
submodules
...
mtsr_relea
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -1,3 +0,0 @@
|
||||
[submodule "techage"]
|
||||
path = techage
|
||||
url = https://git.luanti.ru/MTSR/techage
|
44
README.md
44
README.md
@ -46,50 +46,6 @@ ta4_jetpack requires the modpack 3d_armor. 3d_armor is itself a modpack and can'
|
||||
|
||||
### History
|
||||
|
||||
#### 2024-08-25
|
||||
|
||||
Updated Mods:
|
||||
|
||||
- techage:
|
||||
- Fix bug 'TA2 boiler work without requiring boiler'
|
||||
- Improve move/fly controller
|
||||
- Add replacement to electricmeter recipe (Eternal-Study)
|
||||
- Fix injector bug
|
||||
- Fix flowers detection when using ethereal (Niklp09)
|
||||
- Fix issue #175 (TA3 Furnace Does not implement replacements in recipes)
|
||||
- Water to Salt + River Water Recipe, add Salt.lua (Eternal-Study)
|
||||
- Add new button commands
|
||||
- Fix bug with injector with a full 8x2000 chest
|
||||
- Fix forceload block formspec list (Niklp09)
|
||||
- Improve techage:ta4_power_box node_box (Niklp09)
|
||||
- Allow to move the TA4 terminal with the assembly tool (#165)
|
||||
- add aluminum recipes for techpack_stairway items (jfanjoy)
|
||||
- Replace techage_invisible.png w/ engine provided blank.png (Niklp09)
|
||||
- adds a new chat command and column to forceload formspec (jfanjoy)
|
||||
- Fix bug #24 (Energy Storage respawn red gravel infinit)
|
||||
- Allow TA3 screwdriver to get repaired by anvil (Niklp09)
|
||||
- Add translations and fix bug with growlight
|
||||
- Add manual for pt-BR language (hephaestus-br)
|
||||
- Add reverse mode for ta5 pump
|
||||
- Fix assembly tool bug with non-empty chests
|
||||
- hyperloop:
|
||||
- add luacheck workflow
|
||||
- Change code so that no luacheck warnings are issued
|
||||
- Fix set_look_horizontal bug
|
||||
- Prevent 'deprecated warnings'
|
||||
- signs_bot:
|
||||
- Fix to Fall_Down when non-walkable nodes are in path (Eternal-Study)
|
||||
- Fix to fall_down (Eternal-Study)
|
||||
- hold aux1 to invert signal (blaboing)
|
||||
- Fix flowers detection when using ethereal (Niklp09)
|
||||
- Fix bug #38 (Colon in print command breaks jumps)
|
||||
- tubelib2:
|
||||
- Replace deprecated meta:set_string(*, nil) calls (Niklp09)
|
||||
- lcdlib:
|
||||
- Use initial_properties for text entity (Niklp09)
|
||||
- safer_lua:
|
||||
- Add safe variants for strin.rep() and string.find()
|
||||
|
||||
#### 2023-11-26
|
||||
|
||||
Updated Mods:
|
||||
|
@ -3,4 +3,3 @@ title=Autobahn
|
||||
description=Street mod for faster travelling.
|
||||
depends=default
|
||||
optional_depends=moreblocks, techage, minecart, player_monoids
|
||||
supported_games = minetest_game
|
||||
|
3
basic_materials/.gitmodules
vendored
Normal file
3
basic_materials/.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[submodule "sound_api_core"]
|
||||
path = sound_api_core
|
||||
url = https://github.com/mt-mods/sound_api_core.git
|
@ -3,5 +3,5 @@ globals = {
|
||||
}
|
||||
|
||||
read_globals = {
|
||||
"default", "xcompat",
|
||||
"default",
|
||||
}
|
@ -21,3 +21,10 @@ mod that adds basic material nodes and items
|
||||
|
||||
* `git clone https://github.com/mt-mods/basic_materials.git`
|
||||
* `cd basic_materials`
|
||||
* `git submodule init`
|
||||
* `git submodule update`
|
||||
|
||||
to update please use the following commands starting inside the mod directory
|
||||
|
||||
* `git submodule sync`
|
||||
* `git submodule update`
|
@ -1,4 +1,136 @@
|
||||
local materials = xcompat.materials
|
||||
local materials = {
|
||||
dirt = "default:dirt",
|
||||
sand = "default:sand",
|
||||
gravel = "default:gravel",
|
||||
copper_ingot = "default:copper_ingot",
|
||||
steel_ingot = "default:steel_ingot",
|
||||
gold_ingot = "default:gold_ingot",
|
||||
tin_ingot = "default:tin_ingot",
|
||||
mese_crystal_fragment = "default:mese_crystal_fragment",
|
||||
torch = "default:torch",
|
||||
diamond = "default:diamond",
|
||||
clay_lump = "default:clay_lump",
|
||||
water_bucket = "bucket:bucket_water",
|
||||
empty_bucket = "bucket:bucket_empty",
|
||||
dye_dark_grey = "dye:dark_grey",
|
||||
silicon = "mesecons_materials:silicon",
|
||||
}
|
||||
|
||||
if minetest.get_modpath("moreores") then
|
||||
materials.silver_ingot = "moreores:silver_ingot"
|
||||
end
|
||||
|
||||
if minetest.get_modpath("technic") then
|
||||
materials.lead_ingot = "technic:lead_ingot"
|
||||
materials.carbon_steel_ingot = "technic:carbon_steel_ingot"
|
||||
materials.stainless_steel_ingot = "technic:stainless_steel_ingot"
|
||||
end
|
||||
|
||||
if minetest.get_modpath("aloz") then
|
||||
materials.aluminum_ingot = "aloz:aluminum_ingot"
|
||||
end
|
||||
|
||||
if minetest.get_modpath("techage") then
|
||||
materials.aluminum_ingot = "techage:aluminum"
|
||||
end
|
||||
|
||||
if minetest.get_modpath("mcl_core") then
|
||||
materials = {
|
||||
dirt = "mcl_core:dirt",
|
||||
sand = "mcl_core:sand",
|
||||
gravel = "mcl_core:gravel",
|
||||
steel_ingot = "mcl_core:iron_ingot",
|
||||
gold_ingot = "mcl_core:gold_ingot",
|
||||
mese_crystal_fragment = "mesecons:redstone",
|
||||
torch = "mcl_torches:torch",
|
||||
diamond = "mcl_core:diamond",
|
||||
clay_lump = "mcl_core:clay_lump",
|
||||
water_bucket = "mcl_buckets:bucket_water",
|
||||
empty_bucket = "mcl_buckets:bucket_empty",
|
||||
dye_dark_grey = "mcl_dye:dark_grey",
|
||||
-- Use iron where no equivalent
|
||||
copper_ingot = "mcl_core:iron_ingot",
|
||||
tin_ingot = "mcl_core:iron_ingot",
|
||||
silver_ingot = "mcl_core:iron_ingot",
|
||||
silicon = "mesecons_materials:silicon",
|
||||
}
|
||||
elseif minetest.get_modpath("fl_ores") and minetest.get_modpath("fl_stone") then
|
||||
materials = {
|
||||
dirt = "fl_topsoil:dirt",
|
||||
sand = "fl_stone:sand",
|
||||
gravel = "fl_topsoil:gravel",
|
||||
steel_ingot = "fl_ores:iron_ingot",
|
||||
gold_ingot = "fl_ores:gold_ingot",
|
||||
mese_crystal_fragment = "fl_ores:iron_ingot",
|
||||
torch = "fl_light_sources:torch",
|
||||
diamond = "fl_ores:diamond",
|
||||
clay_lump = "fl_bricks:clay_lump",
|
||||
water_bucket = "fl_bucket:bucket_water",
|
||||
empty_bucket = "fl_bucket:bucket",
|
||||
dye_dark_grey = "fl_dyes:dark_grey_dye",
|
||||
copper_ingot = "fl_ores:copper_ingot",
|
||||
tin_ingot = "fl_ores:tin_ingot",
|
||||
silver_ingot = "fl_ores:iron_ingot",
|
||||
silicon = "mesecons_materials:silicon",
|
||||
}
|
||||
elseif minetest.get_modpath("rp_default") then
|
||||
materials = {
|
||||
dirt = "rp_default:dirt",
|
||||
sand = "rp_default:sand",
|
||||
gravel = "rp_default:gravel",
|
||||
steel_ingot = "rp_default:ingot_steel",
|
||||
gold_ingot = "rp_default:ingot_gold",
|
||||
mese_crystal_fragment = "rp_default:ingot_steel",
|
||||
torch = "rp_default:torch",
|
||||
diamond = "rp_default:pearl",
|
||||
clay_lump = "rp_default:ingot_steel",
|
||||
water_bucket = "rp_default:swamp_dirt",
|
||||
empty_bucket = "rp_default:dirt",
|
||||
dye_dark_grey = "rp_default:ingot_steel",
|
||||
copper_ingot = "rp_default:ingot_copper",
|
||||
tin_ingot = "rp_default:ingot_tin",
|
||||
silver_ingot = "rp_default:ingot_steel",
|
||||
silicon = "rp_default:ingot_steel",
|
||||
}
|
||||
elseif minetest.get_modpath("hades_core") then
|
||||
materials = {
|
||||
dirt = "hades_core:dirt",
|
||||
sand = "hades_core:fertile_sand",
|
||||
gravel = "hades_core:gravel",
|
||||
steel_ingot = "hades_core:steel_ingot",
|
||||
gold_ingot = "hades_core:gold_ingot",
|
||||
mese_crystal_fragment = "hades_core:mese_crystal_fragment",
|
||||
torch = "hades_torches:torch",
|
||||
diamond = "hades_core:diamond",
|
||||
clay_lump = "hades_core:clay_lump",
|
||||
dye_dark_grey = "hades_dye:dark_grey",
|
||||
copper_ingot = "hades_core:copper_ingot",
|
||||
tin_ingot = "hades_core:tin_ingot",
|
||||
--[[
|
||||
Since hades doesnt have buckets or water for the user,
|
||||
using dirt from near water to pull the water out
|
||||
]]
|
||||
water_bucket = "hades_core:dirt",
|
||||
empty_bucket = "hades_core:fertile_sand",
|
||||
-- Set this to steel unless hadesextraores is present
|
||||
silver_ingot = "hades_core:steel_ingot",
|
||||
silicon = "hades_materials:silicon",
|
||||
}
|
||||
|
||||
if minetest.get_modpath("hades_bucket") then
|
||||
materials["water_bucket"] = "hades_bucket:bucket_water"
|
||||
materials["empty_bucket"] = "hades_bucket:bucket_empty"
|
||||
end
|
||||
if minetest.get_modpath("hades_extraores") then
|
||||
materials["silver_ingot"] = "hades_extraores:silver_ingot"
|
||||
materials["aluminum_ingot"] = "hades_extraores:aluminum_ingot"
|
||||
end
|
||||
if minetest.get_modpath("hades_technic") then
|
||||
materials.lead_ingot = "hades_technic:lead_ingot"
|
||||
materials.carbon_steel_ingot = "hades_technic:carbon_steel_ingot"
|
||||
materials.stainless_steel_ingot = "hades_technic:stainless_steel_ingot"
|
||||
end
|
||||
end
|
||||
|
||||
local have_hades_materials = minetest.get_modpath("hades_materials")
|
||||
|
||||
@ -392,7 +524,7 @@ register_craft({
|
||||
|
||||
if not have_hades_materials then
|
||||
register_craft( {
|
||||
output = "basic_materials:silicon 4",
|
||||
output = materials.silicon.." 4",
|
||||
recipe = {
|
||||
{materials.sand, materials.sand},
|
||||
{materials.sand, materials.steel_ingot},
|
||||
@ -403,8 +535,8 @@ end
|
||||
register_craft( {
|
||||
output = "basic_materials:ic 4",
|
||||
recipe = {
|
||||
{"basic_materials:silicon", "basic_materials:silicon"},
|
||||
{"basic_materials:silicon", materials.copper_ingot},
|
||||
{materials.silicon, materials.silicon},
|
||||
{materials.silicon, materials.copper_ingot},
|
||||
},
|
||||
})
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
name = basic_materials
|
||||
depends = xcompat
|
||||
optional_depends = moreores, default, mesecons_materials, dye, bucket, fl_stone, fl_trees, mcl_sounds, hades_core, hades_sounds, hades_materials, hades_dye, hades_bucket, hades_extraores, hades_mesecons_materials, aloz, rp_crafting, mcl_core, mcl_copper
|
||||
optional_depends = moreores, default, mesecons_materials, dye, bucket, fl_stone, fl_trees, mcl_sounds, hades_core, hades_sounds, hades_materials, hades_dye, hades_bucket, hades_extraores, hades_mesecons_materials, aloz, rp_crafting
|
||||
min_minetest_version = 5.2.0
|
||||
|
@ -1,11 +1,11 @@
|
||||
local S = minetest.get_translator("basic_materials")
|
||||
local sound_api = xcompat.sounds
|
||||
local sound_api = dofile(basic_materials.modpath .. "/sound_api_core/init.lua")
|
||||
local chains_sbox = {type = "fixed",fixed = { -0.1, -0.5, -0.1, 0.1, 0.5, 0.1 }}
|
||||
|
||||
minetest.register_node("basic_materials:cement_block", {
|
||||
description = S("Cement"),
|
||||
tiles = {"basic_materials_cement_block.png"},
|
||||
is_ground_content = false,
|
||||
is_ground_content = true,
|
||||
groups = {cracky=2, dig_stone = 1, pickaxey=5},
|
||||
_mcl_hardness=1.6,
|
||||
sounds = sound_api.node_sound_stone_defaults(),
|
||||
@ -14,7 +14,6 @@ minetest.register_node("basic_materials:cement_block", {
|
||||
minetest.register_node("basic_materials:concrete_block", {
|
||||
description = S("Concrete Block"),
|
||||
tiles = {"basic_materials_concrete_block.png",},
|
||||
is_ground_content = false,
|
||||
groups = {cracky=1, concrete=1, dig_stone = 1, pickaxey=5},
|
||||
_mcl_hardness=1.6,
|
||||
sounds = sound_api.node_sound_stone_defaults(),
|
||||
@ -30,7 +29,6 @@ minetest.register_node("basic_materials:chain_steel", {
|
||||
sunlight_propagates = true,
|
||||
paramtype = "light",
|
||||
inventory_image = "basic_materials_chain_steel_inv.png",
|
||||
is_ground_content = false,
|
||||
groups = {cracky=3, dig_stone = 1, pickaxey=5},
|
||||
_mcl_hardness=1.6,
|
||||
selection_box = chains_sbox,
|
||||
@ -46,7 +44,6 @@ minetest.register_node("basic_materials:chain_brass", {
|
||||
sunlight_propagates = true,
|
||||
paramtype = "light",
|
||||
inventory_image = "basic_materials_chain_brass_inv.png",
|
||||
is_ground_content = false,
|
||||
groups = {cracky=3, dig_stone = 1, pickaxey=5},
|
||||
_mcl_hardness=1.6,
|
||||
selection_box = chains_sbox,
|
||||
|
5
basic_materials/sound_api_core/.luacheckrc
Normal file
5
basic_materials/sound_api_core/.luacheckrc
Normal file
@ -0,0 +1,5 @@
|
||||
read_globals = {
|
||||
"minetest", "mcl_sounds", "default", "ks_sounds",
|
||||
"nodes_nature", "fl_stone", "fl_topsoil", "fl_trees",
|
||||
"hades_sounds",
|
||||
}
|
19
basic_materials/sound_api_core/LICENSE
Normal file
19
basic_materials/sound_api_core/LICENSE
Normal file
@ -0,0 +1,19 @@
|
||||
|
||||
MIT Copyright 2021 wsor4035
|
||||
|
||||
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.
|
276
basic_materials/sound_api_core/init.lua
Normal file
276
basic_materials/sound_api_core/init.lua
Normal file
@ -0,0 +1,276 @@
|
||||
local sound_api = {}
|
||||
|
||||
--convert some games for api usage
|
||||
|
||||
--ks_sounds conversion
|
||||
--currently loggy and bedrock are ignored
|
||||
local ks = {}
|
||||
|
||||
function ks.node_sound_defaults(table)
|
||||
table = table or {}
|
||||
table.footstep = table.footstep or ks_sounds.generalnode_sounds.footstep
|
||||
table.dug = table.dug or ks_sounds.generalnode_sounds.dug
|
||||
table.dig = table.dig or ks_sounds.generalnode_sounds.dig
|
||||
table.place = table.place or ks_sounds.generalnode_sounds.place
|
||||
return table
|
||||
end
|
||||
|
||||
function ks.node_sound_wood_defaults(table)
|
||||
table = table or {}
|
||||
table.footstep = table.footstep or ks_sounds.woodennode_sounds.footstep
|
||||
table.dug = table.dug or ks_sounds.woodennode_sounds.dug
|
||||
table.dig = table.dig or ks_sounds.woodennode_sounds.dig
|
||||
table.place = table.place or ks_sounds.woodennode_sounds.place
|
||||
ks.node_sound_defaults(table)
|
||||
return table
|
||||
end
|
||||
|
||||
function ks.node_sound_leaves_defaults(table)
|
||||
table = table or {}
|
||||
table.footstep = table.footstep or ks_sounds.leafynode_sounds.footstep
|
||||
table.dug = table.dug or ks_sounds.leafynode_sounds.dug
|
||||
table.dig = table.dig or ks_sounds.leafynode_sounds.dig
|
||||
table.place = table.place or ks_sounds.leafynode_sounds.place
|
||||
ks.node_sound_defaults(table)
|
||||
return table
|
||||
end
|
||||
|
||||
function ks.node_sound_snow_defaults(table)
|
||||
table = table or {}
|
||||
table.footstep = table.footstep or ks_sounds.snowynode_sounds.footstep
|
||||
table.dug = table.dug or ks_sounds.snowynode_sounds.dug
|
||||
table.dig = table.dig or ks_sounds.snowynode_sounds.dig
|
||||
table.place = table.place or ks_sounds.snowynode_sounds.place
|
||||
ks.node_sound_defaults(table)
|
||||
return table
|
||||
end
|
||||
|
||||
|
||||
--api
|
||||
function sound_api.node_sound_default(table)
|
||||
if minetest.get_modpath("default") then
|
||||
return default.node_sound_defaults(table)
|
||||
elseif minetest.get_modpath("mcl_sounds") then
|
||||
return mcl_sounds.node_sound_defaults(table)
|
||||
elseif minetest.get_modpath("ks_sounds") then
|
||||
return ks.node_sound_default(table)
|
||||
elseif minetest.get_modpath("nodes_nature") then
|
||||
return nodes_nature.node_sound_default(table)
|
||||
elseif minetest.get_modpath("hades_sounds") then
|
||||
return hades_sounds.node_sound_defaults(table)
|
||||
else
|
||||
return table
|
||||
end
|
||||
end
|
||||
|
||||
function sound_api.node_sound_stone_defaults(table)
|
||||
if minetest.get_modpath("default") then
|
||||
return default.node_sound_stone_defaults(table)
|
||||
elseif minetest.get_modpath("mcl_sounds") then
|
||||
return mcl_sounds.node_sound_stone_defaults(table)
|
||||
elseif minetest.get_modpath("nodes_nature") then
|
||||
return nodes_nature.node_sound_stone_defaults(table)
|
||||
elseif minetest.get_modpath("fl_stone") then
|
||||
return fl_stone.sounds.stone(table)
|
||||
elseif minetest.get_modpath("hades_sounds") then
|
||||
return hades_sounds.node_sound_stone_defaults(table)
|
||||
else
|
||||
return table
|
||||
end
|
||||
end
|
||||
|
||||
function sound_api.node_sound_dirt_defaults(table)
|
||||
if minetest.get_modpath("default") then
|
||||
return default.node_sound_dirt_defaults(table)
|
||||
elseif minetest.get_modpath("mcl_sounds") then
|
||||
return mcl_sounds.node_sound_dirt_defaults(table)
|
||||
elseif minetest.get_modpath("nodes_nature") then
|
||||
return nodes_nature.node_sound_dirt_defaults(table)
|
||||
--s/dirt/grass
|
||||
elseif minetest.get_modpath("fl_topsoil") then
|
||||
return fl_topsoil.sounds.grass(table)
|
||||
elseif minetest.get_modpath("hades_sounds") then
|
||||
return hades_sounds.node_sound_dirt_defaults(table)
|
||||
else
|
||||
return table
|
||||
end
|
||||
end
|
||||
|
||||
--return dirt as some games use dirt vs grass
|
||||
function sound_api.node_sound_grass_defaults(table)
|
||||
if minetest.get_modpath("hades_sounds") then
|
||||
return hades_sounds.node_sound_grass_defaults(table)
|
||||
else
|
||||
return sound_api.node_sound_dirt_defaults(table)
|
||||
end
|
||||
end
|
||||
|
||||
function sound_api.node_sound_sand_defaults(table)
|
||||
if minetest.get_modpath("default") then
|
||||
return default.node_sound_sand_defaults(table)
|
||||
elseif minetest.get_modpath("mcl_sounds") then
|
||||
return mcl_sounds.node_sound_sand_defaults(table)
|
||||
elseif minetest.get_modpath("nodes_nature") then
|
||||
return nodes_nature.node_sound_sand_defaults(table)
|
||||
elseif minetest.get_modpath("fl_stone") then
|
||||
return fl_stone.sounds.sand(table)
|
||||
elseif minetest.get_modpath("hades_sounds") then
|
||||
return hades_sounds.node_sound_sand_defaults(table)
|
||||
else
|
||||
return table
|
||||
end
|
||||
end
|
||||
|
||||
function sound_api.node_sound_gravel_defaults(table)
|
||||
if minetest.get_modpath("default") then
|
||||
return default.node_sound_gravel_defaults(table)
|
||||
--s/gravel/sand
|
||||
elseif minetest.get_modpath("mcl_sounds") then
|
||||
return mcl_sounds.node_sound_sand_defaults(table)
|
||||
elseif minetest.get_modpath("nodes_nature") then
|
||||
return nodes_nature.node_sound_gravel_defaults(table)
|
||||
elseif minetest.get_modpath("fl_topsoil") then
|
||||
return fl_topsoil.sounds.gravel(table)
|
||||
elseif minetest.get_modpath("hades_sounds") then
|
||||
return hades_sounds.node_sound_gravel_defaults(table)
|
||||
else
|
||||
return table
|
||||
end
|
||||
end
|
||||
|
||||
function sound_api.node_sound_wood_defaults(table)
|
||||
if minetest.get_modpath("default") then
|
||||
return default.node_sound_wood_defaults(table)
|
||||
elseif minetest.get_modpath("mcl_sounds") then
|
||||
return mcl_sounds.node_sound_wood_defaults(table)
|
||||
elseif minetest.get_modpath("ks_sounds") then
|
||||
return ks.node_sound_wood_default(table)
|
||||
elseif minetest.get_modpath("nodes_nature") then
|
||||
return nodes_nature.node_sound_wood_defaults(table)
|
||||
elseif minetest.get_modpath("fl_trees") then
|
||||
return fl_trees.sounds.wood(table)
|
||||
elseif minetest.get_modpath("hades_sounds") then
|
||||
return hades_sounds.node_sound_wood_defaults(table)
|
||||
else
|
||||
return table
|
||||
end
|
||||
end
|
||||
|
||||
function sound_api.node_sound_leaves_defaults(table)
|
||||
if minetest.get_modpath("default") then
|
||||
return default.node_sound_leaves_defaults(table)
|
||||
elseif minetest.get_modpath("mcl_sounds") then
|
||||
return mcl_sounds.node_sound_leaves_defaults(table)
|
||||
elseif minetest.get_modpath("ks_sounds") then
|
||||
return ks.node_sound_leaves_default(table)
|
||||
elseif minetest.get_modpath("nodes_nature") then
|
||||
return nodes_nature.node_sound_leaves_defaults(table)
|
||||
elseif minetest.get_modpath("hades_sounds") then
|
||||
return hades_sounds.node_sound_leaves_defaults(table)
|
||||
else
|
||||
return table
|
||||
end
|
||||
end
|
||||
|
||||
function sound_api.node_sound_glass_defaults(table)
|
||||
if minetest.get_modpath("default") then
|
||||
return default.node_sound_glass_defaults(table)
|
||||
elseif minetest.get_modpath("mcl_sounds") then
|
||||
return mcl_sounds.node_sound_glass_defaults(table)
|
||||
elseif minetest.get_modpath("nodes_nature") then
|
||||
return nodes_nature.node_sound_glass_defaults(table)
|
||||
elseif minetest.get_modpath("hades_sounds") then
|
||||
return hades_sounds.node_sound_glass_defaults(table)
|
||||
else
|
||||
return table
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function sound_api.node_sound_ice_defaults(table)
|
||||
if minetest.get_modpath("default") then
|
||||
return default.node_sound_ice_defaults(table)
|
||||
--s/ice/glass
|
||||
elseif minetest.get_modpath("mcl_sounds") then
|
||||
return mcl_sounds.node_sound_glass_defaults(table)
|
||||
--s/ice/glass
|
||||
elseif minetest.get_modpath("nodes_nature") then
|
||||
return nodes_nature.node_sound_glass_defaults(table)
|
||||
--s/ice/glass
|
||||
elseif minetest.get_modpath("hades_sounds") then
|
||||
return hades_sounds.node_sound_glass_defaults(table)
|
||||
else
|
||||
return table
|
||||
end
|
||||
end
|
||||
|
||||
function sound_api.node_sound_metal_defaults(table)
|
||||
if minetest.get_modpath("default") then
|
||||
return default.node_sound_metal_defaults(table)
|
||||
elseif minetest.get_modpath("mcl_sounds") then
|
||||
return mcl_sounds.node_sound_metal_defaults(table)
|
||||
elseif minetest.get_modpath("hades_sounds") then
|
||||
return hades_sounds.node_sound_metal_defaults(table)
|
||||
else
|
||||
return table
|
||||
end
|
||||
end
|
||||
|
||||
function sound_api.node_sound_water_defaults(table)
|
||||
if minetest.get_modpath("default") then
|
||||
return default.node_sound_water_defaults(table)
|
||||
elseif minetest.get_modpath("mcl_sounds") then
|
||||
return mcl_sounds.node_sound_water_defaults(table)
|
||||
elseif minetest.get_modpath("nodes_nature") then
|
||||
return nodes_nature.node_sound_water_defaults(table)
|
||||
elseif minetest.get_modpath("hades_sounds") then
|
||||
return hades_sounds.node_sound_water_defaults(table)
|
||||
else
|
||||
return table
|
||||
end
|
||||
end
|
||||
|
||||
function sound_api.node_sound_lava_defaults(table)
|
||||
--s/lava/water
|
||||
if minetest.get_modpath("default") then
|
||||
return default.node_sound_water_defaults(table)
|
||||
elseif minetest.get_modpath("mcl_sounds") then
|
||||
return mcl_sounds.node_sound_lava_defaults(table)
|
||||
--s/lava/water
|
||||
elseif minetest.get_modpath("nodes_nature") then
|
||||
return nodes_nature.node_sound_water_defaults(table)
|
||||
elseif minetest.get_modpath("hades_sounds") then
|
||||
return hades_sounds.node_sound_lava_defaults(table)
|
||||
else
|
||||
return table
|
||||
end
|
||||
end
|
||||
|
||||
function sound_api.node_sound_snow_defaults(table)
|
||||
if minetest.get_modpath("default") then
|
||||
return default.node_sound_snow_defaults(table)
|
||||
elseif minetest.get_modpath("mcl_sounds") then
|
||||
return mcl_sounds.node_sound_snow_defaults(table)
|
||||
elseif minetest.get_modpath("ks_sounds") then
|
||||
return ks.node_sound_snow_default(table)
|
||||
elseif minetest.get_modpath("nodes_nature") then
|
||||
return nodes_nature.node_sound_snow_defaults(table)
|
||||
elseif minetest.get_modpath("fl_topsoil") then
|
||||
return fl_topsoil.sounds.snow(table)
|
||||
else
|
||||
return table
|
||||
end
|
||||
end
|
||||
|
||||
function sound_api.node_sound_wool_defaults(table)
|
||||
--s/wool/default
|
||||
if minetest.get_modpath("default") then
|
||||
return default.node_sound_defaults(table)
|
||||
elseif minetest.get_modpath("mcl_sounds") then
|
||||
return mcl_sounds.node_sound_wool_defaults(table)
|
||||
else
|
||||
return table
|
||||
end
|
||||
end
|
||||
|
||||
return sound_api
|
29
grep.py
29
grep.py
@ -1,29 +0,0 @@
|
||||
#!/usr/bin/env python2
|
||||
# -*- coding: iso-8859-1 -*-
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
def grep(pattern, path='./', endings=['.lua']):
|
||||
"""
|
||||
Search in all files with the ending 'endings' in the given 'path'
|
||||
for the given text 'pattern'.
|
||||
"""
|
||||
lOut = []
|
||||
for dirpath, dirnames, filenames in os.walk(path):
|
||||
for name in filenames:
|
||||
_, ext = os.path.splitext(name)
|
||||
if ext in endings:
|
||||
filename = os.path.join(dirpath, name)
|
||||
i = 0
|
||||
## line oriented approach
|
||||
for line in file(filename).readlines():
|
||||
i = i + 1
|
||||
match = re.search(pattern, line) # search pattern in line
|
||||
if match:
|
||||
print filename + ' [' + str(i) + '] ' + line.strip()
|
||||
return lOut
|
||||
|
||||
if __name__ == '__main__':
|
||||
grep(sys.argv[1])
|
10
hyperloop/.github/workflows/luacheck.yml
vendored
10
hyperloop/.github/workflows/luacheck.yml
vendored
@ -1,10 +0,0 @@
|
||||
name: luacheck
|
||||
on: [push, pull_request]
|
||||
jobs:
|
||||
luacheck:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@main
|
||||
- name: Luacheck
|
||||
uses: lunarmodules/luacheck@master
|
@ -1,28 +0,0 @@
|
||||
unused_args = false
|
||||
|
||||
ignore = {
|
||||
"131", -- Unused global variable
|
||||
"432", -- Shadowing an upvalue argument
|
||||
}
|
||||
|
||||
read_globals = {
|
||||
"core",
|
||||
"minetest",
|
||||
"default",
|
||||
"worldedit",
|
||||
"tubelib2",
|
||||
"intllib",
|
||||
"DIR_DELIM",
|
||||
"techage",
|
||||
|
||||
string = {fields = {"split", "trim"}},
|
||||
vector = {fields = {"add", "equals", "multiply"}},
|
||||
table = {fields = {"copy", ""}},
|
||||
}
|
||||
|
||||
globals = {
|
||||
"hyperloop",
|
||||
"ItemStack",
|
||||
"screwdriver",
|
||||
}
|
||||
|
@ -13,11 +13,11 @@
|
||||
|
||||
-- for lazy programmers
|
||||
local SP = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
--local P = minetest.string_to_pos
|
||||
--local M = minetest.get_meta
|
||||
local P = minetest.string_to_pos
|
||||
local M = minetest.get_meta
|
||||
|
||||
local S = hyperloop.S
|
||||
--local NS = hyperloop.NS
|
||||
local NS = hyperloop.NS
|
||||
|
||||
local tBlockingTime = {}
|
||||
local tBookings = {} -- open bookings: tBookings[SP(departure_pos)] = arrival_pos
|
||||
|
@ -18,6 +18,7 @@ local M = minetest.get_meta
|
||||
|
||||
-- Load support for intllib.
|
||||
local S = hyperloop.S
|
||||
local NS = hyperloop.NS
|
||||
|
||||
-- Used to store the Station list for each booking machine:
|
||||
-- tStationList[SP(pos)] = {pos1, pos2, ...}
|
||||
@ -94,6 +95,7 @@ end
|
||||
-- Used to update the station list for booking machine
|
||||
-- and teleport list.
|
||||
local function station_list_as_string(pos, subnet)
|
||||
local meta = M(pos)
|
||||
-- Generate a name sorted list of all connected stations
|
||||
local sortedList = Stations:station_list(pos, pos, "name")
|
||||
-- remove all junctions from the list
|
||||
@ -106,7 +108,7 @@ local function station_list_as_string(pos, subnet)
|
||||
return generate_string(sortedList)
|
||||
end
|
||||
|
||||
local naming_formspec
|
||||
local naming_formspec = nil
|
||||
|
||||
if hyperloop.subnet_enabled then
|
||||
naming_formspec = function(pos)
|
||||
|
@ -12,12 +12,30 @@
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
--local P = minetest.string_to_pos
|
||||
--local M = minetest.get_meta
|
||||
local S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local P = minetest.string_to_pos
|
||||
local M = minetest.get_meta
|
||||
|
||||
hyperloop.Stations = hyperloop.Network:new()
|
||||
hyperloop.Elevators = hyperloop.Network:new()
|
||||
|
||||
|
||||
-- Check all nodes on the map and delete useless data base entries
|
||||
local function check_data_base()
|
||||
-- used for VM get_node
|
||||
local tube = tubelib2.Tube:new({})
|
||||
|
||||
hyperloop.Stations:filter(function(pos)
|
||||
local _,node = tube:get_node(pos)
|
||||
return node.name == "hyperloop:station" or node.name == "hyperloop:junction"
|
||||
end)
|
||||
|
||||
hyperloop.Elevators:filter(function(pos)
|
||||
local _,node = tube:get_node(pos)
|
||||
return node.name == "hyperloop:elevator_bottom"
|
||||
end)
|
||||
end
|
||||
|
||||
local storage = minetest.get_mod_storage()
|
||||
hyperloop.Stations:deserialize(storage:get_string("Stations"))
|
||||
hyperloop.Elevators:deserialize(storage:get_string("Elevators"))
|
||||
|
@ -15,12 +15,13 @@
|
||||
|
||||
-- Load support for intllib.
|
||||
local S = hyperloop.S
|
||||
local NS = hyperloop.NS
|
||||
|
||||
local tilesL = {"hyperloop_alpsL.png", "hyperloop_seaL.png", "hyperloop_agyptL.png"}
|
||||
local tilesR = {"hyperloop_alpsR.png", "hyperloop_seaR.png", "hyperloop_agyptR.png"}
|
||||
|
||||
-- determine facedir and pos on the right hand side from the given pos
|
||||
local function right_hand_side(pos, placer)
|
||||
function right_hand_side(pos, placer)
|
||||
local facedir = hyperloop.get_facedir(placer)
|
||||
pos = hyperloop.new_pos(pos, facedir, "1R", 0)
|
||||
return facedir,pos
|
||||
|
@ -11,12 +11,13 @@
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
--local SP = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
--local P = minetest.string_to_pos
|
||||
local SP = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local P = minetest.string_to_pos
|
||||
local M = minetest.get_meta
|
||||
|
||||
--- Load support for intllib.
|
||||
local S = hyperloop.S
|
||||
local NS = hyperloop.NS
|
||||
|
||||
-- Open the door for an emergency
|
||||
local function door_on_punch(pos, node, puncher, pointed_thing)
|
||||
@ -39,6 +40,7 @@ local function door_command(door_pos1, facedir, cmnd)
|
||||
|
||||
local node1 = minetest.get_node(door_pos1)
|
||||
local node2 = minetest.get_node(door_pos2)
|
||||
local meta = minetest.get_meta(door_pos1)
|
||||
if cmnd == "open" then
|
||||
minetest.sound_play("door", {
|
||||
pos = door_pos1,
|
||||
|
@ -17,6 +17,7 @@ local M = minetest.get_meta
|
||||
|
||||
-- Load support for intllib.
|
||||
local S = hyperloop.S
|
||||
local NS = hyperloop.NS
|
||||
|
||||
-- To store elevator floors and formspecs
|
||||
local Cache = {}
|
||||
@ -64,8 +65,8 @@ Shaft:register_on_tube_update(function(node, pos, out_dir, peer_pos, peer_in_dir
|
||||
-- switch to elevator_bottom node
|
||||
pos = Shaft:get_pos(pos, 5)
|
||||
elseif peer_pos then
|
||||
local _,node1 = Shaft:get_node(peer_pos)
|
||||
if node1.name == "hyperloop:elevator_top" then
|
||||
local _,node = Shaft:get_node(peer_pos)
|
||||
if node.name == "hyperloop:elevator_top" then
|
||||
peer_pos = Shaft:get_pos(peer_pos, 5)
|
||||
end
|
||||
end
|
||||
@ -462,11 +463,11 @@ minetest.register_node("hyperloop:elevator_bottom", {
|
||||
|
||||
-- formspec
|
||||
local meta = minetest.get_meta(pos)
|
||||
local fs = "size[6,4]"..
|
||||
local formspec = "size[6,4]"..
|
||||
"label[0,0;"..S("Please insert floor name").."]" ..
|
||||
"field[0.5,1.5;5,1;floor;"..S("Floor name")..";"..S("Base").."]" ..
|
||||
"button_exit[2,3;2,1;exit;"..S("Save").."]"
|
||||
meta:set_string("formspec", fs)
|
||||
meta:set_string("formspec", formspec)
|
||||
meta:set_string("owner", placer:get_player_name())
|
||||
|
||||
-- add upper part of the car
|
||||
|
@ -11,12 +11,13 @@
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
--local SP = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
--local P = minetest.string_to_pos
|
||||
local SP = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local P = minetest.string_to_pos
|
||||
local M = minetest.get_meta
|
||||
|
||||
-- Load support for intllib.
|
||||
local S = hyperloop.S
|
||||
local NS = hyperloop.NS
|
||||
|
||||
local Tube = hyperloop.Tube
|
||||
local Stations = hyperloop.Stations
|
||||
|
@ -12,6 +12,8 @@
|
||||
|
||||
-- Load support for intllib.
|
||||
local S = hyperloop.S
|
||||
local NS = hyperloop.NS
|
||||
|
||||
|
||||
-- load characters map
|
||||
local chars_file = io.open(minetest.get_modpath("hyperloop").."/characters.data", "r")
|
||||
@ -136,7 +138,7 @@ local prepare_writing = function(pos)
|
||||
{x = pos.x + lcd_info.delta.x,
|
||||
y = pos.y + lcd_info.delta.y,
|
||||
z = pos.z + lcd_info.delta.z}, "hyperloop_lcd:text")
|
||||
text:set_yaw(lcd_info.yaw or 0)
|
||||
text:setyaw(lcd_info.yaw or 0)
|
||||
--* text:setpitch(lcd_info.yaw or 0)
|
||||
return text
|
||||
end
|
||||
|
@ -12,11 +12,12 @@
|
||||
|
||||
-- for lazy programmers
|
||||
local SP = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
--local P = minetest.string_to_pos
|
||||
--local M = minetest.get_meta
|
||||
local P = minetest.string_to_pos
|
||||
local M = minetest.get_meta
|
||||
|
||||
-- Load support for intllib.
|
||||
local S = hyperloop.S
|
||||
local NS = hyperloop.NS
|
||||
|
||||
local Stations = hyperloop.Stations
|
||||
|
||||
|
@ -22,6 +22,7 @@ local M = minetest.get_meta
|
||||
|
||||
-- Load support for intllib.
|
||||
local S = hyperloop.S
|
||||
local NS = hyperloop.NS
|
||||
|
||||
local Tube = hyperloop.Tube
|
||||
local Shaft = hyperloop.Shaft
|
||||
@ -125,7 +126,7 @@ local function convert_legary_nodes(self, pos, dir)
|
||||
end
|
||||
|
||||
local function convert_line(self, pos, dir)
|
||||
convert_legary_nodes(self, pos, dir)
|
||||
local fpos,fdir = convert_legary_nodes(self, pos, dir)
|
||||
self:tool_repair_tube(pos)
|
||||
end
|
||||
|
||||
@ -138,8 +139,8 @@ local function set_pairing(pos, peer_pos)
|
||||
M(pos):set_int("tube_dir", Tube:get_primary_dir(pos))
|
||||
M(peer_pos):set_int("tube_dir", Tube:get_primary_dir(peer_pos))
|
||||
|
||||
Tube:store_teleport_data(pos, peer_pos)
|
||||
Tube:store_teleport_data(peer_pos, pos)
|
||||
local tube_dir1 = Tube:store_teleport_data(pos, peer_pos)
|
||||
local tube_dir2 = Tube:store_teleport_data(peer_pos, pos)
|
||||
end
|
||||
|
||||
|
||||
@ -194,8 +195,8 @@ end
|
||||
|
||||
local function search_wifi_node(pos, dir)
|
||||
local convert_next_tube = function(pos, dir)
|
||||
local npos, _ = Tube:get_node(pos, dir)
|
||||
local dir1, dir2, _ = next_node_on_the_way_to_a_wifi_node(npos)
|
||||
local npos, node = Tube:get_node(pos, dir)
|
||||
local dir1, dir2, num = next_node_on_the_way_to_a_wifi_node(npos)
|
||||
if dir1 then
|
||||
if tubelib2.Turn180Deg[dir] == dir1 then
|
||||
return npos, dir2
|
||||
@ -219,7 +220,7 @@ end
|
||||
local function search_wifi_node_in_all_dirs(pos)
|
||||
-- check all positions
|
||||
for dir = 1, 6 do
|
||||
local _, node = Tube:get_node(pos, dir)
|
||||
local npos, node = Tube:get_node(pos, dir)
|
||||
if node and node.name == "hyperloop:tube1" then
|
||||
search_wifi_node(pos, dir)
|
||||
end
|
||||
@ -229,7 +230,7 @@ end
|
||||
local function convert_tube_line(pos)
|
||||
-- check all positions
|
||||
for dir = 1, 6 do
|
||||
local _, node = Tube:get_node(pos, dir)
|
||||
local npos, node = Tube:get_node(pos, dir)
|
||||
if node and node.name == "hyperloop:tube1" then
|
||||
convert_line(Tube, pos, dir)
|
||||
end
|
||||
|
@ -2,4 +2,3 @@ name = hyperloop
|
||||
depends = default, tubelib2
|
||||
optional_depends = techage, worldedit
|
||||
description = Hyperloop Mod, the fast and modern way of traveling.
|
||||
supported_games = minetest_game
|
||||
|
@ -14,7 +14,7 @@
|
||||
-- for lazy programmers
|
||||
local S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local P = minetest.string_to_pos
|
||||
--local M = minetest.get_meta
|
||||
local M = minetest.get_meta
|
||||
|
||||
-- Convert to list and add pos based on key string
|
||||
local function table_to_list(table)
|
||||
|
@ -15,6 +15,7 @@
|
||||
|
||||
-- Load support for intllib.
|
||||
local S = hyperloop.S
|
||||
local NS = hyperloop.NS
|
||||
|
||||
minetest.register_craftitem("hyperloop:hypersteel_ingot", {
|
||||
description = S("Hypersteel Ingot"),
|
||||
|
@ -11,14 +11,16 @@
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
--local SP = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
--local P = minetest.string_to_pos
|
||||
local SP = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local P = minetest.string_to_pos
|
||||
local M = minetest.get_meta
|
||||
|
||||
-- Load support for intllib.
|
||||
local S = hyperloop.S
|
||||
local NS = hyperloop.NS
|
||||
local I, _ = dofile( minetest.get_modpath("hyperloop").."/intllib.lua")
|
||||
|
||||
local Stations = hyperloop.Stations
|
||||
local PlayerNameTags = {}
|
||||
|
||||
local function enter_display(tStation, text)
|
||||
@ -72,7 +74,7 @@ local function on_arrival(tDeparture, tArrival, player_name, sound)
|
||||
if val1 ~= nil and val2 ~= nil then
|
||||
local offs = val1 - val2
|
||||
local yaw = hyperloop.facedir_to_rad(tArrival.facedir) - offs
|
||||
player:set_look_horizontal(yaw)
|
||||
player:set_look_yaw(yaw)
|
||||
end
|
||||
-- set player name again
|
||||
if PlayerNameTags[player_name] then
|
||||
|
@ -12,11 +12,12 @@
|
||||
|
||||
-- for lazy programmers
|
||||
local SP = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
--local P = minetest.string_to_pos
|
||||
local P = minetest.string_to_pos
|
||||
local M = minetest.get_meta
|
||||
|
||||
-- Load support for intllib.
|
||||
local S = hyperloop.S
|
||||
local NS = hyperloop.NS
|
||||
|
||||
local Tube = hyperloop.Tube
|
||||
local Stations = hyperloop.Stations
|
||||
@ -110,7 +111,7 @@ end
|
||||
|
||||
local function check_space(pos, facedir, placer)
|
||||
for _,item in ipairs(AssemblyPlan) do
|
||||
local y, path, _ = item[1], item[2], item[4]
|
||||
local y, path, node_name = item[1], item[2], item[4]
|
||||
pos = hyperloop.new_pos(pos, facedir, path, y)
|
||||
if minetest.is_protected(pos, placer:get_player_name()) then
|
||||
hyperloop.chat(placer, S("Area is protected!"))
|
||||
@ -228,7 +229,7 @@ local function destroy_station(pos, player_name)
|
||||
-- remove nodes
|
||||
local _pos = table.copy(pos)
|
||||
for _,item in ipairs(AssemblyPlan) do
|
||||
local y, path, _ = item[1], item[2], item[4]
|
||||
local y, path, node_name = item[1], item[2], item[4]
|
||||
_pos = hyperloop.new_pos(_pos, station.facedir, path, y)
|
||||
minetest.remove_node(_pos)
|
||||
end
|
||||
|
@ -11,11 +11,27 @@
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local P = minetest.string_to_pos
|
||||
--local M = minetest.get_meta
|
||||
local M = minetest.get_meta
|
||||
|
||||
-- Load support for intllib.
|
||||
local S = hyperloop.S
|
||||
local NS = hyperloop.NS
|
||||
|
||||
local function station_name(pos)
|
||||
local dataSet = hyperloop.get_station(pos)
|
||||
if dataSet then
|
||||
if dataSet.junction == true then
|
||||
return S("Junction at ")..SP(pos)
|
||||
elseif dataSet.name ~= nil then
|
||||
return S("Station '")..dataSet.name.."' at "..SP(pos)
|
||||
else
|
||||
return S("Station at ")..SP(pos)
|
||||
end
|
||||
end
|
||||
return S("Open end at ")..minetest.pos_to_string(pos)
|
||||
end
|
||||
|
||||
function hyperloop.check_network_level(pos, player)
|
||||
if hyperloop.free_tube_placement_enabled then
|
||||
|
@ -12,11 +12,12 @@
|
||||
|
||||
-- for lazy programmers
|
||||
local SP = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
--local P = minetest.string_to_pos
|
||||
--local M = minetest.get_meta
|
||||
local P = minetest.string_to_pos
|
||||
local M = minetest.get_meta
|
||||
|
||||
-- Load support for intllib.
|
||||
local S = hyperloop.S
|
||||
local NS = hyperloop.NS
|
||||
|
||||
local Shaft = hyperloop.Shaft
|
||||
local Tube = hyperloop.Tube
|
||||
@ -51,7 +52,7 @@ local function repair_tubes(itemstack, placer, pointed_thing)
|
||||
max_hear_distance=5,
|
||||
loop=false})
|
||||
else
|
||||
dir1, dir2, fpos1, fpos2, fdir1, fdir2, cnt1, cnt2 =
|
||||
local dir1, dir2, fpos1, fpos2, fdir1, fdir2, cnt1, cnt2 =
|
||||
Tube:tool_repair_tube(pos, placer, pointed_thing)
|
||||
if fpos1 and fpos2 then
|
||||
if cnt1 + cnt2 >= Shaft.max_tube_length then
|
||||
@ -99,6 +100,10 @@ local function remove_tube(itemstack, placer, pointed_thing)
|
||||
end
|
||||
end
|
||||
|
||||
local function dump_data_base(pos)
|
||||
print(dump(hyperloop.tDatabase))
|
||||
end
|
||||
|
||||
-- Tool for tube workers to crack a protected tube line
|
||||
minetest.register_node("hyperloop:tube_crowbar", {
|
||||
description = S("Hyperloop Tube Crowbar"),
|
||||
|
@ -14,7 +14,7 @@
|
||||
local PI = 3.1415926
|
||||
|
||||
-- for lazy programmers
|
||||
--local SP = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local SP = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local P = minetest.string_to_pos
|
||||
local M = minetest.get_meta
|
||||
|
||||
|
@ -17,6 +17,7 @@ local Waypoints = {}
|
||||
|
||||
-- Load support for intllib.
|
||||
local S = hyperloop.S
|
||||
local NS = hyperloop.NS
|
||||
|
||||
minetest.register_node("hyperloop:waypoint", {
|
||||
description = S("Hyperloop Waypoint"),
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
-- Load support for intllib.
|
||||
local S = hyperloop.S
|
||||
local NS = hyperloop.NS
|
||||
|
||||
local Tube = hyperloop.Tube
|
||||
|
||||
|
@ -217,11 +217,9 @@ end
|
||||
function lcdlib.register_display_entity(entity_name)
|
||||
if not minetest.registered_entity then
|
||||
minetest.register_entity(':'..entity_name, {
|
||||
initial_properties = {
|
||||
collisionbox = { 0, 0, 0, 0, 0, 0 },
|
||||
visual = "upright_sprite",
|
||||
textures = {},
|
||||
},
|
||||
on_activate = lcdlib.on_activate,
|
||||
})
|
||||
end
|
||||
|
@ -2,4 +2,3 @@ name=minecart
|
||||
depends = default,carts
|
||||
optional_depends = doc, doclib
|
||||
description = Minecart, the lean railway transportation automation system
|
||||
supported_games = minetest_game
|
||||
|
@ -129,10 +129,7 @@ function networks.open_node(pos, node, placer)
|
||||
if ndef and ndef.paramtype2 == "color" then
|
||||
stack:get_meta():set_int("palette_index", node.param2)
|
||||
end
|
||||
local leftover = inv:add_item("main", stack)
|
||||
if leftover:get_count() > 0 then
|
||||
minetest.add_item(pos, leftover)
|
||||
end
|
||||
inv:add_item("main", stack)
|
||||
return true
|
||||
end
|
||||
|
||||
|
@ -31,30 +31,6 @@ local function range(from, to)
|
||||
end, minetest.get_us_time() + safer_lua.MaxExeTime, from-1
|
||||
end
|
||||
|
||||
-- Borrowed from mesecons_luacontroller
|
||||
-- string.rep(str, n) with a high value for n can be used to DoS
|
||||
-- the server. Therefore, limit max. length of generated string.
|
||||
local function safe_string_rep(str, n)
|
||||
if #str * n > 1000 then
|
||||
debug.sethook() -- Clear hook
|
||||
error("string.rep: string length overflow", 2)
|
||||
end
|
||||
|
||||
return string.rep(str, n)
|
||||
end
|
||||
|
||||
-- Borrowed from mesecons_luacontroller
|
||||
-- string.find with a pattern can be used to DoS the server.
|
||||
-- Therefore, limit string.find to patternless matching.
|
||||
local function safe_string_find(...)
|
||||
if (select(4, ...)) ~= true then
|
||||
debug.sethook() -- Clear hook
|
||||
error("string.find: 'plain' (fourth parameter) must always be true")
|
||||
end
|
||||
|
||||
return string.find(...)
|
||||
end
|
||||
|
||||
local BASE_ENV = {
|
||||
Array = safer_lua.Array,
|
||||
Store = safer_lua.Store,
|
||||
@ -71,14 +47,14 @@ local BASE_ENV = {
|
||||
string = {
|
||||
byte = string.byte,
|
||||
char = string.char,
|
||||
find = safe_string_find,
|
||||
find = string.find,
|
||||
format = string.format,
|
||||
gmatch = string.gmatch,
|
||||
gsub = string.gsub,
|
||||
len = string.len,
|
||||
lower = string.lower,
|
||||
match = string.match,
|
||||
rep = safe_string_rep,
|
||||
rep = string.rep,
|
||||
sub = string.sub,
|
||||
upper = string.upper,
|
||||
split = function(str, separator, include_empty, max_splits, sep_is_pattern)
|
||||
|
@ -15,7 +15,7 @@
|
||||
safer_lua = {}
|
||||
|
||||
-- Version for compatibility checks, see readme.md/history
|
||||
safer_lua.version = 1.03
|
||||
safer_lua.version = 1.01
|
||||
|
||||
dofile(minetest.get_modpath("safer_lua") .. "/data_struct.lua")
|
||||
dofile(minetest.get_modpath("safer_lua") .. "/scanner.lua")
|
||||
|
@ -10,7 +10,7 @@ A subset of the language Lua for safe and secure Lua sandboxes with:
|
||||
### License
|
||||
Copyright (C) 2018-2022 Joachim Stolberg
|
||||
Code: Licensed under the GNU LGPL version 2.1 or later. See LICENSE.txt
|
||||
Functions safe_string_rep and safe_string_find (from mesecons) LGPL version 3
|
||||
|
||||
|
||||
### Dependencies
|
||||
none
|
||||
@ -20,4 +20,3 @@ none
|
||||
- 2020-03-14 v1.00 * extracted from TechPack and released
|
||||
- 2021-11-28 v1.01 * function `string.split2` added, `unpack` removed
|
||||
- 2022-12-22 v1.02 * Limit code execution time for recursive function calls (#3 by Thomas--S)
|
||||
- 2024-06-19 V1.03 * Add safe variants for strin.rep() and string.find()
|
||||
|
@ -35,20 +35,12 @@ signs_bot.register_flower("default:acacia_bush_stem")
|
||||
signs_bot.register_flower("default:pine_bush_stem")
|
||||
|
||||
minetest.after(1, function()
|
||||
local function add_flower(name)
|
||||
local def = minetest.registered_nodes[name]
|
||||
if def and (def.groups.mushroom == 1 or def.groups.flower == 1) then
|
||||
signs_bot.register_flower(name)
|
||||
end
|
||||
end
|
||||
|
||||
for _,def in pairs(minetest.registered_decorations) do
|
||||
local name = def.decoration
|
||||
if type(name) == "string" then
|
||||
add_flower(name)
|
||||
elseif type(name) == "table" then
|
||||
for _,sub_name in ipairs(name) do
|
||||
add_flower(sub_name)
|
||||
if name and type(name) == "string" then
|
||||
local mod = string.split(name, ":")[1]
|
||||
if mod == "flowers" or mod == "bakedclay" then -- Bakedclay also registers flowers as decoration.
|
||||
signs_bot.register_flower(name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -3,7 +3,7 @@
|
||||
Signs Bot
|
||||
=========
|
||||
|
||||
Copyright (C) 2019-2024 Joachim Stolberg
|
||||
Copyright (C) 2019-2021 Joachim Stolberg
|
||||
|
||||
GPL v3
|
||||
See LICENSE.txt for more information
|
||||
@ -263,25 +263,11 @@ signs_bot.register_botcommand("fall_down", {
|
||||
description = S("Fall into a hole/chasm (up to 10 blocks)"),
|
||||
cmnd = function(base_pos, mem)
|
||||
if not mem.bot_falling then
|
||||
--Run a while loop that checks the 10 nodes below bot for a node with the walkable property, breaking the loop once it finds a walkable node.
|
||||
local fallcounter = 0
|
||||
local fallnode = {walkable = false}
|
||||
while fallcounter <= 9 and fallnode.walkable == false do
|
||||
fallcounter = fallcounter + 1
|
||||
--Pulls the node name from the next position, then assigns the node definintion to the fallnode variable.
|
||||
fallnode = minetest.get_node_or_nil({x=mem.robot_pos.x, y=mem.robot_pos.y-fallcounter, z=mem.robot_pos.z})
|
||||
fallnode = minetest.registered_nodes[fallnode.name]
|
||||
end
|
||||
--If the first nine nodes below the bot are not walkable, then it should assign the definintion to the 10th node. If it too is not walkable, then a "Too Deep" error is returned.
|
||||
if fallnode.walkable == false then
|
||||
return signs_bot.ERROR, "Too deep"
|
||||
end
|
||||
--Designates the node above the walkable node as the new location for the bot.
|
||||
local pos3 = {x=mem.robot_pos.x, y=mem.robot_pos.y-fallcounter+1, z=mem.robot_pos.z}
|
||||
--Turns the bot into a falling node.
|
||||
local sts, _ = minetest.spawn_falling_node(mem.robot_pos)
|
||||
--Stores the data for the node the bot will land in and replaces it with air. This way if the node the bot lands in is occupied by an unwalkable node, such as a rail or sign,
|
||||
--it will be repalced when the bot moves.
|
||||
local pos1 = {x=mem.robot_pos.x, y=mem.robot_pos.y-1, z=mem.robot_pos.z}
|
||||
local pos2 = {x=mem.robot_pos.x, y=mem.robot_pos.y-10, z=mem.robot_pos.z}
|
||||
local sts, pos3 = minetest.line_of_sight(pos1, pos2)
|
||||
if sts == false then
|
||||
sts, _ = minetest.spawn_falling_node(mem.robot_pos)
|
||||
mem.stored_node = get_node_lvm(pos3)
|
||||
minetest.swap_node(pos3, {name="air"})
|
||||
if sts then
|
||||
@ -289,6 +275,8 @@ signs_bot.register_botcommand("fall_down", {
|
||||
mem.robot_pos = {x=pos3.x, y=pos3.y, z=pos3.z}
|
||||
return signs_bot.BUSY
|
||||
end
|
||||
end
|
||||
return signs_bot.ERROR, "Too deep"
|
||||
else
|
||||
mem.bot_falling = mem.bot_falling - 1
|
||||
if mem.bot_falling <= 0 then
|
||||
|
@ -86,7 +86,7 @@ local function tokenizer(script)
|
||||
if num_param >= 3 then
|
||||
tokens[#tokens + 1] = param3 or "nil"
|
||||
end
|
||||
elseif cmnd:find("%w+:$") then
|
||||
elseif cmnd:find("%w+:") then
|
||||
tokens[#tokens + 1] = cmnd
|
||||
end
|
||||
end
|
||||
@ -98,7 +98,7 @@ local function pass1(tokens)
|
||||
local pc = 1
|
||||
tSymbolTbl = {}
|
||||
for _, token in ipairs(tokens) do
|
||||
if token:find("%w+:$") then
|
||||
if token:find("%w+:") then
|
||||
tSymbolTbl[token] = pc
|
||||
else
|
||||
pc = pc + 1
|
||||
@ -265,7 +265,7 @@ function api.check_script(script)
|
||||
if tCmdDef[cmnd].num_param > 0 and not tCmdDef[cmnd].check(param1, param2, param3) then
|
||||
return false, S("Parameter error"), idx
|
||||
end
|
||||
elseif not cmnd:find("%w+:$") then
|
||||
elseif not cmnd:find("%w+:") then
|
||||
return false, S("Command error"), idx
|
||||
end
|
||||
tbl[cmnd] = (tbl[cmnd] or 0) + 1
|
||||
@ -298,10 +298,8 @@ function api.run_script(base_pos, mem)
|
||||
CodeCache[hash] = compile(mem.script)
|
||||
mem.pc = 1
|
||||
mem.Stack = {}
|
||||
elseif res == api.ERROR then
|
||||
return res, err, gen_string_cmnd(code, mem.pc, num_param, mem.script)
|
||||
end
|
||||
return res, err, ""
|
||||
return res, err, gen_string_cmnd(code, mem.pc, num_param, mem.script)
|
||||
end
|
||||
return api.EXIT
|
||||
end
|
||||
|
@ -2,5 +2,3 @@ name=signs_bot
|
||||
depends = default,farming,basic_materials,tubelib2
|
||||
optional_depends = node_io,techage,doc,minecart,bucket,fire,xdecor,ethereal,compost,doclib
|
||||
description = A robot controlled by signs
|
||||
supported_games = minetest_game
|
||||
|
||||
|
@ -39,18 +39,14 @@ local function store_data(placer, pos, name)
|
||||
meta:set_string("signs_bot_spos", spos)
|
||||
meta:set_string("signs_bot_name", name)
|
||||
else
|
||||
meta:set_string("signs_bot_spos", "")
|
||||
meta:set_string("signs_bot_name", "")
|
||||
meta:set_string("signs_bot_spos", nil)
|
||||
meta:set_string("signs_bot_name", nil)
|
||||
end
|
||||
end
|
||||
|
||||
-- Write actuator_pos data to sensor_pos
|
||||
local function pairing(actuator_pos, sensor_pos, invert)
|
||||
local function pairing(actuator_pos, sensor_pos)
|
||||
local signal = signs_bot.get_signal(actuator_pos)
|
||||
if invert then
|
||||
signal = ({on = "off", off = "on"})[signal]
|
||||
end
|
||||
|
||||
if signal then
|
||||
signs_bot.store_signal(sensor_pos, actuator_pos, signal)
|
||||
local node = tubelib2.get_node_lvm(sensor_pos)
|
||||
@ -62,29 +58,24 @@ local function pairing(actuator_pos, sensor_pos, invert)
|
||||
end
|
||||
|
||||
local function use_tool(itemstack, placer, pointed_thing)
|
||||
local invert = false
|
||||
if placer:get_player_control().aux1 then
|
||||
invert = true
|
||||
end
|
||||
|
||||
if pointed_thing.type == "node" then
|
||||
local pos1,ntype1 = get_stored_data(placer)
|
||||
local pos2,ntype2 = get_current_data(pointed_thing)
|
||||
|
||||
if ntype1 == "actuator" and (ntype2 == "sensor" or ntype2 == "repeater") then
|
||||
pairing(pos1, pos2, invert)
|
||||
pairing(pos1, pos2)
|
||||
store_data(placer, nil, nil)
|
||||
minetest.sound_play('signs_bot_pong', {to_player = placer:get_player_name()})
|
||||
elseif (ntype1 == "actuator" or ntype1 == "repeater") and ntype2 == "sensor" then
|
||||
pairing(pos1, pos2, invert)
|
||||
pairing(pos1, pos2)
|
||||
store_data(placer, nil, nil)
|
||||
minetest.sound_play('signs_bot_pong', {to_player = placer:get_player_name()})
|
||||
elseif ntype2 == "actuator" and (ntype1 == "sensor" or ntype1 == "repeater") then
|
||||
pairing(pos2, pos1, invert)
|
||||
pairing(pos2, pos1)
|
||||
store_data(placer, nil, nil)
|
||||
minetest.sound_play('signs_bot_pong', {to_player = placer:get_player_name()})
|
||||
elseif (ntype2 == "actuator" or ntype2 == "repeater") and ntype1 == "sensor" then
|
||||
pairing(pos2, pos1, invert)
|
||||
pairing(pos2, pos1)
|
||||
store_data(placer, nil, nil)
|
||||
minetest.sound_play('signs_bot_pong', {to_player = placer:get_player_name()})
|
||||
elseif ntype2 == "actuator" or ntype2 == "sensor" or ntype2 == "repeater" then
|
||||
|
1
techage
1
techage
@ -1 +0,0 @@
|
||||
Subproject commit ce8b08d2d7937a46e643da1eb745a8ac6bf4598f
|
112
techage/.test/sink.lua
Normal file
112
techage/.test/sink.lua
Normal file
@ -0,0 +1,112 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019-2021 Joachim Stolberg
|
||||
|
||||
GPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
Demo for a electrical power consuming node
|
||||
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local P2S = minetest.pos_to_string
|
||||
local M = minetest.get_meta
|
||||
local S = techage.S
|
||||
|
||||
local PWR_NEEDED = 5
|
||||
local CYCLE_TIME = 2
|
||||
|
||||
local Cable = techage.ElectricCable
|
||||
--local Cable = techage.Axle
|
||||
local power = networks.power
|
||||
|
||||
local function swap_node(pos, name)
|
||||
local node = techage.get_node_lvm(pos)
|
||||
if node.name == name then
|
||||
return
|
||||
end
|
||||
node.name = name
|
||||
minetest.swap_node(pos, node)
|
||||
end
|
||||
|
||||
local function on_rightclick(pos, node, clicker)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
if not nvm.running and power.power_available(pos, Cable) then
|
||||
nvm.running = true
|
||||
swap_node(pos, "techage:sink_on")
|
||||
M(pos):set_string("infotext", "on")
|
||||
minetest.get_node_timer(pos):start(CYCLE_TIME)
|
||||
else
|
||||
nvm.running = false
|
||||
swap_node(pos, "techage:sink")
|
||||
M(pos):set_string("infotext", "off")
|
||||
minetest.get_node_timer(pos):stop()
|
||||
end
|
||||
end
|
||||
|
||||
local function after_place_node(pos)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
M(pos):set_string("infotext", "off")
|
||||
Cable:after_place_node(pos)
|
||||
end
|
||||
|
||||
local function after_dig_node(pos, oldnode)
|
||||
Cable:after_dig_node(pos)
|
||||
techage.del_mem(pos)
|
||||
end
|
||||
|
||||
minetest.register_node("techage:sink", {
|
||||
description = "Sink",
|
||||
tiles = {'techage_electric_button.png^[colorize:#000000:50'},
|
||||
|
||||
on_timer = function(pos, elapsed)
|
||||
local consumed = power.consume_power(pos, Cable, nil, PWR_NEEDED)
|
||||
if consumed == PWR_NEEDED then
|
||||
swap_node(pos, "techage:sink_on")
|
||||
M(pos):set_string("infotext", "on")
|
||||
end
|
||||
return true
|
||||
end,
|
||||
on_rightclick = on_rightclick,
|
||||
after_place_node = after_place_node,
|
||||
after_dig_node = after_dig_node,
|
||||
|
||||
paramtype = "light",
|
||||
light_source = 0,
|
||||
paramtype2 = "facedir",
|
||||
groups = {choppy = 2, cracky = 2, crumbly = 2},
|
||||
is_ground_content = false,
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
})
|
||||
|
||||
minetest.register_node("techage:sink_on", {
|
||||
description = "Sink",
|
||||
tiles = {'techage_electric_button.png'},
|
||||
|
||||
on_timer = function(pos, elapsed)
|
||||
local consumed = power.consume_power(pos, Cable, nil, PWR_NEEDED)
|
||||
if consumed < PWR_NEEDED then
|
||||
swap_node(pos, "techage:sink")
|
||||
M(pos):set_string("infotext", "off")
|
||||
end
|
||||
return true
|
||||
end,
|
||||
on_rightclick = on_rightclick,
|
||||
after_place_node = after_place_node,
|
||||
after_dig_node = after_dig_node,
|
||||
|
||||
paramtype = "light",
|
||||
light_source = minetest.LIGHT_MAX,
|
||||
paramtype2 = "facedir",
|
||||
diggable = false,
|
||||
drop = "",
|
||||
groups = {not_in_creative_inventory = 1},
|
||||
is_ground_content = false,
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
})
|
||||
|
||||
power.register_nodes({"techage:sink", "techage:sink_on"}, Cable, "con")
|
43
techage/.test/testblock.lua
Normal file
43
techage/.test/testblock.lua
Normal file
@ -0,0 +1,43 @@
|
||||
local M = minetest.get_meta
|
||||
|
||||
minetest.register_node("techage:testblock", {
|
||||
description = "Testblock",
|
||||
tiles = {
|
||||
"techage_top_ta4.png",
|
||||
"techage_filling_ta4.png^techage_frame_ta4.png",
|
||||
},
|
||||
paramtype2 = "facedir",
|
||||
groups = {cracky=2, crumbly=2, choppy=2},
|
||||
is_ground_content = false,
|
||||
|
||||
after_place_node = function(pos, placer)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
nvm.test_val = 1
|
||||
M(pos):set_int("test_val", 1)
|
||||
M(pos):set_string("infotext", "Value = " .. 1)
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_lbm({
|
||||
label = "Update testblock",
|
||||
name = "techage:update_testblock",
|
||||
|
||||
nodenames = {
|
||||
"techage:testblock",
|
||||
},
|
||||
|
||||
run_at_every_load = true,
|
||||
|
||||
action = function(pos, node)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
if M(pos):get_int("test_val") == nvm.test_val then
|
||||
nvm.test_val = nvm.test_val + 1
|
||||
M(pos):set_int("test_val", nvm.test_val)
|
||||
M(pos):set_string("infotext", "Value = " .. nvm.test_val)
|
||||
else
|
||||
minetest.log("error", "[techage] Memory error at " .. minetest.pos_to_string(pos))
|
||||
M(pos):set_string("infotext", "Error")
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
662
techage/LICENSE.txt
Normal file
662
techage/LICENSE.txt
Normal file
@ -0,0 +1,662 @@
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
Version 3, 19 November 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU Affero General Public License is a free, copyleft license for
|
||||
software and other kinds of works, specifically designed to ensure
|
||||
cooperation with the community in the case of network server software.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
our General Public Licenses are intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
Developers that use our General Public Licenses protect your rights
|
||||
with two steps: (1) assert copyright on the software, and (2) offer
|
||||
you this License which gives you legal permission to copy, distribute
|
||||
and/or modify the software.
|
||||
|
||||
A secondary benefit of defending all users' freedom is that
|
||||
improvements made in alternate versions of the program, if they
|
||||
receive widespread use, become available for other developers to
|
||||
incorporate. Many developers of free software are heartened and
|
||||
encouraged by the resulting cooperation. However, in the case of
|
||||
software used on network servers, this result may fail to come about.
|
||||
The GNU General Public License permits making a modified version and
|
||||
letting the public access it on a server without ever releasing its
|
||||
source code to the public.
|
||||
|
||||
The GNU Affero General Public License is designed specifically to
|
||||
ensure that, in such cases, the modified source code becomes available
|
||||
to the community. It requires the operator of a network server to
|
||||
provide the source code of the modified version running there to the
|
||||
users of that server. Therefore, public use of a modified version, on
|
||||
a publicly accessible server, gives the public access to the source
|
||||
code of the modified version.
|
||||
|
||||
An older license, called the Affero General Public License and
|
||||
published by Affero, was designed to accomplish similar goals. This is
|
||||
a different license, not a version of the Affero GPL, but Affero has
|
||||
released a new version of the Affero GPL which permits relicensing under
|
||||
this license.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Remote Network Interaction; Use with the GNU General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, if you modify the
|
||||
Program, your modified version must prominently offer all users
|
||||
interacting with it remotely through a computer network (if your version
|
||||
supports such interaction) an opportunity to receive the Corresponding
|
||||
Source of your version by providing access to the Corresponding Source
|
||||
from a network server at no charge, through some standard or customary
|
||||
means of facilitating copying of software. This Corresponding Source
|
||||
shall include the Corresponding Source for any work covered by version 3
|
||||
of the GNU General Public License that is incorporated pursuant to the
|
||||
following paragraph.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the work with which it is combined will remain governed by version
|
||||
3 of the GNU General Public License.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU Affero General Public License from time to time. Such new versions
|
||||
will be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU Affero General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU Affero General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU Affero General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
TechAge, a mod to go through 5 tech ages in search of wealth and power.
|
||||
Copyright (C) 2019-2023 Joachim Stolberg
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If your software can interact with users remotely through a computer
|
||||
network, you should also make sure that it provides a way for users to
|
||||
get its source. For example, if your program is a web application, its
|
||||
interface could display a "Source" link that leads users to an archive
|
||||
of the code. There are many ways you could offer source, and different
|
||||
solutions will be better for different programs; see section 13 for the
|
||||
specific requirements.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
408
techage/README.md
Normal file
408
techage/README.md
Normal file
@ -0,0 +1,408 @@
|
||||
# Tech Age [techage] (Minetest 5.4+)
|
||||
|
||||
Tech Age, a mod to go through 5 tech ages in search of wealth and power.
|
||||
|
||||
![screenshot](https://github.com/joe7575/techage/blob/master/screenshot.png)
|
||||
|
||||
|
||||
Important facts:
|
||||
- techage is not backwards compatible and cannot be installed on a server together with TechPack
|
||||
- techage is significantly more extensive, since additional mods are integrated
|
||||
- techage represents 5 technological ages:
|
||||
- Iron Age (TA1) - simple tools like coal pile, coal burner, gravel sieve, hammer for getting ores and making goods
|
||||
- Steam Age (TA2) - Simple machines that are powered by steam engines and drive axles
|
||||
- Oil Age (TA3) - More modern machines that are powered by electricity.
|
||||
- Present (TA4) - Electricity from renewable energy sources such as sun and wind.
|
||||
- Future (TA5) - Machines to overcome space and time, new sources of energy and other achievements.
|
||||
- Since the levels build on each other, all ages have to be run through one after the other
|
||||
|
||||
In contrast to TechPack, the resources are more limited and it is much more difficult to pass all levels.
|
||||
(no endless ore generation by means of cobble generators)
|
||||
|
||||
**Techage blocks store information outside of the block. This is for performance reasons.
|
||||
If you move, place, or remove blocks with any tool, at best, only the information is lost.
|
||||
In the worst case, the server crashes.**
|
||||
|
||||
[Manuals](https://github.com/joe7575/techage/wiki)
|
||||
|
||||
|
||||
### License
|
||||
Copyright (C) 2019-2023 Joachim Stolberg
|
||||
Code: Licensed under the GNU AGPL version 3 or later. See LICENSE.txt
|
||||
Textures: CC BY-SA 3.0
|
||||
|
||||
The TA1 mill sound is from https://freesound.org/people/JustinBW/sounds/70200/
|
||||
The TA1 watermill sound is from https://freesound.org/people/bmoreno/sounds/164182/
|
||||
|
||||
Many thanks to Thomas-S, niklp09, and others for their contributions
|
||||
|
||||
|
||||
### Dependencies
|
||||
Required: default, doors, bucket, stairs, screwdriver, basic_materials, tubelib2, networks, minecart, lcdlib, safer_lua, doclib
|
||||
Recommended: signs_bot, hyperloop, compost, techpack_stairway, autobahn
|
||||
Optional: unified_inventory, wielded_light, unifieddyes, lua-mashal, lsqlite3, moreores, ethereal, mesecon
|
||||
|
||||
|
||||
The mods `default`, `doors`, `bucket`, `stairs`, and `screwdriver` are part of Minetest Game.
|
||||
|
||||
`basic_materials` will be found here: https://content.minetest.net/
|
||||
|
||||
The following mods in the newest version have to be downloaded directly from GitHub:
|
||||
* [tubelib2](https://github.com/joe7575/tubelib2)
|
||||
* [networks](https://github.com/joe7575/networks)
|
||||
* [minecart](https://github.com/joe7575/minecart)
|
||||
* [lcdlib](https://github.com/joe7575/lcdlib)
|
||||
* [safer_lua](https://github.com/joe7575/safer_lua)
|
||||
* [doclib](https://github.com/joe7575/doclib)
|
||||
|
||||
It is highly recommended that you install the following mods, too:
|
||||
|
||||
* [signs_bot](https://github.com/joe7575/signs_bot): For many automation tasks in TA3/TA4 like farming, mining, and item transportation
|
||||
* [hyperloop](https://github.com/joe7575/Minetest-Hyperloop): Used as passenger transportation system in TA4
|
||||
* [compost](https://github.com/joe7575/compost): The garden soil is needed for the TA4 LED Grow Light based flower bed
|
||||
* [techpack_stairway](https://github.com/joe7575/techpack_stairway): Ladders, stairways, and bridges for your machines
|
||||
* [autobahn](https://github.com/joe7575/autobahn): Street blocks and slopes with stripes for faster traveling
|
||||
* [ta4_jetpack](https://github.com/joe7575/ta4_jetpack): A Jetpack with hydrogen as fuel and TA4 recipe
|
||||
|
||||
More recommended Techage related mods by other authors:
|
||||
|
||||
* [ta4_addons](https://github.com/Thomas--S/ta4_addons) from Thomas--S: A Touchscreen for the Lua controller
|
||||
* [ts_vehicles](https://github.com/Thomas--S/ts_vehicles) from Thomas--S: A mod to provide cars and other vehicles for Minetest.
|
||||
* [ta_apiary](https://gitlab.com/lesya_minetest_mods/ta_apiary) from Olesya Sibidanova: TechAge Machines for beekeeping
|
||||
|
||||
For large servers with many players, the following packages are recommended:
|
||||
|
||||
* lua-mashal for faster serialization/deserialization of data
|
||||
* lsqlite3 for storing node and network data
|
||||
|
||||
The packages have to be installed via [luarocks](https://luarocks.org/):
|
||||
|
||||
luarocks --lua-version 5.1 install lsqlite3
|
||||
luarocks --lua-version 5.1 install lua-marshal
|
||||
|
||||
To enable these `unsafe` packages, add 'techage' and 'lua-marshal'
|
||||
to the list of trusted mods in `minetest.conf`:
|
||||
|
||||
secure.trusted_mods = techage,lua-marshal
|
||||
|
||||
and add the following line to your `world.mt` or `minetest.conf`:
|
||||
|
||||
techage_use_sqlite = true
|
||||
|
||||
Available worlds will be converted to 'lsqlite3', but there is no way back, so:
|
||||
|
||||
**Never disable 'lsqlite3' for a world that has already been used!**
|
||||
|
||||
### History
|
||||
|
||||
**2023-11-05 V1.18**
|
||||
|
||||
- Add TA2 clutch
|
||||
- TA5 Generator: Add generator menu
|
||||
- TA4 Injector: Allow rotation with a screwdriver
|
||||
- Escape equal sign in german translation (Niklp09)
|
||||
- Autocrafter: Add Beduino command interface
|
||||
- Autocrafter: Add flush command
|
||||
- Fix converter stores mesecon signals (Niklp09)
|
||||
- TA1 Gravel Sieve: Use proper player creative check (Niklp09)
|
||||
- TA4 Chest: Add storesize command
|
||||
- Improve Assembly Tool
|
||||
- Furnace: Fix burn time issue
|
||||
- Allow further types of cobblestone for the coalburner
|
||||
- Fix water mill river water bug (alwayshopeless)
|
||||
- Improve manual
|
||||
- Further improvements
|
||||
|
||||
**2023-08-25 V1.17**
|
||||
|
||||
- Add support for doclib / remove techage internal doc support
|
||||
**The mod doclib is a new hard depenency !**
|
||||
- Fix LICENCSE file bug
|
||||
- Add beduino support for TA3 repeater (realmicu)
|
||||
- Add inv_name_prefix to `techage.register_consumer` (debiankaios)
|
||||
- Add generator menu to TA5 generator (fusion reactor)
|
||||
- Adapt mod to the new lcdlib mod
|
||||
- Fix some bugs
|
||||
|
||||
**2023-06-30 V1.16**
|
||||
|
||||
- Add TA4 node detector
|
||||
- Add wrench menu to TA3 button
|
||||
- Add arrows to the pump bottom and allow to turn the pump with the Techage Screwdriver
|
||||
- Fix bug with configurred TA4 chest and TA5 teleport tubes
|
||||
- Add gaze sensor
|
||||
- Many bugfixes and improvements
|
||||
|
||||
**2023-05-05 V1.15**
|
||||
|
||||
- Allow energy storage with up to 13x13x13 concrete blocks
|
||||
- Allow registration of other buckets
|
||||
- Add hyperloop chest only if the hyperloop mod is available
|
||||
- Add missing 'minetest.formspec_escape' #131
|
||||
- Fix bug "Trouble with flycontroller #130"
|
||||
- Add optional dependency on farming mod (orwell96)
|
||||
- Fix forceload formspec receiver (Niklp09)
|
||||
|
||||
**2023-04-16 V1.14**
|
||||
|
||||
- Add file "api.md"
|
||||
- Add API function `register_ore_for_gravelsieve`
|
||||
- Add support for the game Asuna
|
||||
- Merge pull request #124 from Niklp09/drops
|
||||
- Fix keep node number issue
|
||||
- Fix manual issue #123
|
||||
|
||||
**2023-04-10 V1.13**
|
||||
|
||||
- Add "Teleport mode" to the ta5 fly controller
|
||||
|
||||
**2023-04-01 V1.12**
|
||||
|
||||
- Improve Transformer:
|
||||
- add wrench menu for 'max. power passed through'
|
||||
- Increase max. power passed through from 100 to 300 ku
|
||||
- Improve Electricmeter:
|
||||
- add wrench menu for 'max. power passed through' and 'power countdown' 2458
|
||||
- add commands to read the countdown value (Lua and Beduino controller)
|
||||
- Improve TA3 Mesecons Converter:
|
||||
- fix overload bug
|
||||
- fix missing dominant 'on' issue
|
||||
- Add version command to TA3/TA4 Terminal
|
||||
- TA5 Hyperloop Chest: Disable inventory access on client side due to minetest core issues
|
||||
|
||||
**2023-03-05 V1.11**
|
||||
|
||||
- Reduce the number of necessary exp points for TA5 Hyperloop Chest,
|
||||
TA5 Hyperloop Tank, and TA5 AI Chip II
|
||||
- Fix possible kernel crashes with TA5 Hyperloop Chest and autocrafter
|
||||
- Rework doorcontroller (menu changed)
|
||||
- Increase tank cart storage size to 200 units
|
||||
- Fix several paramtype/use_texture_alpha issues
|
||||
- Add command 'load' to the TA4 power terminal
|
||||
- Add beduino tank commands
|
||||
- Fix power consumption bug for a stopped collider
|
||||
- Fix electrolyzer formspec bug
|
||||
- Add Rack and pinion node
|
||||
- Expand ta4 sequencer wrench menu
|
||||
- Accept mincart carts for the move controller
|
||||
- movecontroller: Allow to move objects 'without' a move block
|
||||
- Add empty_spool as fab output
|
||||
- Fix doser goes blocked bug
|
||||
|
||||
**2023-02-04 V1.10**
|
||||
- Improve flycontroller
|
||||
- Remove handover for movecontroller
|
||||
- Rename "techage:signal_lamp" to "techage:color_lamp"
|
||||
- Rename "techage:signal_lamp2" to "techage:color_lamp2"
|
||||
- Add countdown mode to TA4 Detector
|
||||
- Adapt to new beduino and minecart versions
|
||||
- Improve manuals
|
||||
- flycontroller/movecontroller: Allow moving blocks through unloaded areas
|
||||
- playerdetector: Add wrench menu to configure search radius
|
||||
- Default furnace: Don't use items filled from the top as fuel
|
||||
- Many further improvements and bug fixes from joe7575 and Niklp09
|
||||
|
||||
**2022-09-03 V1.09**
|
||||
- Change the way items are pushed
|
||||
- Add "Flow Limiter" mode to TA4 pump and TA4 pusher
|
||||
|
||||
**2022-06-06 V1.08**
|
||||
- Native support for the mod Beduino added
|
||||
|
||||
**2022-01-22 V1.07**
|
||||
- TA5 fusion reactor added
|
||||
|
||||
**2022-01-03 V1.06**
|
||||
- TA5 teleport blocks added
|
||||
- Many improvements
|
||||
|
||||
**2021-12-25 V1.05**
|
||||
- Support for the mod i3 added (thanks to ghaydn)
|
||||
- TA5 enabled
|
||||
- Many improvements
|
||||
|
||||
**2021-12-12 V1.04**
|
||||
- TA4 Collider added (experimental)
|
||||
- move, turn, sound, and fly blocks added
|
||||
- TA5 (future) introduced (TA4 is now the "present")
|
||||
|
||||
**2021-10-24 V1.03**
|
||||
- Add TA4 Sequencer for time controlled command sequences
|
||||
- Add TA4 Move Controller for moving blocks
|
||||
- Add techage command counting function to be able to limit the amount of commands/min.
|
||||
- Pull request #67: Add switch mode for 4x Button (by realmicu)
|
||||
- Pull request #69: Add option to keep assignment for TA4 Tank (by Thomas-S)
|
||||
|
||||
**2021-09-18 V1.02**
|
||||
- TA4 Chest: Fix items disappearing (PR #64 by Thomas--S)
|
||||
- Add support for colored cables (PR #63 by Thomas--S)
|
||||
|
||||
**2021-08-16 V1.01**
|
||||
- Allow singleplayer to place lava on y>0.
|
||||
- Logic block: allow to use output numbers for the expression
|
||||
- Pull request #60: Allow to pause the sequencer with a TechAge command (by Thomas-S)
|
||||
- Pull request #61: Allow sharing the button based on protection (by Thomas-S)
|
||||
- Pull request #62: Allow picking TA3 Tiny Generator with fuel (by realmicu)
|
||||
- Add TA1 watermill
|
||||
- Fix TA4 LED Grow Light bug
|
||||
- Fix grinder recipe bu
|
||||
|
||||
**2021-07-23 V1.00**
|
||||
- Change the way, power distribution works
|
||||
- Add TA2 storage system
|
||||
- Add TA4 Isolation Transformer
|
||||
- Add TA4 Electric Meter
|
||||
- Add new power terminal
|
||||
- Many improvements on power producing/consuming nodes
|
||||
- See Construction Board for some hints on moving to v1
|
||||
|
||||
**2021-05-14 V0.26**
|
||||
- Add concentrator tubes
|
||||
- Add ta4 cable wall entry
|
||||
- Pull request #57: Distributor improvements (from Thomas-S)
|
||||
- Add new power terminal commands
|
||||
- Add new door controller
|
||||
- Add laser beam nodes for energy transfer
|
||||
- Add TA4 recycle machine
|
||||
- Many improvements and bug fixes
|
||||
|
||||
**2020-11-01 V0.25**
|
||||
- Pull request #37: Trowel: Add protection support (from Thomas-S)
|
||||
- Pull request #38: Charcoal Pile: Ignore "ignore" nodes (from Thomas-S)
|
||||
- Autocrafter: Add register function for uncraftable items
|
||||
- Fix bug: Tubes do not recognize when TA2 nodes are added/removed
|
||||
- TA4 chest/tank: Add 'public' checkbox to allow public access
|
||||
- Add nodes TA2 Power Generator and TA3 Electric Motor
|
||||
|
||||
**2020-10-20 V0.24**
|
||||
- Pull request #27: Liquid Tanks: Add protection support (from Thomas-S)
|
||||
- Pull request #28: Quarry: Improve digging behaviour (from Thomas-S)
|
||||
- Pull request #29: Distributor: Keep metadata (from Thomas-S)
|
||||
- Pull request #30: TA4: Add Liquid Filter (from Thomas-S)
|
||||
- Pull request #31: Fix chest crash (from Thomas-S)
|
||||
- Pull request #32: Fix Filter Sink Bug (from Thomas-S)
|
||||
- Pull request #33: Add TA4 High Performance Distributor (from Thomas-S)
|
||||
- Pull request #34: Add TA4 High Performance Distributor to Hopper (from Thomas-S)
|
||||
- Pull request #35: Fixed Gravel Sieve bug (from CosmicConveyor)
|
||||
- Fix doorcontroller and ta4 doser bugs
|
||||
- Add check for wind turbine areas
|
||||
- Fix translation errors
|
||||
- QSG: Add power consumptions and fix manual bug
|
||||
- Add load command to the controller battery
|
||||
- TA4 silo: Add load command
|
||||
- silo/tank: Add second return value for load command
|
||||
- Liquid Pumps: Fix issue with undetected pipe connection gaps
|
||||
- Shrink PGN files
|
||||
- Fix ta4 chest bugs
|
||||
- Fix ta4 chest and ta3 firebox issues
|
||||
- Remove repairkit recipe
|
||||
- Switched to AGPL license
|
||||
- API added for ingame manual
|
||||
|
||||
**2020-09-13 V0.23**
|
||||
- Pull request #26: Digtron Battery: Fix duplication bug (from Thomas-S)
|
||||
- Improve ta4 sensor box
|
||||
- Firebox: Add check for free space when placing the node
|
||||
- Lua controller: Add 'get_gametime' function
|
||||
- Pull request #27: Liquid Tanks: Add protection support (from Thomas-S)
|
||||
- Fix pump issue (silo source items can disappear)
|
||||
- Pull request #28: Quarry: Improve digging behaviour (from Thomas-S)
|
||||
- Pull request #28: Battery: Store battery load as metadata (from Thomas-S)
|
||||
- Pull request #29: Distributor: Keep item metadata (from Thomas-S)
|
||||
|
||||
**2020-08-08 V0.22**
|
||||
- Pull request #25: Growlight: Improve flower registration (from Thomas-S)
|
||||
- Add tube support for digtron chests and protector:chest
|
||||
|
||||
**2020-08-08 V0.21**
|
||||
- Pull request #18: Add a simple Digtron battery (from Thomas-S)
|
||||
- Pull request #23: Lua Controller: Fix $item_description() documentation and translation (from Thomas-S)
|
||||
- Pull request #24: Distributor: improve fairness by using random spread (from realmicu)
|
||||
- Bugfix: TA1 meridian hammer did not glow (from realmicu)
|
||||
- Bugfix: power.power_available() did not check the network state
|
||||
|
||||
**2020-07-31 V0.20**
|
||||
- Pull request #21: Lua Controller: Allow to get itemstring and description of 8x2000 chest contents (from Thomas-S)
|
||||
- Pull request #22: Trowel: Prevent hidden nodes from being dug (from Thomas-S)
|
||||
- Improvement: TA3 Power Terminal: Outputs max needed power in addition
|
||||
- Bugfix: Quarry: Shall not dig Techage Light Blocks
|
||||
|
||||
**2020-07-24 V0.19**
|
||||
- Pull request #19: Refactor ICTA to use functions instead of loadstring (from Thomas-S)
|
||||
- State command added for cart-, node-, and player detectors
|
||||
|
||||
**2020-07-21 V0.18**
|
||||
- Pull request #13: Use Monospace Font for Code-Related Formspecs (from Thomas-S)
|
||||
- Pull request #14: Don't allow to put items with meta or wear information into the 8x2000 chest (from Thomas-S)
|
||||
- Pull request #15: Blackhole: Add support for liquids (from Thomas-S)
|
||||
- Pull request #16: ICTA Controller: Add support for valves by adding on/off states (from Thomas-S)
|
||||
- Bugfix: Digging Redstone gives an 'unknown block'
|
||||
- ICTA Controller: Escape quotation marks for text outputs
|
||||
|
||||
**2020-07-16 V0.17**
|
||||
- TA4 Reactor recipe bugfix
|
||||
- TA3 furnace power bugfix (response to the pull request #12 from Thomas-S)
|
||||
- Manual bugfix (Thomas-S)
|
||||
- Charcoal pile doesn't start smoking after beeing unloaded (issue #9 from Skamiz)
|
||||
|
||||
**2020-07-06 V0.16**
|
||||
- Oil cracking/hydrogenation recipes added
|
||||
- Ethereal growlight bugfix
|
||||
- Charcoal pile bugfix (issue #9) Thanks to Skamiz
|
||||
- Quarry bugfix (pull request #10) Thanks to programmerjake
|
||||
|
||||
**2020-07-02 V0.15**
|
||||
- pipe valve added
|
||||
- growlight bugfix
|
||||
- further textures to gate/door blocks added
|
||||
- cement recipe bugfix
|
||||
- manual improvements
|
||||
|
||||
**2020-06-29 V0.14**
|
||||
- quarry sound bugfix
|
||||
- grinder bugfix
|
||||
- ore probability calculation changed
|
||||
- lua-marshal deactivated (due to weird server crashes)
|
||||
- alternative cement recipe added
|
||||
- aluminum output increased
|
||||
- reboiler cycle time increased to 16 s (from 6)
|
||||
- many manual improvements
|
||||
|
||||
**2020-06-19 V0.13**
|
||||
- Mesecons Converter added
|
||||
|
||||
**2020-06-17 V0.12**
|
||||
- Ethereal support added
|
||||
- manual correction
|
||||
- tin ingot recipe bugfix
|
||||
|
||||
**2020-06-14 V0.11**
|
||||
- cart commands added for both controllers
|
||||
- support for moreores added
|
||||
|
||||
**2020-06-04 V0.10**
|
||||
- minor changes and bugfixes
|
||||
|
||||
**2020-05-31 V0.09**
|
||||
- TA4 tubes upgraded, manuals updated
|
||||
|
||||
**2020-05-22 V0.08**
|
||||
- Support for 'lua-marshal' and 'lsqlite3' added
|
||||
|
||||
**2020-04-26 V0.07**
|
||||
- English translation added
|
||||
|
||||
**2020-04-24 V0.06**
|
||||
- TA4 injector added
|
||||
|
||||
**2020-03-14 V0.05**
|
||||
- TA4 Lua controller added
|
||||
|
||||
**2020-02-29 V0.04**
|
||||
- TA4 ICTA controller added
|
||||
|
||||
**2019-09-28 V0.02**
|
||||
- TA3 finished
|
||||
|
||||
**2019-06-16 V0.01**
|
||||
- First upload
|
1
techage/_config.yml
Normal file
1
techage/_config.yml
Normal file
@ -0,0 +1 @@
|
||||
theme: jekyll-theme-leap-day
|
225
techage/api.md
Normal file
225
techage/api.md
Normal file
@ -0,0 +1,225 @@
|
||||
# Techage API Functions
|
||||
|
||||
Techage API function to adapt/prepare techage for other mods/games.
|
||||
|
||||
|
||||
|
||||
## Move/Fly Controller
|
||||
|
||||
Register node names for nodes allowed to be moved by fly/move controllers.
|
||||
|
||||
This is only necessary for undiggable/intelligent nodes with one of the following attributes:
|
||||
|
||||
- ```drop = ""```
|
||||
- ```diggable = false```
|
||||
- ```after_dig_node ~= nil```
|
||||
|
||||
```lua
|
||||
techage.register_simple_nodes(node_names, is_valid)
|
||||
```
|
||||
|
||||
- `is_valid = true` - Add node to the list of simple nodes
|
||||
- `is_valid = false` - Remove node from the list of simple nodes
|
||||
|
||||
Example:
|
||||
|
||||
```lua
|
||||
techage.register_simple_nodes({"techage:power_lineS"}, true)
|
||||
```
|
||||
|
||||
For door nodes used as sliding doors by means of the move controller, call in addition:
|
||||
|
||||
```lua
|
||||
techage.flylib.protect_door_from_being_opened(node_name)
|
||||
```
|
||||
|
||||
|
||||
|
||||
## TA1 Hammer
|
||||
|
||||
Register stone/gravel name pair for the hammer blow:
|
||||
|
||||
```lua
|
||||
techage.register_stone_gravel_pair(stone_name, gravel_name)
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```lua
|
||||
techage.register_stone_gravel_pair("default:stone", "default:gravel")
|
||||
```
|
||||
|
||||
|
||||
|
||||
## TA1 Melting Pot
|
||||
|
||||
Register a pot recipe:
|
||||
|
||||
```lua
|
||||
techage.ironage_register_recipe(recipe)
|
||||
```
|
||||
|
||||
Examples:
|
||||
|
||||
```lua
|
||||
techage.ironage_register_recipe({
|
||||
output = "default:obsidian",
|
||||
recipe = {"default:cobble"},
|
||||
heat = 10, -- Corresponds to the tower height
|
||||
time = 8, -- Cooking time in seconds
|
||||
})
|
||||
techage.ironage_register_recipe({
|
||||
output = "default:bronze_ingot 4",
|
||||
recipe = {"default:copper_ingot", "default:copper_ingot", "default:copper_ingot", "default:tin_ingot"},
|
||||
heat = 4, -- Corresponds to the tower height
|
||||
time = 8, -- Cooking time in seconds
|
||||
})
|
||||
```
|
||||
|
||||
|
||||
|
||||
## TA2/TA3/TA4 Autocrafter
|
||||
|
||||
Register any nodes/items that should not be crafted via the autocrafter.
|
||||
|
||||
```lua
|
||||
techage.register_uncraftable_items(item_name)
|
||||
```
|
||||
|
||||
|
||||
|
||||
## TA2/TA3/TA4 Gravel Sieve
|
||||
|
||||
Change the probability of ores or register new ores for sieving.
|
||||
|
||||
```lua
|
||||
techage.register_ore_for_gravelsieve(ore_name, probability)
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```lua
|
||||
techage.register_ore_for_gravelsieve("default:iron_lump", 30)
|
||||
```
|
||||
|
||||
Default values for MTG are:
|
||||
|
||||
```lua
|
||||
-- higher value means less frequent occurrence
|
||||
techage:baborium_lump 100000 -- hardly ever
|
||||
default:mese_crystal 548 -- every 548th time
|
||||
default:gold_lump 439
|
||||
default:tin_lump 60
|
||||
default:diamond 843
|
||||
default:copper_lump 145
|
||||
default:coal_lump 11
|
||||
default:iron_lump 15
|
||||
```
|
||||
|
||||
|
||||
|
||||
## TA2/TA3/TA4 Gravel Rinser
|
||||
|
||||
Add a rinser recipe.
|
||||
|
||||
```lua
|
||||
techage.add_rinser_recipe(recipe)
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```lua
|
||||
techage.add_rinser_recipe({input = "techage:sieved_gravel", output = "techage:usmium_nuggets", probability = 30})
|
||||
```
|
||||
|
||||
|
||||
|
||||
## TA2/TA3/TA4 Grinder
|
||||
|
||||
Add a grinder recipe.
|
||||
|
||||
```lua
|
||||
techage.add_grinder_recipe(recipe, ta1_permitted)
|
||||
```
|
||||
|
||||
Examples:
|
||||
|
||||
```lua
|
||||
echage.add_grinder_recipe({input = "default:cobble", output = "default:gravel"})
|
||||
techage.add_grinder_recipe({input = "default:sandstone", output = "default:sand 4"})
|
||||
```
|
||||
|
||||
|
||||
|
||||
## TA3/TA4 Electronic Fab, TA4 Doser
|
||||
|
||||
Add recipes to an electronic fab or doser (chemical reactor):
|
||||
|
||||
|
||||
```lua
|
||||
techage.recipes.add(rtype, recipe)
|
||||
```
|
||||
|
||||
`rtype` is one of: `ta2_electronic_fab` , `ta4_doser`
|
||||
|
||||
A recipe look like:
|
||||
|
||||
```
|
||||
{
|
||||
output = "<item-name> <units>", -- units = 1..n
|
||||
waste = "<item-name> <units>", -- units = 1..n
|
||||
input = { -- up to 4 items
|
||||
"<item-name> <units>",
|
||||
"<item-name> <units>",
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
Examples:
|
||||
|
||||
```lua
|
||||
techage.recipes.add("ta2_electronic_fab", {
|
||||
output = "techage:vacuum_tube 2",
|
||||
waste = "basic_materials:empty_spool 1",
|
||||
input = {"default:glass 1", "basic_materials:copper_wire 1", "basic_materials:plastic_sheet 1", "techage:usmium_nuggets 1"}
|
||||
})
|
||||
|
||||
techage.recipes.add("ta4_doser", {
|
||||
output = "techage:naphtha 1",
|
||||
input = {
|
||||
"techage:fueloil 1",
|
||||
},
|
||||
catalyst = "techage:gibbsite_powder",
|
||||
})
|
||||
```
|
||||
|
||||
|
||||
|
||||
## TA3 Furnace
|
||||
|
||||
Register recipe:
|
||||
|
||||
```lua
|
||||
techage.furnace.register_recipe(recipe)
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```lua
|
||||
techage.furnace.register_recipe({
|
||||
output = "default:bronze_ingot 4",
|
||||
recipe = {"default:copper_ingot", "default:copper_ingot", "default:copper_ingot", "default:tin_ingot"},
|
||||
time = 2, -- in seconds
|
||||
})
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Assembly Tool
|
||||
|
||||
Disable a block from being removed by the assembly tool:
|
||||
|
||||
```lua
|
||||
techage.disable_block_for_assembly_tool(block_name)
|
||||
```
|
||||
|
556
techage/basic_machines/autocrafter.lua
Normal file
556
techage/basic_machines/autocrafter.lua
Normal file
@ -0,0 +1,556 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019-2023 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
The autocrafter is derived from pipeworks:
|
||||
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net> WTFPL
|
||||
|
||||
TA2/TA3/TA4 Autocrafter
|
||||
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local S2P = minetest.string_to_pos
|
||||
local M = minetest.get_meta
|
||||
-- Consumer Related Data
|
||||
local CRD = function(pos) return (minetest.registered_nodes[techage.get_node_lvm(pos).name] or {}).consumer end
|
||||
|
||||
local S = techage.S
|
||||
|
||||
local STANDBY_TICKS = 3
|
||||
local COUNTDOWN_TICKS = 4
|
||||
local CYCLE_TIME = 4
|
||||
|
||||
local UncraftableItems = {}
|
||||
|
||||
-- Add all nodes/items which should not be crafted with the autocrafter
|
||||
function techage.register_uncraftable_items(item_name)
|
||||
UncraftableItems[item_name] = true
|
||||
end
|
||||
|
||||
local function formspec(self, pos, nvm)
|
||||
return "size[8,9.2]"..
|
||||
default.gui_bg..
|
||||
default.gui_bg_img..
|
||||
default.gui_slots..
|
||||
"list[context;recipe;0,0;3,3;]"..
|
||||
"image[2.9,1;1,1;techage_form_arrow.png]"..
|
||||
"image[3.8,0;1,1;"..techage.get_power_image(pos, nvm).."]"..
|
||||
"list[context;output;3.8,1;1,1;]"..
|
||||
"image_button[3.8,2;1,1;".. self:get_state_button_image(nvm) ..";state_button;]"..
|
||||
"tooltip[3.8,2;1,1;"..self:get_state_tooltip(nvm).."]"..
|
||||
"list[context;src;0,3.2;8,2;]"..
|
||||
"list[context;dst;5,0;3,3;]"..
|
||||
"list[current_player;main;0,5.4;8,4;]" ..
|
||||
"listring[current_player;main]"..
|
||||
"listring[context;src]" ..
|
||||
"listring[current_player;main]"..
|
||||
"listring[context;dst]" ..
|
||||
"listring[current_player;main]"..
|
||||
default.get_hotbar_bg(0, 5.4)
|
||||
end
|
||||
|
||||
local function count_index(invlist)
|
||||
local index = {}
|
||||
for _, stack in pairs(invlist) do
|
||||
if not stack:is_empty() then
|
||||
local stack_name = stack:get_name()
|
||||
index[stack_name] = (index[stack_name] or 0) + stack:get_count()
|
||||
end
|
||||
end
|
||||
return index
|
||||
end
|
||||
|
||||
local function flush_input_inventory(pos)
|
||||
local inv = M(pos):get_inventory()
|
||||
if not inv:is_empty("src") then
|
||||
for idx = 1, 16 do
|
||||
local stack = inv:get_stack("src", idx)
|
||||
if not inv:room_for_item("dst", stack) then
|
||||
return false
|
||||
end
|
||||
inv:add_item("dst", stack)
|
||||
inv:set_stack("src", idx, nil)
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
-- caches some recipe data
|
||||
local autocrafterCache = {}
|
||||
|
||||
local function get_craft(pos, inventory, hash)
|
||||
hash = hash or minetest.hash_node_position(pos)
|
||||
local craft = autocrafterCache[hash]
|
||||
if not craft then
|
||||
local recipe = inventory:get_list("recipe")
|
||||
local output, decremented_input = minetest.get_craft_result(
|
||||
{method = "normal", width = 3, items = recipe})
|
||||
|
||||
-- check if registered item
|
||||
if UncraftableItems[output.item:get_name()] then
|
||||
output.item = ItemStack()
|
||||
end
|
||||
|
||||
craft = {recipe = recipe, consumption = count_index(recipe),
|
||||
output = output, decremented_input = decremented_input}
|
||||
autocrafterCache[hash] = craft
|
||||
end
|
||||
return craft
|
||||
end
|
||||
|
||||
local function autocraft(pos, crd, nvm, inv)
|
||||
local craft = get_craft(pos, inv)
|
||||
if not craft then
|
||||
crd.State:idle(pos, nvm)
|
||||
return
|
||||
end
|
||||
local output_item = craft.output.item
|
||||
if output_item:get_name() == "" then
|
||||
crd.State:idle(pos, nvm)
|
||||
return
|
||||
end
|
||||
|
||||
-- check if we have enough room in dst
|
||||
if not inv:room_for_item("dst", output_item) then
|
||||
crd.State:blocked(pos, nvm)
|
||||
return
|
||||
end
|
||||
local consumption = craft.consumption
|
||||
local inv_index = count_index(inv:get_list("src"))
|
||||
-- check if we have enough material available
|
||||
for itemname, number in pairs(consumption) do
|
||||
if (not inv_index[itemname]) or inv_index[itemname] < number then
|
||||
crd.State:idle(pos, nvm)
|
||||
return
|
||||
end
|
||||
end
|
||||
-- consume material
|
||||
for itemname, number in pairs(consumption) do
|
||||
for i = 1, number do -- We have to do that since remove_item does not work if count > stack_max
|
||||
inv:remove_item("src", ItemStack(itemname))
|
||||
end
|
||||
end
|
||||
|
||||
-- craft the result into the dst inventory and add any "replacements" as well
|
||||
inv:add_item("dst", output_item)
|
||||
for i = 1, 9 do
|
||||
inv:add_item("dst", craft.decremented_input.items[i])
|
||||
end
|
||||
|
||||
crd.State:keep_running(pos, nvm, COUNTDOWN_TICKS)
|
||||
end
|
||||
|
||||
|
||||
local function keep_running(pos, elapsed)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
local crd = CRD(pos)
|
||||
local inv = M(pos):get_inventory()
|
||||
autocraft(pos, crd, nvm, inv)
|
||||
end
|
||||
|
||||
-- note, that this function assumes allready being updated to virtual items
|
||||
-- and doesn't handle recipes with stacksizes > 1
|
||||
local function after_recipe_change(pos, inventory)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
local crd = CRD(pos)
|
||||
-- if we emptied the grid, there's no point in keeping it running or cached
|
||||
if inventory:is_empty("recipe") then
|
||||
autocrafterCache[minetest.hash_node_position(pos)] = nil
|
||||
inventory:set_stack("output", 1, "")
|
||||
crd.State:stop(pos, nvm)
|
||||
return
|
||||
end
|
||||
local recipe = inventory:get_list("recipe")
|
||||
|
||||
local hash = minetest.hash_node_position(pos)
|
||||
local craft = autocrafterCache[hash]
|
||||
|
||||
if craft then
|
||||
-- check if it changed
|
||||
local cached_recipe = craft.recipe
|
||||
for i = 1, 9 do
|
||||
if recipe[i]:get_name() ~= cached_recipe[i]:get_name() then
|
||||
autocrafterCache[hash] = nil -- invalidate recipe
|
||||
craft = nil
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
craft = craft or get_craft(pos, inventory, hash)
|
||||
local output_item = craft.output.item
|
||||
inventory:set_stack("output", 1, output_item)
|
||||
crd.State:stop(pos, nvm)
|
||||
end
|
||||
|
||||
-- clean out unknown items and groups, which would be handled like unknown items in the crafting grid
|
||||
-- if minetest supports query by group one day, this might replace them
|
||||
-- with a canonical version instead
|
||||
local function normalize(item_list)
|
||||
for i = 1, #item_list do
|
||||
local name = item_list[i]
|
||||
if not minetest.registered_items[name] then
|
||||
item_list[i] = ""
|
||||
end
|
||||
end
|
||||
return item_list
|
||||
end
|
||||
|
||||
local function get_input_from_recipeblock(pos, number, idx)
|
||||
local own_num = M(pos):get_string("node_number")
|
||||
local owner = M(pos):get_string("owner")
|
||||
if techage.check_numbers(number, owner) then
|
||||
local input = techage.send_single(own_num, number, "input", idx)
|
||||
if input and type(input) == "string" then
|
||||
return input
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function on_output_change(pos, inventory, stack)
|
||||
if not stack then
|
||||
inventory:set_list("output", {})
|
||||
inventory:set_list("recipe", {})
|
||||
else
|
||||
local input = minetest.get_craft_recipe(stack:get_name())
|
||||
if not input.items or input.type ~= "normal" then return end
|
||||
local items, width = normalize(input.items), input.width
|
||||
local item_idx, width_idx = 1, 1
|
||||
for i = 1, 9 do
|
||||
if width_idx <= width then
|
||||
inventory:set_stack("recipe", i, items[item_idx])
|
||||
item_idx = item_idx + 1
|
||||
else
|
||||
inventory:set_stack("recipe", i, ItemStack(""))
|
||||
end
|
||||
width_idx = (width_idx < 3) and (width_idx + 1) or 1
|
||||
end
|
||||
-- we'll set the output slot in after_recipe_change to the actual result of the new recipe
|
||||
end
|
||||
after_recipe_change(pos, inventory)
|
||||
end
|
||||
|
||||
local function determine_recipe_items(pos, input)
|
||||
local num, idx
|
||||
|
||||
if input and type(input) == "string" then -- Lua controller
|
||||
-- Test if "<node-number>.<recipe-number>" input
|
||||
num, idx = unpack(string.split(input, ".", false, 1))
|
||||
elseif input and type(input) == "table" then -- Beduino
|
||||
num = tostring(input[1] * 65536 + input[2])
|
||||
idx = tostring(input[3])
|
||||
end
|
||||
|
||||
if num and idx then
|
||||
input = get_input_from_recipeblock(pos, num, idx)
|
||||
if input then
|
||||
-- "<item>,<item>,..." input
|
||||
local items = string.split(input, ",", true, 8)
|
||||
if items and type(items) == "table" and next(items) then
|
||||
return items
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function on_new_recipe(pos, input)
|
||||
local items = determine_recipe_items(pos, input)
|
||||
local inv = M(pos):get_inventory()
|
||||
if items then
|
||||
for i = 1, 9 do
|
||||
inv:set_stack("recipe", i, items[i])
|
||||
end
|
||||
else
|
||||
inv:set_list("recipe", {})
|
||||
end
|
||||
local hash = minetest.hash_node_position(pos)
|
||||
autocrafterCache[hash] = nil
|
||||
local craft = get_craft(pos, inv, hash)
|
||||
if craft.output and craft.output.item then
|
||||
inv:set_stack("output", 1, craft.output.item)
|
||||
else
|
||||
inv:set_stack("output", 1, nil)
|
||||
end
|
||||
end
|
||||
|
||||
local function allow_metadata_inventory_put(pos, listname, index, stack, player)
|
||||
if listname == "output" then
|
||||
return 0
|
||||
end
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return 0
|
||||
end
|
||||
local inv = M(pos):get_inventory()
|
||||
if listname == "recipe" then
|
||||
stack:set_count(1)
|
||||
inv:set_stack(listname, index, stack)
|
||||
after_recipe_change(pos, inv)
|
||||
return 0
|
||||
elseif listname == "output" then
|
||||
on_output_change(pos, inv, stack)
|
||||
return 0
|
||||
elseif listname == "src" then
|
||||
CRD(pos).State:start_if_standby(pos)
|
||||
end
|
||||
return stack:get_count()
|
||||
end
|
||||
|
||||
local function allow_metadata_inventory_take(pos, listname, index, stack, player)
|
||||
if listname == "output" then
|
||||
return 0
|
||||
end
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return 0
|
||||
end
|
||||
-- upgrade_autocrafter(pos)
|
||||
local inv = minetest.get_meta(pos):get_inventory()
|
||||
if listname == "recipe" then
|
||||
inv:set_stack(listname, index, ItemStack(""))
|
||||
after_recipe_change(pos, inv)
|
||||
return 0
|
||||
elseif listname == "output" then
|
||||
on_output_change(pos, inv, nil)
|
||||
return 0
|
||||
end
|
||||
return stack:get_count()
|
||||
end
|
||||
|
||||
local function allow_metadata_inventory_move(pos, from_list, from_index, to_list, to_index, count, player)
|
||||
if from_list == "output" or "to_list" == "output" then
|
||||
return 0
|
||||
end
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return 0
|
||||
end
|
||||
local inv = minetest.get_meta(pos):get_inventory()
|
||||
local stack = inv:get_stack(from_list, from_index)
|
||||
|
||||
if to_list == "output" then
|
||||
on_output_change(pos, inv, stack)
|
||||
return 0
|
||||
elseif from_list == "output" then
|
||||
on_output_change(pos, inv, nil)
|
||||
if to_list ~= "recipe" then
|
||||
return 0
|
||||
end -- else fall through to recipe list handling
|
||||
end
|
||||
|
||||
if from_list == "recipe" or to_list == "recipe" then
|
||||
if from_list == "recipe" then
|
||||
inv:set_stack(from_list, from_index, ItemStack(""))
|
||||
end
|
||||
if to_list == "recipe" then
|
||||
stack:set_count(1)
|
||||
inv:set_stack(to_list, to_index, stack)
|
||||
end
|
||||
after_recipe_change(pos, inv)
|
||||
return 0
|
||||
end
|
||||
|
||||
return count
|
||||
end
|
||||
|
||||
local function on_receive_fields(pos, formname, fields, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return
|
||||
end
|
||||
local nvm = techage.get_nvm(pos)
|
||||
CRD(pos).State:state_button_event(pos, nvm, fields)
|
||||
end
|
||||
|
||||
local function can_dig(pos, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return false
|
||||
end
|
||||
local inv = M(pos):get_inventory()
|
||||
return inv:is_empty("dst") and inv:is_empty("src")
|
||||
end
|
||||
|
||||
local tiles = {}
|
||||
-- '#' will be replaced by the stage number
|
||||
-- '{power}' will be replaced by the power PNG
|
||||
tiles.pas = {
|
||||
-- up, down, right, left, back, front
|
||||
"techage_filling_ta#.png^techage_appl_autocrafter.png^techage_frame_ta#_top.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_outp.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_inp.png",
|
||||
"techage_filling_ta#.png^techage_appl_autocrafter.png^techage_frame_ta#.png",
|
||||
"techage_filling_ta#.png^techage_appl_autocrafter.png^techage_frame_ta#.png",
|
||||
}
|
||||
tiles.act = {
|
||||
-- up, down, right, left, back, front
|
||||
{
|
||||
name = "techage_filling4_ta#.png^techage_appl_autocrafter4.png^techage_frame4_ta#_top.png",
|
||||
backface_culling = false,
|
||||
animation = {
|
||||
type = "vertical_frames",
|
||||
aspect_w = 32,
|
||||
aspect_h = 32,
|
||||
length = 0.5,
|
||||
},
|
||||
},
|
||||
"techage_filling_ta#.png^techage_frame_ta#.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_outp.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_inp.png",
|
||||
{
|
||||
name = "techage_filling4_ta#.png^techage_appl_autocrafter4.png^techage_frame4_ta#.png",
|
||||
backface_culling = false,
|
||||
animation = {
|
||||
type = "vertical_frames",
|
||||
aspect_w = 32,
|
||||
aspect_h = 32,
|
||||
length = 0.5,
|
||||
},
|
||||
},
|
||||
{
|
||||
name = "techage_filling4_ta#.png^techage_appl_autocrafter4.png^techage_frame4_ta#.png",
|
||||
backface_culling = false,
|
||||
animation = {
|
||||
type = "vertical_frames",
|
||||
aspect_w = 32,
|
||||
aspect_h = 32,
|
||||
length = 0.5,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
local INFO = [[Commands: 'state', 'recipe']]
|
||||
|
||||
local tubing = {
|
||||
on_inv_request = function(pos, in_dir, access_type)
|
||||
if access_type == "push" then
|
||||
local meta = minetest.get_meta(pos)
|
||||
if meta:get_int("push_dir") == in_dir or in_dir == 5 then
|
||||
return meta:get_inventory(), "src"
|
||||
end
|
||||
end
|
||||
end,
|
||||
on_pull_item = function(pos, in_dir, num)
|
||||
local meta = minetest.get_meta(pos)
|
||||
if meta:get_int("pull_dir") == in_dir then
|
||||
local inv = M(pos):get_inventory()
|
||||
return techage.get_items(pos, inv, "dst", num)
|
||||
end
|
||||
end,
|
||||
on_push_item = function(pos, in_dir, stack, idx)
|
||||
local meta = minetest.get_meta(pos)
|
||||
if meta:get_int("push_dir") == in_dir or in_dir == 5 then
|
||||
local inv = M(pos):get_inventory()
|
||||
--CRD(pos).State:start_if_standby(pos) -- would need power!
|
||||
return techage.put_items(inv, "src", stack, idx)
|
||||
end
|
||||
end,
|
||||
on_unpull_item = function(pos, in_dir, stack)
|
||||
local meta = minetest.get_meta(pos)
|
||||
if meta:get_int("pull_dir") == in_dir then
|
||||
local inv = M(pos):get_inventory()
|
||||
return techage.put_items(inv, "dst", stack)
|
||||
end
|
||||
end,
|
||||
on_recv_message = function(pos, src, topic, payload)
|
||||
if topic == "recipe" and CRD(pos).stage == 4 then
|
||||
if payload and payload ~= "" then
|
||||
on_new_recipe(pos, payload)
|
||||
return true
|
||||
else
|
||||
local inv = M(pos):get_inventory()
|
||||
return inv:get_stack("output", 1):get_name()
|
||||
end
|
||||
elseif topic == "flush" and CRD(pos).stage == 4 then
|
||||
return flush_input_inventory(pos)
|
||||
elseif topic == "info" and CRD(pos).stage == 4 then
|
||||
return INFO
|
||||
else
|
||||
return CRD(pos).State:on_receive_message(pos, topic, payload)
|
||||
end
|
||||
end,
|
||||
on_beduino_receive_cmnd = function(pos, src, topic, payload)
|
||||
if topic == 10 and CRD(pos).stage == 4 then
|
||||
on_new_recipe(pos, payload)
|
||||
return 1, ""
|
||||
elseif topic == 11 and CRD(pos).stage == 4 then
|
||||
if flush_input_inventory(pos) then
|
||||
return 1, ""
|
||||
else
|
||||
return 0, ""
|
||||
end
|
||||
end
|
||||
return CRD(pos).State:on_beduino_receive_cmnd(pos, topic, payload)
|
||||
end,
|
||||
on_beduino_request_data = function(pos, src, topic, payload)
|
||||
return CRD(pos).State:on_beduino_request_data(pos, topic, payload)
|
||||
end,
|
||||
on_node_load = function(pos)
|
||||
CRD(pos).State:on_node_load(pos)
|
||||
end,
|
||||
}
|
||||
|
||||
local node_name_ta2, node_name_ta3, node_name_ta4 =
|
||||
techage.register_consumer("autocrafter", S("Autocrafter"), tiles, {
|
||||
drawtype = "normal",
|
||||
cycle_time = CYCLE_TIME,
|
||||
standby_ticks = STANDBY_TICKS,
|
||||
formspec = formspec,
|
||||
tubing = tubing,
|
||||
after_place_node = function(pos, placer)
|
||||
local inv = M(pos):get_inventory()
|
||||
inv:set_size("src", 2*8)
|
||||
inv:set_size("recipe", 3*3)
|
||||
inv:set_size("dst", 3*3)
|
||||
inv:set_size("output", 1)
|
||||
end,
|
||||
can_dig = can_dig,
|
||||
node_timer = keep_running,
|
||||
on_receive_fields = on_receive_fields,
|
||||
allow_metadata_inventory_put = allow_metadata_inventory_put,
|
||||
allow_metadata_inventory_move = allow_metadata_inventory_move,
|
||||
allow_metadata_inventory_take = allow_metadata_inventory_take,
|
||||
groups = {choppy=2, cracky=2, crumbly=2},
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
num_items = {0,1,2,4},
|
||||
power_consumption = {0,4,6,9},
|
||||
},
|
||||
{false, true, true, true}) -- TA2/TA3/TA4
|
||||
|
||||
minetest.register_craft({
|
||||
output = node_name_ta2,
|
||||
recipe = {
|
||||
{"group:wood", "default:diamond", "group:wood"},
|
||||
{"techage:tubeS", "basic_materials:gear_steel", "techage:tubeS"},
|
||||
{"group:wood", "techage:iron_ingot", "group:wood"},
|
||||
},
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = node_name_ta3,
|
||||
recipe = {
|
||||
{"", "default:diamond", ""},
|
||||
{"", node_name_ta2, ""},
|
||||
{"", "techage:vacuum_tube", ""},
|
||||
},
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = node_name_ta4,
|
||||
recipe = {
|
||||
{"", "default:diamond", ""},
|
||||
{"", node_name_ta3, ""},
|
||||
{"", "techage:ta4_wlanchip", ""},
|
||||
},
|
||||
})
|
||||
|
||||
local Cable = techage.ElectricCable
|
||||
local power = networks.power
|
||||
|
||||
techage.register_node_for_v1_transition({"techage:ta3_autocrafter_pas", "techage:ta4_autocrafter_pas"}, function(pos, node)
|
||||
power.update_network(pos, nil, Cable)
|
||||
end)
|
89
techage/basic_machines/blackhole.lua
Normal file
89
techage/basic_machines/blackhole.lua
Normal file
@ -0,0 +1,89 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
All items and liquids disappear.
|
||||
|
||||
]]--
|
||||
|
||||
local S = techage.S
|
||||
local Pipe = techage.LiquidPipe
|
||||
local liquid = networks.liquid
|
||||
|
||||
local function take_liquid(pos, indir, name, amount)
|
||||
return 0, name
|
||||
end
|
||||
|
||||
local function put_liquid(pos, indir, name, amount)
|
||||
return 0
|
||||
end
|
||||
|
||||
local function peek_liquid(pos, indir)
|
||||
return nil
|
||||
end
|
||||
|
||||
minetest.register_node("techage:blackhole", {
|
||||
description = S("TechAge Black Hole"),
|
||||
tiles = {
|
||||
-- up, down, right, left, back, front
|
||||
"techage_filling_ta2.png^techage_frame_ta2.png",
|
||||
"techage_filling_ta2.png^techage_frame_ta2.png",
|
||||
"techage_filling_ta2.png^techage_frame_ta2.png^techage_appl_blackhole.png^techage_appl_hole_pipe.png",
|
||||
"techage_filling_ta2.png^techage_frame_ta2.png^techage_appl_blackhole.png^techage_appl_inp.png",
|
||||
"techage_filling_ta2.png^techage_frame_ta2.png^techage_appl_blackhole.png",
|
||||
"techage_filling_ta2.png^techage_frame_ta2.png^techage_appl_blackhole.png",
|
||||
},
|
||||
|
||||
after_place_node = function(pos, placer)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local node = minetest.get_node(pos)
|
||||
meta:set_int("push_dir", techage.side_to_indir("L", node.param2))
|
||||
meta:set_string("infotext", S("TechAge Black Hole (let items and liquids disappear)"))
|
||||
Pipe:after_place_node(pos)
|
||||
end,
|
||||
after_dig_node = function(pos, oldnode)
|
||||
Pipe:after_dig_node(pos)
|
||||
end,
|
||||
|
||||
on_rotate = screwdriver.disallow,
|
||||
paramtype2 = "facedir",
|
||||
groups = {choppy=2, cracky=2, crumbly=2},
|
||||
is_ground_content = false,
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "techage:blackhole",
|
||||
recipe = {
|
||||
{"group:wood", "", "group:wood"},
|
||||
{"techage:tubeS", "default:coal_lump", "techage:ta3_pipeS"},
|
||||
{"group:wood", "techage:iron_ingot", "group:wood"},
|
||||
},
|
||||
})
|
||||
|
||||
techage.register_node({"techage:blackhole"}, {
|
||||
on_pull_item = nil, -- not needed
|
||||
on_unpull_item = nil, -- not needed
|
||||
|
||||
on_push_item = function(pos, in_dir, stack)
|
||||
local meta = minetest.get_meta(pos)
|
||||
if meta:get_int("push_dir") == in_dir then
|
||||
return true
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
liquid.register_nodes({"techage:blackhole"},
|
||||
Pipe, "tank", {"R"}, {
|
||||
capa = 9999999,
|
||||
peek = peek_liquid,
|
||||
put = put_liquid,
|
||||
take = take_liquid,
|
||||
}
|
||||
)
|
463
techage/basic_machines/chest.lua
Normal file
463
techage/basic_machines/chest.lua
Normal file
@ -0,0 +1,463 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019-2022 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
TA2/TA3/TA4 Chest
|
||||
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local M = minetest.get_meta
|
||||
local S = techage.S
|
||||
|
||||
local TA4_INV_SIZE = 50
|
||||
|
||||
local MP = minetest.get_modpath(minetest.get_current_modname())
|
||||
local mConf = dofile(MP.."/basis/conf_inv.lua")
|
||||
|
||||
local function allow_metadata_inventory_put(pos, listname, index, stack, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return 0
|
||||
end
|
||||
return stack:get_count()
|
||||
end
|
||||
|
||||
local function allow_metadata_inventory_take(pos, listname, index, stack, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return 0
|
||||
end
|
||||
return stack:get_count()
|
||||
end
|
||||
|
||||
local function can_dig(pos, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return false
|
||||
end
|
||||
local inv = minetest.get_meta(pos):get_inventory()
|
||||
return inv:is_empty("main")
|
||||
end
|
||||
|
||||
local function after_dig_node(pos, oldnode, oldmetadata, digger)
|
||||
techage.remove_node(pos, oldnode, oldmetadata)
|
||||
techage.del_mem(pos)
|
||||
end
|
||||
|
||||
local function formspec2()
|
||||
return "size[9,8]"..
|
||||
default.gui_bg..
|
||||
default.gui_bg_img..
|
||||
default.gui_slots..
|
||||
"list[context;main;0.5,0;8,4;]"..
|
||||
"list[current_player;main;0.5,4.3;8,4;]"..
|
||||
"listring[context;main]"..
|
||||
"listring[current_player;main]"
|
||||
end
|
||||
|
||||
minetest.register_node("techage:chest_ta2", {
|
||||
description = S("TA2 Protected Chest"),
|
||||
tiles = {
|
||||
-- up, down, right, left, back, front
|
||||
"techage_filling_ta2.png^techage_frame_ta2.png",
|
||||
"techage_filling_ta2.png^techage_frame_ta2.png",
|
||||
"techage_filling_ta2.png^techage_frame_ta2.png^techage_appl_chest_back_ta3.png",
|
||||
"techage_filling_ta2.png^techage_frame_ta2.png^techage_appl_chest_back_ta3.png",
|
||||
"techage_filling_ta2.png^techage_frame_ta2.png^techage_appl_chest_back_ta3.png",
|
||||
"techage_filling_ta2.png^techage_frame_ta2.png^techage_appl_chest_front_ta3.png",
|
||||
},
|
||||
|
||||
on_construct = function(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
inv:set_size('main', 32)
|
||||
end,
|
||||
|
||||
after_place_node = function(pos, placer)
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:set_string("owner", placer:get_player_name())
|
||||
meta:set_string("formspec", formspec2())
|
||||
end,
|
||||
|
||||
techage_set_numbers = function(pos, numbers, player_name)
|
||||
return techage.logic.set_numbers(pos, numbers, player_name, S("TA2 Protected Chest"))
|
||||
end,
|
||||
|
||||
can_dig = can_dig,
|
||||
after_dig_node = after_dig_node,
|
||||
allow_metadata_inventory_put = allow_metadata_inventory_put,
|
||||
allow_metadata_inventory_take = allow_metadata_inventory_take,
|
||||
|
||||
paramtype2 = "facedir",
|
||||
groups = {choppy=2, cracky=2, crumbly=2},
|
||||
is_ground_content = false,
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
})
|
||||
|
||||
local function formspec3()
|
||||
return "size[10,8]"..
|
||||
default.gui_bg..
|
||||
default.gui_bg_img..
|
||||
default.gui_slots..
|
||||
"list[context;main;0,0;10,4;]"..
|
||||
"list[current_player;main;1,4.3;8,4;]"..
|
||||
"listring[context;main]"..
|
||||
"listring[current_player;main]"
|
||||
end
|
||||
|
||||
minetest.register_node("techage:chest_ta3", {
|
||||
description = S("TA3 Protected Chest"),
|
||||
tiles = {
|
||||
-- up, down, right, left, back, front
|
||||
"techage_filling_ta3.png^techage_frame_ta3.png",
|
||||
"techage_filling_ta3.png^techage_frame_ta3.png",
|
||||
"techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_chest_back_ta3.png",
|
||||
"techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_chest_back_ta3.png",
|
||||
"techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_chest_back_ta3.png",
|
||||
"techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_chest_front_ta3.png",
|
||||
},
|
||||
|
||||
on_construct = function(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
inv:set_size('main', 40)
|
||||
end,
|
||||
|
||||
after_place_node = function(pos, placer)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local number = techage.add_node(pos, "techage:chest_ta3")
|
||||
meta:set_string("node_number", number)
|
||||
meta:set_string("owner", placer:get_player_name())
|
||||
meta:set_string("formspec", formspec3())
|
||||
meta:set_string("infotext", S("TA3 Protected Chest").." "..number)
|
||||
end,
|
||||
|
||||
techage_set_numbers = function(pos, numbers, player_name)
|
||||
return techage.logic.set_numbers(pos, numbers, player_name, S("TA3 Protected Chest"))
|
||||
end,
|
||||
|
||||
can_dig = can_dig,
|
||||
after_dig_node = after_dig_node,
|
||||
allow_metadata_inventory_put = allow_metadata_inventory_put,
|
||||
allow_metadata_inventory_take = allow_metadata_inventory_take,
|
||||
|
||||
paramtype2 = "facedir",
|
||||
groups = {choppy=2, cracky=2, crumbly=2},
|
||||
is_ground_content = false,
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
})
|
||||
|
||||
techage.register_node({"techage:chest_ta2", "techage:chest_ta3"}, {
|
||||
on_inv_request = function(pos, in_dir, access_type)
|
||||
local meta = minetest.get_meta(pos)
|
||||
return meta:get_inventory(), "main"
|
||||
end,
|
||||
on_pull_item = function(pos, in_dir, num, item_name)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
return techage.get_items(pos, inv, "main", num)
|
||||
end,
|
||||
on_push_item = function(pos, in_dir, stack)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
return techage.put_items(inv, "main", stack)
|
||||
end,
|
||||
on_unpull_item = function(pos, in_dir, stack)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
return techage.put_items(inv, "main", stack)
|
||||
end,
|
||||
on_recv_message = function(pos, src, topic, payload)
|
||||
if topic == "state" then
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
return techage.get_inv_state(inv, "main")
|
||||
else
|
||||
return "unsupported"
|
||||
end
|
||||
end,
|
||||
on_beduino_request_data = function(pos, src, topic, payload)
|
||||
if topic == 131 then
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
return 0, {techage.get_inv_state_num(inv, "main")}
|
||||
else
|
||||
return 2, ""
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
|
||||
local function formspec4(pos)
|
||||
return "size[10,9]"..
|
||||
"tabheader[0,0;tab;"..S("Inventory,Pre-Assignment,Config")..";1;;true]"..
|
||||
default.gui_bg..
|
||||
default.gui_bg_img..
|
||||
default.gui_slots..
|
||||
"list[context;main;0,0;10,5;]"..
|
||||
mConf.preassigned_stacks(pos, 10, 5)..
|
||||
"list[current_player;main;1,5.3;8,4;]"..
|
||||
"listring[context;main]"..
|
||||
"listring[current_player;main]"
|
||||
end
|
||||
|
||||
local function formspec4_pre(pos)
|
||||
return "size[10,9]"..
|
||||
"tabheader[0,0;tab;"..S("Inventory,Pre-Assignment,Config")..";2;;true]"..
|
||||
default.gui_bg..
|
||||
default.gui_bg_img..
|
||||
default.gui_slots..
|
||||
"list[context;conf;0,0;10,5;]"..
|
||||
"list[current_player;main;1,5.3;8,4;]"..
|
||||
"listring[context;conf]"..
|
||||
"listring[current_player;main]"
|
||||
end
|
||||
|
||||
local function formspec4_cfg(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local label = meta:get_string("label") or ""
|
||||
local public = dump((meta:get_int("public") or 0) == 1)
|
||||
return "size[10,5]"..
|
||||
"tabheader[0,0;tab;"..S("Inventory,Pre-Assignment,Config")..";3;;true]"..
|
||||
default.gui_bg..
|
||||
default.gui_bg_img..
|
||||
default.gui_slots..
|
||||
"field[0.5,1;9,1;label;"..S("Node label:")..";"..label.."]" ..
|
||||
"checkbox[1,2;public;"..S("Allow public access to the chest")..";"..public.."]"..
|
||||
"button_exit[3.5,4;3,1;exit;"..S("Save").."]"
|
||||
end
|
||||
|
||||
local function ta4_allow_metadata_inventory_put(pos, listname, index, stack, player)
|
||||
local public = M(pos):get_int("public") == 1
|
||||
if not public and minetest.is_protected(pos, player:get_player_name()) then
|
||||
return 0
|
||||
end
|
||||
|
||||
if listname == "main" then
|
||||
return stack:get_count()
|
||||
else
|
||||
return mConf.allow_conf_inv_put(pos, listname, index, stack, player)
|
||||
end
|
||||
end
|
||||
|
||||
local function ta4_allow_metadata_inventory_take(pos, listname, index, stack, player)
|
||||
local public = M(pos):get_int("public") == 1
|
||||
if not public and minetest.is_protected(pos, player:get_player_name()) then
|
||||
return 0
|
||||
end
|
||||
|
||||
if listname == "main" then
|
||||
return stack:get_count()
|
||||
else
|
||||
return mConf.allow_conf_inv_take(pos, listname, index, stack, player)
|
||||
end
|
||||
end
|
||||
|
||||
local function ta4_allow_metadata_inventory_move(pos, from_list, from_index, to_list, to_index, count, player)
|
||||
local public = M(pos):get_int("public") == 1
|
||||
if not public and minetest.is_protected(pos, player:get_player_name()) then
|
||||
return 0
|
||||
end
|
||||
|
||||
if from_list == "main" then
|
||||
return count
|
||||
else
|
||||
return mConf.allow_conf_inv_move(pos, from_list, from_index, to_list, to_index, count, player)
|
||||
end
|
||||
end
|
||||
|
||||
minetest.register_node("techage:chest_ta4", {
|
||||
description = S("TA4 Protected Chest"),
|
||||
tiles = {
|
||||
-- up, down, right, left, back, front
|
||||
"techage_filling_ta4.png^techage_frame_ta4_top.png",
|
||||
"techage_filling_ta4.png^techage_frame_ta4.png",
|
||||
"techage_filling_ta4.png^techage_frame_ta4.png^techage_appl_chest_back_ta4.png",
|
||||
"techage_filling_ta4.png^techage_frame_ta4.png^techage_appl_chest_back_ta4.png",
|
||||
"techage_filling_ta4.png^techage_frame_ta4.png^techage_appl_chest_back_ta4.png",
|
||||
"techage_filling_ta4.png^techage_frame_ta4.png^techage_appl_chest_front_ta4.png",
|
||||
},
|
||||
|
||||
on_construct = function(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
inv:set_size('main', 50)
|
||||
inv:set_size('conf', 50)
|
||||
end,
|
||||
|
||||
after_place_node = function(pos, placer)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local number = techage.add_node(pos, "techage:chest_ta4")
|
||||
meta:set_string("node_number", number)
|
||||
meta:set_string("owner", placer:get_player_name())
|
||||
meta:set_string("formspec", formspec4(pos))
|
||||
meta:set_string("infotext", S("TA4 Protected Chest").." "..number)
|
||||
end,
|
||||
|
||||
on_receive_fields = function(pos, formname, fields, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return
|
||||
end
|
||||
|
||||
local meta = minetest.get_meta(pos)
|
||||
local mem = techage.get_mem(pos)
|
||||
if fields.tab == "1" then
|
||||
mem.filter = nil
|
||||
meta:set_string("formspec", formspec4(pos))
|
||||
elseif fields.tab == "2" then
|
||||
meta:set_string("formspec", formspec4_pre(pos))
|
||||
elseif fields.tab == "3" then
|
||||
meta:set_string("formspec", formspec4_cfg(pos))
|
||||
elseif fields.quit == "true" then
|
||||
mem.filter = nil
|
||||
end
|
||||
if fields.public then
|
||||
meta:set_int("public", fields.public == "true" and 1 or 0)
|
||||
end
|
||||
if fields.exit then
|
||||
local number = meta:get_string("node_number")
|
||||
if fields.label ~= "" then
|
||||
meta:set_string("infotext", minetest.formspec_escape(fields.label).." #"..number)
|
||||
else
|
||||
meta:set_string("infotext", S("TA4 Protected Chest").." "..number)
|
||||
end
|
||||
meta:set_string("label", fields.label)
|
||||
meta:set_string("formspec", formspec4_cfg(pos))
|
||||
end
|
||||
end,
|
||||
|
||||
techage_set_numbers = function(pos, numbers, player_name)
|
||||
return techage.logic.set_numbers(pos, numbers, player_name, S("TA4 Protected Chest"))
|
||||
end,
|
||||
|
||||
can_dig = can_dig,
|
||||
after_dig_node = after_dig_node,
|
||||
allow_metadata_inventory_put = ta4_allow_metadata_inventory_put,
|
||||
allow_metadata_inventory_take = ta4_allow_metadata_inventory_take,
|
||||
allow_metadata_inventory_move = ta4_allow_metadata_inventory_move,
|
||||
|
||||
paramtype2 = "facedir",
|
||||
groups = {choppy=2, cracky=2, crumbly=2},
|
||||
is_ground_content = false,
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
})
|
||||
|
||||
|
||||
techage.register_node({"techage:chest_ta4"}, {
|
||||
on_inv_request = function(pos, in_dir, access_type)
|
||||
local meta = minetest.get_meta(pos)
|
||||
return meta:get_inventory(), "main"
|
||||
end,
|
||||
on_pull_item = function(pos, in_dir, num, item_name)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
local mem = techage.get_mem(pos)
|
||||
|
||||
mem.filter = mem.filter or mConf.item_filter(pos, TA4_INV_SIZE)
|
||||
mem.chest_configured = mem.chest_configured or not inv:is_empty("conf")
|
||||
|
||||
if inv:is_empty("main") then
|
||||
return nil
|
||||
end
|
||||
|
||||
if item_name then
|
||||
if mem.filter[item_name] or not mem.chest_configured then
|
||||
local taken = inv:remove_item("main", {name = item_name, count = num})
|
||||
if taken:get_count() > 0 then
|
||||
return taken
|
||||
end
|
||||
end
|
||||
else -- no item given
|
||||
if mem.chest_configured then
|
||||
return mConf.take_item(pos, inv, "main", num, mem.filter["unconfigured"])
|
||||
else
|
||||
return techage.get_items(pos, inv, "main", num)
|
||||
end
|
||||
end
|
||||
end,
|
||||
on_push_item = function(pos, in_dir, item, idx)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
local mem = techage.get_mem(pos)
|
||||
|
||||
mem.filter = mem.filter or mConf.item_filter(pos, TA4_INV_SIZE)
|
||||
mem.chest_configured = mem.chest_configured or not inv:is_empty("conf")
|
||||
|
||||
if mem.chest_configured then
|
||||
local name = item:get_name()
|
||||
local stacks = mem.filter[name] or mem.filter["unconfigured"]
|
||||
return mConf.put_items(pos, inv, "main", item, stacks, idx)
|
||||
else
|
||||
return techage.put_items(inv, "main", item, idx)
|
||||
end
|
||||
end,
|
||||
on_unpull_item = function(pos, in_dir, item)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
local mem = techage.get_mem(pos)
|
||||
|
||||
mem.filter = mem.filter or mConf.item_filter(pos, TA4_INV_SIZE)
|
||||
mem.chest_configured = mem.chest_configured or not inv:is_empty("conf")
|
||||
|
||||
if mem.chest_configured then
|
||||
local name = item:get_name()
|
||||
local stacks = mem.filter[name] or mem.filter["unconfigured"]
|
||||
return mConf.put_items(pos, inv, "main", item, stacks)
|
||||
else
|
||||
return techage.put_items(inv, "main", item)
|
||||
end
|
||||
end,
|
||||
|
||||
on_recv_message = function(pos, src, topic, payload)
|
||||
if topic == "state" then
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
return techage.get_inv_state(inv, "main")
|
||||
else
|
||||
return "unsupported"
|
||||
end
|
||||
end,
|
||||
on_beduino_request_data = function(pos, src, topic, payload)
|
||||
if topic == 131 then
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
return 0, {techage.get_inv_state_num(inv, "main")}
|
||||
else
|
||||
return 2, ""
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
type = "shapeless",
|
||||
output = "techage:chest_ta2",
|
||||
recipe = {"default:chest", "techage:tubeS", "techage:iron_ingot"}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
type = "shapeless",
|
||||
output = "techage:chest_ta2",
|
||||
recipe = {"default:chest_locked", "techage:tubeS", "techage:iron_ingot"}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
type = "shapeless",
|
||||
output = "techage:chest_ta2",
|
||||
recipe = {"protector:chest", "techage:tubeS", "techage:iron_ingot"}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
type = "shapeless",
|
||||
output = "techage:chest_ta3",
|
||||
recipe = {"techage:chest_ta2", "default:chest"}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
type = "shapeless",
|
||||
output = "techage:chest_ta4",
|
||||
recipe = {"techage:chest_ta3", "default:chest"}
|
||||
})
|
154
techage/basic_machines/concentrator.lua
Normal file
154
techage/basic_machines/concentrator.lua
Normal file
@ -0,0 +1,154 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019-2022 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
Tube Concentrator
|
||||
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local M = minetest.get_meta
|
||||
local S = techage.S
|
||||
|
||||
local Tube = techage.Tube
|
||||
|
||||
local size = 2/8
|
||||
local Boxes = {
|
||||
{{-size, -size, size, size, size, 0.5 }}, -- z+
|
||||
{{-size, -size, -size, 0.5, size, size}}, -- x+
|
||||
{{-size, -size, -0.5, size, size, size}}, -- z-
|
||||
{{-0.5, -size, -size, size, size, size}}, -- x-
|
||||
{{-size, -0.5, -size, size, size, size}}, -- y-
|
||||
{{-size, -size, -size, size, 0.5, size}}, -- y+
|
||||
}
|
||||
|
||||
local names = networks.register_junction("techage:concentrator", 2/8, Boxes, Tube, {
|
||||
description = S("Tube Concentrator"),
|
||||
tiles = {
|
||||
"techage_tube_junction.png^techage_appl_arrow2.png^[transformR270",
|
||||
"techage_tube_junction.png^techage_appl_arrow2.png^[transformR270",
|
||||
"techage_tube_junction.png^techage_tube_hole.png",
|
||||
"techage_tube_junction.png",
|
||||
"techage_tube_junction.png^techage_appl_arrow2.png^[transformR90",
|
||||
"techage_tube_junction.png^techage_appl_arrow2.png^[transformR270",
|
||||
},
|
||||
paramtype2 = "facedir", -- important!
|
||||
use_texture_alpha = techage.CLIP,
|
||||
groups = {snappy = 2, choppy = 2, oddly_breakable_by_hand = 3, techage_trowel = 1},
|
||||
sounds = default.node_sound_defaults(),
|
||||
|
||||
after_place_node = function(pos, placer, itemstack, pointed_thing)
|
||||
local node = minetest.get_node(pos)
|
||||
local name = "techage:concentrator"..networks.junction_type(pos, Tube, "R", node.param2)
|
||||
minetest.swap_node(pos, {name = name, param2 = node.param2})
|
||||
M(pos):set_int("push_dir", techage.side_to_outdir("R", node.param2))
|
||||
Tube:after_place_node(pos)
|
||||
end,
|
||||
tubelib2_on_update2 = function(pos, dir1, tlib2, node)
|
||||
local name = "techage:concentrator"..networks.junction_type(pos, Tube, "R", node.param2)
|
||||
minetest.swap_node(pos, {name = name, param2 = node.param2})
|
||||
end,
|
||||
ta_rotate_node = function(pos, node, new_param2)
|
||||
Tube:after_dig_node(pos)
|
||||
minetest.swap_node(pos, {name = node.name, param2 = new_param2})
|
||||
Tube:after_place_node(pos)
|
||||
M(pos):set_int("push_dir", techage.side_to_outdir("R", new_param2))
|
||||
end,
|
||||
after_dig_node = function(pos, oldnode, oldmetadata, digger)
|
||||
Tube:after_dig_node(pos)
|
||||
end,
|
||||
}, 27)
|
||||
|
||||
for _, name in ipairs(names) do
|
||||
Tube:set_valid_sides(name, {"B", "R", "F", "L", "D", "U"})
|
||||
end
|
||||
|
||||
techage.register_node(names, {
|
||||
on_push_item = function(pos, in_dir, stack)
|
||||
local push_dir = M(pos):get_int("push_dir")
|
||||
if networks.Flip[push_dir] ~= in_dir then
|
||||
return techage.safe_push_items(pos, push_dir, stack)
|
||||
else
|
||||
return stack
|
||||
end
|
||||
end,
|
||||
is_pusher = true, -- is a pulling/pushing node
|
||||
})
|
||||
|
||||
names = networks.register_junction("techage:ta4_concentrator", 2/8, Boxes, Tube, {
|
||||
description = S("TA4 Tube Concentrator"),
|
||||
tiles = {
|
||||
"techage_tubeta4_junction.png^techage_appl_arrow2.png^[transformR270",
|
||||
"techage_tubeta4_junction.png^techage_appl_arrow2.png^[transformR270",
|
||||
"techage_tubeta4_junction.png^techage_tube_hole.png",
|
||||
"techage_tubeta4_junction.png",
|
||||
"techage_tubeta4_junction.png^techage_appl_arrow2.png^[transformR90",
|
||||
"techage_tubeta4_junction.png^techage_appl_arrow2.png^[transformR270",
|
||||
},
|
||||
paramtype2 = "facedir", -- important!
|
||||
use_texture_alpha = techage.CLIP,
|
||||
groups = {snappy = 2, choppy = 2, oddly_breakable_by_hand = 3, techage_trowel = 1},
|
||||
sounds = default.node_sound_defaults(),
|
||||
|
||||
after_place_node = function(pos, placer, itemstack, pointed_thing)
|
||||
local node = minetest.get_node(pos)
|
||||
local name = "techage:ta4_concentrator"..networks.junction_type(pos, Tube, "R", node.param2)
|
||||
minetest.swap_node(pos, {name = name, param2 = node.param2})
|
||||
M(pos):set_int("push_dir", techage.side_to_outdir("R", node.param2))
|
||||
Tube:after_place_node(pos)
|
||||
end,
|
||||
tubelib2_on_update2 = function(pos, dir1, tlib2, node)
|
||||
local name = "techage:ta4_concentrator"..networks.junction_type(pos, Tube, "R", node.param2)
|
||||
minetest.swap_node(pos, {name = name, param2 = node.param2})
|
||||
end,
|
||||
ta_rotate_node = function(pos, node, new_param2)
|
||||
Tube:after_dig_node(pos)
|
||||
minetest.swap_node(pos, {name = node.name, param2 = new_param2})
|
||||
Tube:after_place_node(pos)
|
||||
M(pos):set_int("push_dir", techage.side_to_outdir("R", new_param2))
|
||||
end,
|
||||
after_dig_node = function(pos, oldnode, oldmetadata, digger)
|
||||
Tube:after_dig_node(pos)
|
||||
end,
|
||||
}, 27)
|
||||
|
||||
for _, name in ipairs(names) do
|
||||
Tube:set_valid_sides(name, {"B", "R", "F", "L", "D", "U"})
|
||||
end
|
||||
|
||||
techage.register_node(names, {
|
||||
on_push_item = function(pos, in_dir, stack)
|
||||
local push_dir = M(pos):get_int("push_dir")
|
||||
if networks.Flip[push_dir] ~= in_dir then
|
||||
return techage.safe_push_items(pos, push_dir, stack)
|
||||
else
|
||||
return stack
|
||||
end
|
||||
end,
|
||||
is_pusher = true, -- is a pulling/pushing node
|
||||
})
|
||||
|
||||
|
||||
minetest.register_craft({
|
||||
output = "techage:concentrator27",
|
||||
recipe = {
|
||||
{"", "techage:tubeS", ""},
|
||||
{"techage:tubeS", "", "techage:tubeS"},
|
||||
{"", "techage:tubeS", ""},
|
||||
},
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "techage:ta4_concentrator27",
|
||||
recipe = {
|
||||
{"", "techage:ta4_tubeS", ""},
|
||||
{"techage:ta4_tubeS", "", "techage:ta4_tubeS"},
|
||||
{"", "techage:ta4_tubeS", ""},
|
||||
},
|
||||
})
|
314
techage/basic_machines/consumer.lua
Normal file
314
techage/basic_machines/consumer.lua
Normal file
@ -0,0 +1,314 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019-2022 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
Consumer node basis functionality.
|
||||
It handles:
|
||||
- up to 4 stages of nodes (TA2/TA3/TA4/TA5)
|
||||
- power consumption
|
||||
- node state handling
|
||||
- registration of passive and active nodes
|
||||
- Tube connections are on left and right side (from left to right)
|
||||
- Power connection are on front and back side (front or back)
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local P = minetest.string_to_pos
|
||||
local M = minetest.get_meta
|
||||
|
||||
-- Consumer Related Data
|
||||
local CRD = function(pos) return (minetest.registered_nodes[techage.get_node_lvm(pos).name] or {}).consumer end
|
||||
local CRDN = function(node) return (minetest.registered_nodes[node.name] or {}).consumer end
|
||||
|
||||
local Tube = techage.Tube
|
||||
local power = networks.power
|
||||
local liquid = networks.liquid
|
||||
local CYCLE_TIME = 2
|
||||
|
||||
local function get_keys(tbl)
|
||||
local keys = {}
|
||||
for k,v in pairs(tbl) do
|
||||
keys[#keys + 1] = k
|
||||
end
|
||||
return keys
|
||||
end
|
||||
|
||||
local function has_power(pos, nvm, state)
|
||||
local crd = CRD(pos)
|
||||
return power.power_available(pos, crd.power_netw)
|
||||
end
|
||||
|
||||
local function start_node(pos, nvm, state)
|
||||
end
|
||||
|
||||
local function stop_node(pos, nvm, state)
|
||||
end
|
||||
|
||||
local function node_timer_pas(pos, elapsed)
|
||||
local crd = CRD(pos)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
|
||||
-- handle power consumption
|
||||
if crd.power_netw and techage.needs_power(nvm) then
|
||||
local consumed = power.consume_power(pos, crd.power_netw, nil, crd.power_consumption)
|
||||
if consumed == crd.power_consumption then
|
||||
crd.State:start(pos, nvm)
|
||||
end
|
||||
end
|
||||
|
||||
-- call the node timer routine
|
||||
if techage.is_operational(nvm) then
|
||||
nvm.node_timer_call_cyle = (nvm.node_timer_call_cyle or 0) + 1
|
||||
if nvm.node_timer_call_cyle >= crd.call_cycle then
|
||||
crd.node_timer(pos, crd.cycle_time)
|
||||
nvm.node_timer_call_cyle = 0
|
||||
end
|
||||
end
|
||||
return crd.State:is_active(nvm)
|
||||
end
|
||||
|
||||
local function node_timer_act(pos, elapsed)
|
||||
local crd = CRD(pos)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
|
||||
-- handle power consumption
|
||||
if crd.power_netw and techage.needs_power(nvm) then
|
||||
local consumed = power.consume_power(pos, crd.power_netw, nil, crd.power_consumption)
|
||||
if consumed < crd.power_consumption then
|
||||
crd.State:nopower(pos, nvm)
|
||||
end
|
||||
end
|
||||
|
||||
-- call the node timer routine
|
||||
if techage.is_operational(nvm) then
|
||||
nvm.node_timer_call_cyle = (nvm.node_timer_call_cyle or 0) + 1
|
||||
if nvm.node_timer_call_cyle >= crd.call_cycle then
|
||||
crd.node_timer(pos, crd.cycle_time)
|
||||
nvm.node_timer_call_cyle = 0
|
||||
end
|
||||
end
|
||||
return crd.State:is_active(nvm)
|
||||
end
|
||||
|
||||
local function prepare_tiles(tiles, stage, power_png)
|
||||
local tbl = {}
|
||||
for _,item in ipairs(tiles) do
|
||||
if type(item) == "string" then
|
||||
tbl[#tbl+1] = item:gsub("#", stage):gsub("{power}", power_png):gsub("@@", '#')
|
||||
else
|
||||
local temp = table.copy(item)
|
||||
temp.name = temp.name:gsub("#", stage):gsub("{power}", power_png):gsub("@@", '#')
|
||||
tbl[#tbl+1] = temp
|
||||
end
|
||||
end
|
||||
return tbl
|
||||
end
|
||||
|
||||
-- 'validStates' is optional and can be used to e.g. enable
|
||||
-- only one TA2 node {false, true, false, false}
|
||||
function techage.register_consumer(base_name, inv_name, tiles, tNode, validStates, node_name_prefix, inv_name_prefix)
|
||||
local names = {}
|
||||
validStates = validStates or {true, true, true, true}
|
||||
|
||||
node_name_prefix = node_name_prefix or "techage:ta"
|
||||
if inv_name_prefix then
|
||||
inv_name_prefix = inv_name_prefix.." "
|
||||
else
|
||||
inv_name_prefix = ""
|
||||
end
|
||||
|
||||
for stage = 2,5 do
|
||||
local name_pas = node_name_prefix..stage.."_"..base_name.."_pas"
|
||||
local name_act = node_name_prefix..stage.."_"..base_name.."_act"
|
||||
local name_inv = inv_name_prefix.."TA"..stage.." "..inv_name
|
||||
names[#names+1] = name_pas
|
||||
|
||||
if validStates[stage] then
|
||||
local power_network
|
||||
local power_png = 'techage_axle_clutch.png'
|
||||
local power_used = tNode.power_consumption ~= nil
|
||||
local sides
|
||||
-- power needed?
|
||||
if power_used then
|
||||
if stage > 2 then
|
||||
power_network = techage.ElectricCable
|
||||
power_png = 'techage_appl_hole_electric.png'
|
||||
sides = get_keys(tNode.power_sides or {F=1, B=1, U=1, D=1})
|
||||
else
|
||||
power_network = techage.Axle
|
||||
power_png = 'techage_axle_clutch.png'
|
||||
sides = get_keys(tNode.power_sides or {F=1, B=1, U=1, D=1})
|
||||
end
|
||||
end
|
||||
|
||||
local tState = techage.NodeStates:new({
|
||||
node_name_passive = name_pas,
|
||||
node_name_active = name_act,
|
||||
infotext_name = name_inv,
|
||||
cycle_time = CYCLE_TIME,
|
||||
standby_ticks = tNode.standby_ticks,
|
||||
formspec_func = tNode.formspec,
|
||||
on_state_change = tNode.on_state_change,
|
||||
can_start = tNode.can_start,
|
||||
quick_start = tNode.quick_start,
|
||||
has_power = tNode.has_power or power_used and has_power or nil,
|
||||
start_node = power_used and start_node or nil,
|
||||
stop_node = power_used and stop_node or nil,
|
||||
})
|
||||
|
||||
local tConsumer = {
|
||||
stage = stage,
|
||||
State = tState,
|
||||
-- number of items to be processed per cycle
|
||||
num_items = tNode.num_items and tNode.num_items[stage],
|
||||
power_consumption = power_used and
|
||||
tNode.power_consumption[stage] or 0,
|
||||
node_timer = tNode.node_timer,
|
||||
cycle_time = tNode.cycle_time,
|
||||
call_cycle = tNode.cycle_time / 2,
|
||||
power_netw = power_network,
|
||||
}
|
||||
|
||||
local after_place_node = function(pos, placer, itemstack, pointed_thing)
|
||||
local crd = CRD(pos)
|
||||
local meta = M(pos)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
local node = minetest.get_node(pos)
|
||||
meta:set_int("push_dir", techage.side_to_indir("L", node.param2))
|
||||
meta:set_int("pull_dir", techage.side_to_indir("R", node.param2))
|
||||
meta:set_string("owner", placer:get_player_name())
|
||||
-- Delete existing node number. Needed for Digtron compatibility.
|
||||
if (meta:contains("node_number")) then
|
||||
meta:set_string("node_number", "")
|
||||
end
|
||||
local number = techage.add_node(pos, name_pas, stage == 2)
|
||||
if crd.power_netw then
|
||||
crd.power_netw:after_place_node(pos)
|
||||
end
|
||||
if tNode.after_place_node then
|
||||
tNode.after_place_node(pos, placer, itemstack, pointed_thing)
|
||||
end
|
||||
crd.State:node_init(pos, nvm, number)
|
||||
end
|
||||
|
||||
local after_dig_node = function(pos, oldnode, oldmetadata, digger)
|
||||
if tNode.after_dig_node then
|
||||
tNode.after_dig_node(pos, oldnode, oldmetadata, digger)
|
||||
end
|
||||
local crd = CRDN(oldnode)
|
||||
if crd.power_netw then
|
||||
crd.power_netw:after_dig_node(pos)
|
||||
end
|
||||
techage.remove_node(pos, oldnode, oldmetadata)
|
||||
techage.del_mem(pos)
|
||||
end
|
||||
|
||||
tNode.groups.not_in_creative_inventory = 0
|
||||
|
||||
local def_pas = {
|
||||
description = name_inv,
|
||||
tiles = prepare_tiles(tiles.pas, stage, power_png),
|
||||
consumer = tConsumer,
|
||||
drawtype = tNode.drawtype,
|
||||
node_box = tNode.node_box,
|
||||
selection_box = tNode.selection_box,
|
||||
|
||||
can_dig = tNode.can_dig,
|
||||
on_rotate = tNode.on_rotate or screwdriver.disallow,
|
||||
on_timer = node_timer_pas,
|
||||
on_receive_fields = tNode.on_receive_fields,
|
||||
on_rightclick = tNode.on_rightclick,
|
||||
after_place_node = after_place_node,
|
||||
after_dig_node = after_dig_node,
|
||||
preserve_metadata = tNode.preserve_metadata,
|
||||
allow_metadata_inventory_put = tNode.allow_metadata_inventory_put,
|
||||
allow_metadata_inventory_move = tNode.allow_metadata_inventory_move,
|
||||
allow_metadata_inventory_take = tNode.allow_metadata_inventory_take,
|
||||
on_metadata_inventory_move = tNode.on_metadata_inventory_move,
|
||||
on_metadata_inventory_put = tNode.on_metadata_inventory_put,
|
||||
on_metadata_inventory_take = tNode.on_metadata_inventory_take,
|
||||
ta_rotate_node = tNode.ta_rotate_node,
|
||||
ta3_formspec = stage == 3 and tNode.ta3_formspec,
|
||||
ta4_formspec = stage == 4 and tNode.ta4_formspec,
|
||||
|
||||
paramtype = tNode.paramtype,
|
||||
paramtype2 = "facedir",
|
||||
drop = tNode.drop,
|
||||
groups = table.copy(tNode.groups),
|
||||
is_ground_content = false,
|
||||
sounds = tNode.sounds,
|
||||
}
|
||||
|
||||
-- Copy custom properties (starting with an underscore)
|
||||
for k,v in pairs(tNode) do
|
||||
if string.sub(k, 1, 1) == "_" then
|
||||
def_pas[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
minetest.register_node(name_pas, def_pas)
|
||||
|
||||
tNode.groups.not_in_creative_inventory = 1
|
||||
|
||||
local def_act = {
|
||||
description = name_inv,
|
||||
tiles = prepare_tiles(tiles.act, stage, power_png),
|
||||
consumer = tConsumer,
|
||||
drawtype = tNode.drawtype,
|
||||
node_box = tNode.node_box,
|
||||
selection_box = tNode.selection_box,
|
||||
|
||||
on_rotate = tNode.on_rotate or screwdriver.disallow,
|
||||
on_timer = node_timer_act,
|
||||
on_receive_fields = tNode.on_receive_fields,
|
||||
on_rightclick = tNode.on_rightclick,
|
||||
after_place_node = after_place_node,
|
||||
after_dig_node = after_dig_node,
|
||||
allow_metadata_inventory_put = tNode.allow_metadata_inventory_put,
|
||||
allow_metadata_inventory_move = tNode.allow_metadata_inventory_move,
|
||||
allow_metadata_inventory_take = tNode.allow_metadata_inventory_take,
|
||||
on_metadata_inventory_move = tNode.on_metadata_inventory_move,
|
||||
on_metadata_inventory_put = tNode.on_metadata_inventory_put,
|
||||
on_metadata_inventory_take = tNode.on_metadata_inventory_take,
|
||||
ta_rotate_node = tNode.ta_rotate_node,
|
||||
ta3_formspec = stage == 3 and tNode.ta3_formspec,
|
||||
ta4_formspec = stage == 4 and tNode.ta4_formspec,
|
||||
|
||||
paramtype = tNode.paramtype,
|
||||
paramtype2 = "facedir",
|
||||
drop = "",
|
||||
diggable = false,
|
||||
groups = table.copy(tNode.groups),
|
||||
is_ground_content = false,
|
||||
sounds = tNode.sounds,
|
||||
}
|
||||
|
||||
-- Copy custom properties (starting with an underscore)
|
||||
for k,v in pairs(tNode) do
|
||||
if string.sub(k, 1, 1) == "_" then
|
||||
def_act[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
minetest.register_node(name_act, def_act)
|
||||
|
||||
if power_used then
|
||||
power.register_nodes({name_pas, name_act}, power_network, "con", sides)
|
||||
end
|
||||
techage.register_node({name_pas, name_act}, tNode.tubing)
|
||||
|
||||
if tNode.tube_sides then
|
||||
Tube:set_valid_sides(name_pas, get_keys(tNode.tube_sides))
|
||||
Tube:set_valid_sides(name_act, get_keys(tNode.tube_sides))
|
||||
end
|
||||
end
|
||||
end
|
||||
return names[1], names[2], names[3], names[4]
|
||||
end
|
683
techage/basic_machines/distributor.lua
Normal file
683
techage/basic_machines/distributor.lua
Normal file
@ -0,0 +1,683 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019-2022 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
TA2/TA3/TA4 Distributor
|
||||
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local M = minetest.get_meta
|
||||
local N = minetest.get_node
|
||||
-- Consumer Related Data
|
||||
local CRD = function(pos) return (minetest.registered_nodes[techage.get_node_lvm(pos).name] or {}).consumer end
|
||||
local Tube = techage.Tube
|
||||
|
||||
local S = techage.S
|
||||
|
||||
local SRC_INV_SIZE = 8
|
||||
|
||||
local STANDBY_TICKS = 3
|
||||
local COUNTDOWN_TICKS = 4
|
||||
local CYCLE_TIME = 4
|
||||
local FILTER_ITEM_LIMIT_PER_STACK = 12
|
||||
local FILTER_ITEM_LIMIT = 36
|
||||
|
||||
local INFO = [[Turn port on/off or read its state: command = 'port', payload = red/green/blue/yellow{=on/off}]]
|
||||
|
||||
|
||||
--local Side2Color = {B="red", L="green", F="blue", R="yellow"}
|
||||
local SlotColors = {"red", "green", "blue", "yellow"}
|
||||
local SlotNumbers = {red = 1, green = 2, blue = 3, yellow = 4}
|
||||
local Num2Ascii = {"B", "L", "F", "R"}
|
||||
local FilterCache = {} -- local cache for filter settings
|
||||
|
||||
local function filter_settings(pos)
|
||||
local meta = M(pos)
|
||||
local param2 = techage.get_node_lvm(pos).param2
|
||||
local inv = meta:get_inventory()
|
||||
local filter = minetest.deserialize(meta:get_string("filter")) or {false,false,false,false}
|
||||
local ItemFilter = {} -- {<item:name> = {dir,...}]
|
||||
local OpenPorts = {} -- {dir, ...}
|
||||
local counter = 0 -- counts total item number in filter configuration
|
||||
|
||||
-- collect all filter settings
|
||||
for idx,slot in ipairs(SlotColors) do
|
||||
if filter[idx] == true then
|
||||
local side = Num2Ascii[idx]
|
||||
local out_dir = techage.side_to_outdir(side, param2)
|
||||
if inv:is_empty(slot) then
|
||||
table.insert(OpenPorts, out_dir)
|
||||
else
|
||||
for idx2,stack in ipairs(inv:get_list(slot)) do
|
||||
local name = stack:get_name()
|
||||
if name ~= "" then
|
||||
if not ItemFilter[name] then
|
||||
ItemFilter[name] = {}
|
||||
end
|
||||
for _ = 1, math.min(FILTER_ITEM_LIMIT_PER_STACK, stack:get_count()) do
|
||||
if counter > FILTER_ITEM_LIMIT then
|
||||
break
|
||||
end
|
||||
table.insert(ItemFilter[name], out_dir)
|
||||
counter = counter + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
FilterCache[minetest.hash_node_position(pos)] = {
|
||||
ItemFilter = ItemFilter,
|
||||
OpenPorts = OpenPorts,
|
||||
}
|
||||
end
|
||||
|
||||
-- Return filter table and list of open ports.
|
||||
-- (see test data)
|
||||
local function get_filter_settings(pos)
|
||||
-- local ItemFilter = {
|
||||
-- ["default:dirt"] = {1,2},
|
||||
-- ["default:cobble"] = {4},
|
||||
-- }
|
||||
-- local OpenPorts = {3}
|
||||
-- return ItemFilter, OpenPorts
|
||||
|
||||
local hash = minetest.hash_node_position(pos)
|
||||
if FilterCache[hash] == nil then
|
||||
filter_settings(pos)
|
||||
end
|
||||
return FilterCache[hash].ItemFilter, FilterCache[hash].OpenPorts
|
||||
end
|
||||
|
||||
local function blocking_checkbox(pos, filter, is_hp)
|
||||
local cnt = 0
|
||||
local _, open_ports = get_filter_settings(pos)
|
||||
local fs_pos = is_hp and "0.25,5" or "3,3.9"
|
||||
for _,val in ipairs(filter) do
|
||||
if val then cnt = cnt + 1 end
|
||||
end
|
||||
if cnt > 1 and #open_ports > 0 then
|
||||
local blocking = M(pos):get_int("blocking") == 1 and "true" or "false"
|
||||
return "checkbox["..fs_pos..";blocking;"..S("blocking mode")..";"..blocking.."]"..
|
||||
"tooltip["..fs_pos..";1,1;"..S("Block configured items for open ports")..";#0C3D32;#FFFFFF]"
|
||||
else
|
||||
M(pos):set_int("blocking", 0) -- disable blocking
|
||||
end
|
||||
return ""
|
||||
end
|
||||
|
||||
local function formspec(self, pos, nvm)
|
||||
local filter = minetest.deserialize(M(pos):get_string("filter")) or {false,false,false,false}
|
||||
local is_hp = nvm.high_performance == true
|
||||
local blocking = blocking_checkbox(pos, filter, is_hp)
|
||||
|
||||
if is_hp then
|
||||
return "size[10.5,9.5]"..
|
||||
"box[0.25,-0.1;9.6,1.1;#005500]"..
|
||||
"label[0.6,0.2;"..S("Input").."]"..
|
||||
"list[context;src;1.75,0;8,1;]"..
|
||||
blocking..
|
||||
"image_button[0.25,5.8;1,1;"..self:get_state_button_image(nvm)..";state_button;]"..
|
||||
"tooltip[0.25,5.8;1,1;"..self:get_state_tooltip(nvm).."]"..
|
||||
"checkbox[0.25,1.2;filter1;On;"..dump(filter[1]).."]"..
|
||||
"checkbox[0.25,2.2;filter2;On;"..dump(filter[2]).."]"..
|
||||
"checkbox[0.25,3.2;filter3;On;"..dump(filter[3]).."]"..
|
||||
"checkbox[0.25,4.2;filter4;On;"..dump(filter[4]).."]"..
|
||||
"image[1.25,1.2;0.3,1;techage_inv_red.png]"..
|
||||
"image[1.25,2.2;0.3,1;techage_inv_green.png]"..
|
||||
"image[1.25,3.2;0.3,1;techage_inv_blue.png]"..
|
||||
"image[1.25,4.2;0.3,1;techage_inv_yellow.png]"..
|
||||
"list[context;red;1.75,1.2;8,1;]"..
|
||||
"list[context;green;1.75,2.2;8,1;]"..
|
||||
"list[context;blue;1.75,3.2;8,1;]"..
|
||||
"list[context;yellow;1.75,4.2;8,1;]"..
|
||||
"list[current_player;main;1.75,5.8;8,4;]"..
|
||||
"listring[context;src]"..
|
||||
"listring[current_player;main]"..
|
||||
default.get_hotbar_bg(1.75,5.8)
|
||||
else
|
||||
return "size[10.5,8.5]"..
|
||||
"list[context;src;0,0;2,4;]"..
|
||||
blocking..
|
||||
"image[2,1.5;1,1;techage_form_arrow.png]"..
|
||||
"image_button[0,4.8;1,1;"..self:get_state_button_image(nvm)..";state_button;]"..
|
||||
"tooltip[0,4.8;1,1;"..self:get_state_tooltip(nvm).."]"..
|
||||
"checkbox[3,0;filter1;On;"..dump(filter[1]).."]"..
|
||||
"checkbox[3,1;filter2;On;"..dump(filter[2]).."]"..
|
||||
"checkbox[3,2;filter3;On;"..dump(filter[3]).."]"..
|
||||
"checkbox[3,3;filter4;On;"..dump(filter[4]).."]"..
|
||||
"image[4,0;0.3,1;techage_inv_red.png]"..
|
||||
"image[4,1;0.3,1;techage_inv_green.png]"..
|
||||
"image[4,2;0.3,1;techage_inv_blue.png]"..
|
||||
"image[4,3;0.3,1;techage_inv_yellow.png]"..
|
||||
"list[context;red;4.5,0;6,1;]"..
|
||||
"list[context;green;4.5,1;6,1;]"..
|
||||
"list[context;blue;4.5,2;6,1;]"..
|
||||
"list[context;yellow;4.5,3;6,1;]"..
|
||||
"list[current_player;main;1.25,4.8;8,4;]"..
|
||||
"listring[context;src]"..
|
||||
"listring[current_player;main]"..
|
||||
default.get_hotbar_bg(1.25,4.8)
|
||||
end
|
||||
end
|
||||
|
||||
local function get_total_filter_items_number(pos, except_listname, except_index)
|
||||
local inv = M(pos):get_inventory()
|
||||
local total = 0
|
||||
for _, listname in ipairs(SlotColors) do
|
||||
local list = inv:get_list(listname)
|
||||
for idx, stack in ipairs(list) do
|
||||
if not (listname == except_listname and idx == except_index) then
|
||||
total = total + stack:get_count()
|
||||
end
|
||||
end
|
||||
end
|
||||
return total
|
||||
end
|
||||
|
||||
local function allow_metadata_inventory_put(pos, listname, index, stack, player)
|
||||
local inv = M(pos):get_inventory()
|
||||
local list = inv:get_list(listname)
|
||||
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return 0
|
||||
end
|
||||
if listname == "src" then
|
||||
CRD(pos).State:start_if_standby(pos)
|
||||
return stack:get_count()
|
||||
else
|
||||
stack:add_item(list[index])
|
||||
local max_items_to_limit = FILTER_ITEM_LIMIT - get_total_filter_items_number(pos, listname, index)
|
||||
stack:set_count(math.min(FILTER_ITEM_LIMIT_PER_STACK, stack:get_count(), max_items_to_limit))
|
||||
inv:set_stack(listname, index, stack)
|
||||
return 0
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
local function allow_metadata_inventory_take(pos, listname, index, stack, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return 0
|
||||
end
|
||||
if listname == "src" then
|
||||
return stack:get_count()
|
||||
else
|
||||
local inv = M(pos):get_inventory()
|
||||
local list = inv:get_list(listname)
|
||||
list[index]:take_item(stack:get_count())
|
||||
inv:set_stack(listname, index, list[index])
|
||||
return 0
|
||||
end
|
||||
end
|
||||
|
||||
local function allow_metadata_inventory_move(pos, from_list, from_index, to_list, to_index, count, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return 0
|
||||
end
|
||||
local inv = minetest.get_meta(pos):get_inventory()
|
||||
local stack = inv:get_stack(from_list, from_index)
|
||||
|
||||
if from_list == "src" and to_list ~= "src" then
|
||||
stack:add_item(inv:get_stack(to_list, to_index))
|
||||
local max_items_to_limit = FILTER_ITEM_LIMIT - get_total_filter_items_number(pos, to_list, to_index)
|
||||
stack:set_count(math.min(FILTER_ITEM_LIMIT_PER_STACK, stack:get_count(), max_items_to_limit))
|
||||
inv:set_stack(to_list, to_index, stack)
|
||||
return 0
|
||||
elseif from_list ~= "src" and to_list == "src" then
|
||||
inv:set_stack(from_list, from_index, nil)
|
||||
return 0
|
||||
elseif from_list ~= "src" and to_list ~= "src" then
|
||||
return math.min(stack:get_count(), FILTER_ITEM_LIMIT_PER_STACK - inv:get_stack(to_list, to_index):get_count())
|
||||
else
|
||||
return stack:get_count()
|
||||
end
|
||||
end
|
||||
|
||||
local function tubelib2_on_update2(pos, outdir, tlib2, node)
|
||||
local is_ta4_tube = true
|
||||
for dir = 1,4 do
|
||||
for i, pos, node in Tube:get_tube_line(pos, dir) do
|
||||
is_ta4_tube = is_ta4_tube and techage.TA4tubes[node.name]
|
||||
end
|
||||
end
|
||||
|
||||
local nvm = techage.get_nvm(pos)
|
||||
local crd = CRD(pos)
|
||||
if CRD(pos).stage == 4 and not is_ta4_tube then
|
||||
nvm.num_items = crd.num_items / 2
|
||||
else
|
||||
nvm.num_items = crd.num_items
|
||||
end
|
||||
end
|
||||
|
||||
local function shuffle(list)
|
||||
for i = #list, 2, -1 do
|
||||
local j = math.random(i)
|
||||
list[i], list[j] = list[j], list[i]
|
||||
end
|
||||
return list
|
||||
end
|
||||
|
||||
local function push_item(pos, base_filter, itemstack, num_items, nvm)
|
||||
local filter = shuffle(table.copy(base_filter))
|
||||
local idx = 1
|
||||
local num_pushed = 0
|
||||
local num_ports = #filter
|
||||
local amount = math.floor(math.max((num_items + 1) / num_ports, 1))
|
||||
local num_of_trials = 0
|
||||
while num_pushed < num_items and num_of_trials <= 8 do
|
||||
num_of_trials = num_of_trials + 1
|
||||
local push_dir = filter[idx]
|
||||
local num_to_push = math.min(amount, num_items - num_pushed)
|
||||
local leftover = techage.push_items(pos, push_dir, itemstack:peek_item(num_to_push))
|
||||
local pushed
|
||||
if not leftover then
|
||||
pushed = 0
|
||||
elseif leftover ~= true then
|
||||
pushed = num_to_push - leftover:get_count()
|
||||
else -- leftover == true
|
||||
pushed = num_to_push
|
||||
end
|
||||
num_pushed = num_pushed + pushed
|
||||
nvm.port_counter[push_dir] = (nvm.port_counter[push_dir] or 0) + pushed
|
||||
-- filter start offset
|
||||
idx = idx + 1
|
||||
if idx > num_ports then
|
||||
idx = 1
|
||||
end
|
||||
end
|
||||
return num_pushed
|
||||
end
|
||||
|
||||
-- move items to output slots
|
||||
local function distributing(pos, inv, crd, nvm)
|
||||
local item_filter, open_ports = get_filter_settings(pos)
|
||||
local sum_num_pushed = 0
|
||||
local num_pushed = 0
|
||||
local blocking_mode = M(pos):get_int("blocking") == 1
|
||||
|
||||
-- start searching after last position
|
||||
local offs = nvm.last_index or 1
|
||||
|
||||
for i = 1, SRC_INV_SIZE do
|
||||
local idx = ((i + offs - 1) % 8) + 1
|
||||
local stack = inv:get_stack("src", idx)
|
||||
local item_name = stack:get_name()
|
||||
local num_items = stack:get_count()
|
||||
local num_to_push = math.min((nvm.num_items or crd.num_items) - sum_num_pushed, num_items)
|
||||
local stack_to_push = stack:peek_item(num_to_push)
|
||||
local filter = item_filter[item_name]
|
||||
num_pushed = 0
|
||||
|
||||
if filter and #filter > 0 then
|
||||
-- Push items based on filter
|
||||
num_pushed = push_item(pos, filter, stack_to_push, num_to_push, nvm)
|
||||
elseif blocking_mode and #open_ports > 0 then
|
||||
-- Push items based on open ports
|
||||
num_pushed = push_item(pos, open_ports, stack_to_push, num_to_push, nvm)
|
||||
end
|
||||
if not blocking_mode and num_pushed == 0 and #open_ports > 0 then
|
||||
-- Push items based on open ports
|
||||
num_pushed = push_item(pos, open_ports, stack_to_push, num_to_push, nvm)
|
||||
end
|
||||
|
||||
sum_num_pushed = sum_num_pushed + num_pushed
|
||||
stack:take_item(num_pushed)
|
||||
inv:set_stack("src", idx, stack)
|
||||
if sum_num_pushed >= (nvm.num_items or crd.num_items) then
|
||||
nvm.last_index = idx
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if sum_num_pushed == 0 then
|
||||
crd.State:blocked(pos, nvm)
|
||||
else
|
||||
crd.State:keep_running(pos, nvm, COUNTDOWN_TICKS)
|
||||
end
|
||||
end
|
||||
|
||||
-- move items to the output slots
|
||||
local function keep_running(pos, elapsed)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
nvm.port_counter = nvm.port_counter or {}
|
||||
local crd = CRD(pos)
|
||||
local inv = M(pos):get_inventory()
|
||||
if not inv:is_empty("src") then
|
||||
distributing(pos, inv, crd, nvm)
|
||||
else
|
||||
crd.State:idle(pos, nvm)
|
||||
end
|
||||
return crd.State:is_active(nvm)
|
||||
end
|
||||
|
||||
local function on_receive_fields(pos, formname, fields, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return
|
||||
end
|
||||
local meta = M(pos)
|
||||
local crd = CRD(pos)
|
||||
local filter = minetest.deserialize(meta:get_string("filter")) or {false,false,false,false}
|
||||
if fields.filter1 ~= nil then
|
||||
filter[1] = fields.filter1 == "true"
|
||||
elseif fields.filter2 ~= nil then
|
||||
filter[2] = fields.filter2 == "true"
|
||||
elseif fields.filter3 ~= nil then
|
||||
filter[3] = fields.filter3 == "true"
|
||||
elseif fields.filter4 ~= nil then
|
||||
filter[4] = fields.filter4 == "true"
|
||||
elseif fields.blocking ~= nil then
|
||||
meta:set_int("blocking", fields.blocking == "true" and 1 or 0)
|
||||
end
|
||||
meta:set_string("filter", minetest.serialize(filter))
|
||||
|
||||
filter_settings(pos)
|
||||
|
||||
local nvm = techage.get_nvm(pos)
|
||||
if fields.state_button ~= nil then
|
||||
crd.State:state_button_event(pos, nvm, fields)
|
||||
else
|
||||
meta:set_string("formspec", formspec(crd.State, pos, nvm))
|
||||
end
|
||||
end
|
||||
|
||||
-- techage command to turn on/off filter channels
|
||||
local function change_filter_settings(pos, slot, val)
|
||||
local meta = M(pos)
|
||||
local filter = minetest.deserialize(meta:get_string("filter")) or {false,false,false,false}
|
||||
local num = SlotNumbers[slot] or 1
|
||||
if num >= 1 and num <= 4 then
|
||||
filter[num] = val == "on"
|
||||
end
|
||||
meta:set_string("filter", minetest.serialize(filter))
|
||||
|
||||
local hash = minetest.hash_node_position(pos)
|
||||
FilterCache[hash] = nil
|
||||
|
||||
local nvm = techage.get_nvm(pos)
|
||||
meta:set_string("formspec", formspec(CRD(pos).State, pos, nvm))
|
||||
return true
|
||||
end
|
||||
|
||||
-- techage command to read filter channel status (on/off)
|
||||
local function read_filter_settings(pos, slot)
|
||||
local filter = minetest.deserialize(M(pos):get_string("filter")) or {false,false,false,false}
|
||||
return filter[SlotNumbers[slot]] and "on" or "off"
|
||||
end
|
||||
|
||||
local function get_payload_values(payload)
|
||||
local color
|
||||
local idx = 0
|
||||
local items = {ItemStack(""), ItemStack(""), ItemStack(""), ItemStack(""), ItemStack(""), ItemStack("")}
|
||||
for s in payload:gmatch("[^%s]+") do --- white spaces
|
||||
if not color then
|
||||
if SlotNumbers[s] then
|
||||
color = s
|
||||
else
|
||||
return "red", {}
|
||||
end
|
||||
else
|
||||
idx = idx + 1
|
||||
if idx <= 6 then
|
||||
items[idx] = ItemStack(s)
|
||||
end
|
||||
end
|
||||
end
|
||||
return color, items
|
||||
end
|
||||
|
||||
local function str_of_inv_items(pos, color)
|
||||
color = SlotColors[color] or color
|
||||
if SlotNumbers[color] then
|
||||
local inv = M(pos):get_inventory()
|
||||
local t = {}
|
||||
for idx = 1, 6 do
|
||||
local item = inv:get_stack(color, idx)
|
||||
if item:get_count() > 0 then
|
||||
t[#t + 1] = item:get_name()
|
||||
end
|
||||
end
|
||||
return table.concat(t, " ")
|
||||
end
|
||||
return ""
|
||||
end
|
||||
|
||||
local function can_dig(pos, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return false
|
||||
end
|
||||
local inv = M(pos):get_inventory()
|
||||
return inv:is_empty("src")
|
||||
end
|
||||
|
||||
local get_tiles = function(is_hp)
|
||||
local variant = is_hp and "_hp" or ""
|
||||
local tiles = {}
|
||||
-- '#' will be replaced by the stage number
|
||||
-- '{power}' will be replaced by the power PNG
|
||||
tiles.pas = {
|
||||
-- up, down, right, left, back, front
|
||||
"techage_filling_ta#.png^techage_appl_distri.png^techage_frame_ta#_top"..variant..".png^techage_appl_color_top.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#_top"..variant..".png^(techage_appl_color_top.png^[transformFY)",
|
||||
"techage_filling_ta#.png^techage_frame_ta#"..variant..".png^techage_appl_distri_yellow.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#"..variant..".png^techage_appl_distri_green.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#"..variant..".png^techage_appl_distri_red.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#"..variant..".png^techage_appl_distri_blue.png",
|
||||
}
|
||||
tiles.act = {
|
||||
-- up, down, right, left, back, front
|
||||
{
|
||||
name = "techage_filling4_ta#.png^techage_appl_distri4.png^techage_frame4_ta#_top"..variant..".png^techage_appl_color_top4.png",
|
||||
backface_culling = false,
|
||||
animation = {
|
||||
type = "vertical_frames",
|
||||
aspect_w = 32,
|
||||
aspect_h = 32,
|
||||
length = 1.0,
|
||||
},
|
||||
},
|
||||
"techage_filling_ta#.png^techage_frame_ta#_top"..variant..".png^(techage_appl_color_top.png^[transformFY)",
|
||||
"techage_filling_ta#.png^techage_frame_ta#"..variant..".png^techage_appl_distri_yellow.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#"..variant..".png^techage_appl_distri_green.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#"..variant..".png^techage_appl_distri_red.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#"..variant..".png^techage_appl_distri_blue.png",
|
||||
}
|
||||
return tiles
|
||||
end
|
||||
|
||||
local tubing = {
|
||||
on_pull_item = function(pos, in_dir, num)
|
||||
local inv = M(pos):get_inventory()
|
||||
return techage.get_items(pos, inv, "src", num)
|
||||
end,
|
||||
on_push_item = function(pos, in_dir, stack)
|
||||
CRD(pos).State:start_if_standby(pos)
|
||||
local inv = M(pos):get_inventory()
|
||||
return techage.put_items(inv, "src", stack)
|
||||
end,
|
||||
on_unpull_item = function(pos, in_dir, stack)
|
||||
local inv = M(pos):get_inventory()
|
||||
return techage.put_items(inv, "src", stack)
|
||||
end,
|
||||
on_recv_message = function(pos, src, topic, payload)
|
||||
if topic == "info" then
|
||||
return INFO
|
||||
elseif topic == "port" then
|
||||
-- "red"/"green"/"blue"/"yellow" = "on"/"off"
|
||||
local slot, val = techage.ident_value(payload)
|
||||
if val == "" then
|
||||
return read_filter_settings(pos, slot)
|
||||
else
|
||||
return change_filter_settings(pos, slot, val)
|
||||
end
|
||||
elseif topic == "config" then
|
||||
local color, items = get_payload_values(payload)
|
||||
local inv = M(pos):get_inventory()
|
||||
for idx,item in ipairs(items) do
|
||||
inv:set_stack(color, idx, item)
|
||||
end
|
||||
local hash = minetest.hash_node_position(pos)
|
||||
FilterCache[hash] = nil
|
||||
return true
|
||||
elseif topic == "get" then
|
||||
return str_of_inv_items(pos, payload)
|
||||
else
|
||||
return CRD(pos).State:on_receive_message(pos, topic, payload)
|
||||
end
|
||||
end,
|
||||
on_beduino_receive_cmnd = function(pos, src, topic, payload)
|
||||
if topic == 4 then
|
||||
local slot = SlotColors[payload[1]]
|
||||
local state = payload[2] == 1 and "on" or "off"
|
||||
change_filter_settings(pos, slot, state)
|
||||
return 0
|
||||
elseif topic == 67 then
|
||||
local color, items = get_payload_values(payload)
|
||||
local inv = M(pos):get_inventory()
|
||||
for idx,item in ipairs(items) do
|
||||
inv:set_stack(color, idx, item)
|
||||
end
|
||||
local hash = minetest.hash_node_position(pos)
|
||||
FilterCache[hash] = nil
|
||||
return 0
|
||||
else
|
||||
return CRD(pos).State:on_beduino_receive_cmnd(pos, topic, payload)
|
||||
end
|
||||
end,
|
||||
on_beduino_request_data = function(pos, src, topic, payload)
|
||||
if topic == 148 then
|
||||
return 0, str_of_inv_items(pos, payload[1])
|
||||
else
|
||||
return CRD(pos).State:on_beduino_request_data(pos, topic, payload)
|
||||
end
|
||||
end,
|
||||
on_node_load = function(pos)
|
||||
CRD(pos).State:on_node_load(pos)
|
||||
end,
|
||||
}
|
||||
|
||||
local def = {
|
||||
cycle_time = CYCLE_TIME,
|
||||
standby_ticks = STANDBY_TICKS,
|
||||
formspec = formspec,
|
||||
tubing = tubing,
|
||||
after_place_node = function(pos, placer)
|
||||
local meta = M(pos)
|
||||
local filter = {false,false,false,false}
|
||||
meta:set_string("filter", minetest.serialize(filter))
|
||||
local inv = meta:get_inventory()
|
||||
inv:set_size('src', 8)
|
||||
inv:set_size('yellow', 6)
|
||||
inv:set_size('green', 6)
|
||||
inv:set_size('red', 6)
|
||||
inv:set_size('blue', 6)
|
||||
end,
|
||||
can_dig = can_dig,
|
||||
node_timer = keep_running,
|
||||
on_receive_fields = on_receive_fields,
|
||||
allow_metadata_inventory_put = allow_metadata_inventory_put,
|
||||
allow_metadata_inventory_move = allow_metadata_inventory_move,
|
||||
allow_metadata_inventory_take = allow_metadata_inventory_take,
|
||||
tubelib2_on_update2 = tubelib2_on_update2,
|
||||
|
||||
on_metadata_inventory_move = function(pos, from_list, from_index, to_list)
|
||||
if from_list ~= "src" or to_list ~= "src" then
|
||||
filter_settings(pos)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
M(pos):set_string("formspec", formspec(CRD(pos).State, pos, nvm))
|
||||
end
|
||||
end,
|
||||
on_metadata_inventory_put = function(pos, listname)
|
||||
if listname ~= "src" then
|
||||
filter_settings(pos)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
M(pos):set_string("formspec", formspec(CRD(pos).State, pos, nvm))
|
||||
end
|
||||
end,
|
||||
on_metadata_inventory_take = function(pos, listname)
|
||||
if listname ~= "src" then
|
||||
filter_settings(pos)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
M(pos):set_string("formspec", formspec(CRD(pos).State, pos, nvm))
|
||||
end
|
||||
end,
|
||||
|
||||
groups = {choppy=2, cracky=2, crumbly=2},
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
num_items = {0,4,12,24},
|
||||
}
|
||||
|
||||
local node_name_ta2, node_name_ta3, node_name_ta4 = techage.register_consumer(
|
||||
"distributor",
|
||||
S("Distributor"),
|
||||
get_tiles(false),
|
||||
def
|
||||
)
|
||||
|
||||
local hp_def = table.copy(def)
|
||||
|
||||
hp_def.after_place_node = function(pos, placer)
|
||||
local meta = M(pos)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
nvm.high_performance = true
|
||||
local filter = {false,false,false,false}
|
||||
meta:set_string("filter", minetest.serialize(filter))
|
||||
local inv = meta:get_inventory()
|
||||
inv:set_size('src', 8)
|
||||
inv:set_size('yellow', 8)
|
||||
inv:set_size('green', 8)
|
||||
inv:set_size('red', 8)
|
||||
inv:set_size('blue', 8)
|
||||
end
|
||||
hp_def.num_items = {0,0,0,36}
|
||||
|
||||
local _, _, node_name_ta4_hp = techage.register_consumer(
|
||||
"high_performance_distributor", S("High Performance Distributor"),
|
||||
get_tiles(true),
|
||||
hp_def,
|
||||
{false, false, false, true}
|
||||
)
|
||||
|
||||
minetest.register_craft({
|
||||
output = node_name_ta2.." 2",
|
||||
recipe = {
|
||||
{"group:wood", "techage:iron_ingot", "group:wood"},
|
||||
{"techage:tubeS", "default:mese_crystal", "techage:tubeS"},
|
||||
{"group:wood", "techage:iron_ingot", "group:wood"},
|
||||
},
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = node_name_ta3,
|
||||
recipe = {
|
||||
{"", "techage:iron_ingot", ""},
|
||||
{"", node_name_ta2, ""},
|
||||
{"", "techage:vacuum_tube", ""},
|
||||
},
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = node_name_ta4,
|
||||
recipe = {
|
||||
{"", "techage:iron_ingot", ""},
|
||||
{"", node_name_ta3, ""},
|
||||
{"", "techage:ta4_wlanchip", ""},
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
minetest.register_craft({
|
||||
output = node_name_ta4_hp,
|
||||
recipe = {
|
||||
{node_name_ta4, "default:copper_ingot"},
|
||||
{"default:mese_crystal_fragment", node_name_ta4},
|
||||
},
|
||||
})
|
290
techage/basic_machines/electronic_fab.lua
Normal file
290
techage/basic_machines/electronic_fab.lua
Normal file
@ -0,0 +1,290 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019-2022 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
TA2/TA3/TA4 Electronic Fab
|
||||
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local M = minetest.get_meta
|
||||
-- Consumer Related Data
|
||||
local CRD = function(pos) return (minetest.registered_nodes[techage.get_node_lvm(pos).name] or {}).consumer end
|
||||
|
||||
local S = techage.S
|
||||
local STANDBY_TICKS = 3
|
||||
local COUNTDOWN_TICKS = 4
|
||||
local CYCLE_TIME = 6
|
||||
|
||||
local recipes = techage.recipes
|
||||
|
||||
local RecipeType = {
|
||||
[2] = "ta2_electronic_fab",
|
||||
[3] = "ta3_electronic_fab",
|
||||
[4] = "ta4_electronic_fab",
|
||||
}
|
||||
|
||||
local function formspec(self, pos, nvm)
|
||||
local rtype = RecipeType[CRD(pos).stage]
|
||||
local owner = M(pos):get_string("owner")
|
||||
return "size[8.4,8.4]"..
|
||||
"list[context;src;0,0;2,4;]"..
|
||||
recipes.formspec(2.2, 0, rtype, nvm, owner)..
|
||||
"list[context;dst;6.4,0;2,4;]"..
|
||||
"image_button[3.7,3.3;1,1;".. self:get_state_button_image(nvm) ..";state_button;]"..
|
||||
"tooltip[3.7,3.3;1,1;"..self:get_state_tooltip(nvm).."]"..
|
||||
"list[current_player;main;0.2,4.5;8,4;]"..
|
||||
"listring[context;dst]"..
|
||||
"listring[current_player;main]"..
|
||||
"listring[context;src]"..
|
||||
"listring[current_player;main]"..
|
||||
default.get_hotbar_bg(0.2, 4.5)
|
||||
end
|
||||
|
||||
local function allow_metadata_inventory_put(pos, listname, index, stack, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return 0
|
||||
end
|
||||
local crd = CRD(pos)
|
||||
if listname == "src" then
|
||||
crd.State:start_if_standby(pos)
|
||||
return stack:get_count()
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
local function allow_metadata_inventory_move(pos, from_list, from_index, to_list, to_index, count, player)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
local stack = inv:get_stack(from_list, from_index)
|
||||
return allow_metadata_inventory_put(pos, to_list, to_index, stack, player)
|
||||
end
|
||||
|
||||
local function allow_metadata_inventory_take(pos, listname, index, stack, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return 0
|
||||
end
|
||||
return stack:get_count()
|
||||
end
|
||||
|
||||
local function making(pos, crd, nvm, inv)
|
||||
local owner = M(pos):get_string("owner")
|
||||
local rtype = RecipeType[crd.stage]
|
||||
local recipe = recipes.get(nvm, rtype, owner)
|
||||
local output = ItemStack(recipe.output.name .. " " .. recipe.output.num)
|
||||
local waste = recipe.waste and ItemStack(recipe.waste.name .. " " .. recipe.waste.num)
|
||||
if inv:room_for_item("dst", output) and (not waste or inv:room_for_item("dst", waste)) then
|
||||
for _,item in ipairs(recipe.input) do
|
||||
local input = ItemStack(item.name.." "..item.num)
|
||||
if not inv:contains_item("src", input) then
|
||||
crd.State:idle(pos, nvm)
|
||||
return
|
||||
end
|
||||
end
|
||||
for _,item in ipairs(recipe.input) do
|
||||
local input = ItemStack(item.name.." "..item.num)
|
||||
inv:remove_item("src", input)
|
||||
end
|
||||
inv:add_item("dst", output)
|
||||
if waste then
|
||||
inv:add_item("dst", waste)
|
||||
end
|
||||
crd.State:keep_running(pos, nvm, COUNTDOWN_TICKS)
|
||||
return
|
||||
end
|
||||
crd.State:idle(pos, nvm)
|
||||
end
|
||||
|
||||
local function keep_running(pos, elapsed)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
local crd = CRD(pos)
|
||||
local inv = M(pos):get_inventory()
|
||||
if inv then
|
||||
making(pos, crd, nvm, inv)
|
||||
end
|
||||
end
|
||||
|
||||
local function on_receive_fields(pos, formname, fields, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return
|
||||
end
|
||||
|
||||
local nvm = techage.get_nvm(pos)
|
||||
local crd = CRD(pos)
|
||||
|
||||
if not nvm.running then
|
||||
recipes.on_receive_fields(pos, formname, fields, player)
|
||||
end
|
||||
|
||||
crd.State:state_button_event(pos, nvm, fields)
|
||||
M(pos):set_string("formspec", formspec(crd.State, pos, nvm))
|
||||
end
|
||||
|
||||
local function can_dig(pos, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return false
|
||||
end
|
||||
local inv = M(pos):get_inventory()
|
||||
return inv:is_empty("dst") and inv:is_empty("src")
|
||||
end
|
||||
|
||||
local tiles = {}
|
||||
-- '#' will be replaced by the stage number
|
||||
tiles.pas = {
|
||||
-- up, down, right, left, back, front
|
||||
"techage_filling_ta#.png^techage_frame_ta#_top.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_outp.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_inp.png",
|
||||
"techage_filling_ta#.png^techage_appl_electronic_fab.png^techage_frame_ta#.png",
|
||||
"techage_filling_ta#.png^techage_appl_electronic_fab.png^techage_frame_ta#.png",
|
||||
}
|
||||
tiles.act = {
|
||||
-- up, down, right, left, back, front
|
||||
"techage_filling_ta#.png^techage_frame_ta#_top.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_outp.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_inp.png",
|
||||
{
|
||||
name = "techage_filling4_ta#.png^techage_appl_electronic_fab4.png^techage_frame4_ta#.png",
|
||||
backface_culling = false,
|
||||
animation = {
|
||||
type = "vertical_frames",
|
||||
aspect_w = 32,
|
||||
aspect_h = 32,
|
||||
length = 0.5,
|
||||
},
|
||||
},
|
||||
{
|
||||
name = "techage_filling4_ta#.png^techage_appl_electronic_fab4.png^techage_frame4_ta#.png",
|
||||
backface_culling = false,
|
||||
animation = {
|
||||
type = "vertical_frames",
|
||||
aspect_w = 32,
|
||||
aspect_h = 32,
|
||||
length = 0.5,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
local tubing = {
|
||||
on_inv_request = function(pos, in_dir, access_type)
|
||||
if access_type == "push" then
|
||||
local meta = minetest.get_meta(pos)
|
||||
if meta:get_int("push_dir") == in_dir or in_dir == 5 then
|
||||
return meta:get_inventory(), "src"
|
||||
end
|
||||
end
|
||||
end,
|
||||
on_pull_item = function(pos, in_dir, num)
|
||||
local meta = minetest.get_meta(pos)
|
||||
if meta:get_int("pull_dir") == in_dir then
|
||||
local inv = M(pos):get_inventory()
|
||||
return techage.get_items(pos, inv, "dst", num)
|
||||
end
|
||||
end,
|
||||
on_push_item = function(pos, in_dir, stack, idx)
|
||||
local meta = minetest.get_meta(pos)
|
||||
if meta:get_int("push_dir") == in_dir or in_dir == 5 then
|
||||
local inv = M(pos):get_inventory()
|
||||
--CRD(pos).State:start_if_standby(pos) -- would need power!
|
||||
return techage.put_items(inv, "src", stack, idx)
|
||||
end
|
||||
end,
|
||||
on_unpull_item = function(pos, in_dir, stack)
|
||||
local meta = minetest.get_meta(pos)
|
||||
if meta:get_int("pull_dir") == in_dir then
|
||||
local inv = M(pos):get_inventory()
|
||||
return techage.put_items(inv, "dst", stack)
|
||||
end
|
||||
end,
|
||||
on_recv_message = function(pos, src, topic, payload)
|
||||
return CRD(pos).State:on_receive_message(pos, topic, payload)
|
||||
end,
|
||||
on_beduino_receive_cmnd = function(pos, src, topic, payload)
|
||||
return CRD(pos).State:on_beduino_receive_cmnd(pos, topic, payload)
|
||||
end,
|
||||
on_beduino_request_data = function(pos, src, topic, payload)
|
||||
return CRD(pos).State:on_beduino_request_data(pos, topic, payload)
|
||||
end,
|
||||
on_node_load = function(pos)
|
||||
CRD(pos).State:on_node_load(pos)
|
||||
end,
|
||||
}
|
||||
|
||||
local node_name_ta2, node_name_ta3, node_name_ta4 =
|
||||
techage.register_consumer("electronic_fab", S("Electronic Fab"), tiles, {
|
||||
drawtype = "normal",
|
||||
cycle_time = CYCLE_TIME,
|
||||
standby_ticks = STANDBY_TICKS,
|
||||
formspec = formspec,
|
||||
tubing = tubing,
|
||||
after_place_node = function(pos, placer)
|
||||
local inv = M(pos):get_inventory()
|
||||
inv:set_size("src", 8)
|
||||
inv:set_size("dst", 8)
|
||||
end,
|
||||
can_dig = can_dig,
|
||||
node_timer = keep_running,
|
||||
on_receive_fields = on_receive_fields,
|
||||
allow_metadata_inventory_put = allow_metadata_inventory_put,
|
||||
allow_metadata_inventory_move = allow_metadata_inventory_move,
|
||||
allow_metadata_inventory_take = allow_metadata_inventory_take,
|
||||
groups = {choppy=2, cracky=2, crumbly=2},
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
num_items = {0,1,1,1},
|
||||
power_consumption = {0,8,12,12},
|
||||
},
|
||||
{false, true, true, true}) -- TA2/TA3/TA4
|
||||
|
||||
minetest.register_craft({
|
||||
output = node_name_ta2,
|
||||
recipe = {
|
||||
{"group:wood", "default:diamond", "group:wood"},
|
||||
{"techage:tubeS", "basic_materials:gear_steel", "techage:tubeS"},
|
||||
{"group:wood", "default:steel_ingot", "group:wood"},
|
||||
},
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = node_name_ta3,
|
||||
recipe = {
|
||||
{"", "default:diamond", ""},
|
||||
{"", node_name_ta2, ""},
|
||||
{"", "techage:vacuum_tube", ""},
|
||||
},
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = node_name_ta4,
|
||||
recipe = {
|
||||
{"", "default:diamond", ""},
|
||||
{"", node_name_ta3, ""},
|
||||
{"", "techage:ta4_wlanchip", ""},
|
||||
},
|
||||
})
|
||||
|
||||
techage.recipes.register_craft_type("ta2_electronic_fab", {
|
||||
description = S("TA2 Ele Fab"),
|
||||
icon = 'techage_filling_ta2.png^techage_appl_electronic_fab.png^techage_frame_ta2.png',
|
||||
width = 2,
|
||||
height = 2,
|
||||
})
|
||||
techage.recipes.register_craft_type("ta3_electronic_fab", {
|
||||
description = S("TA3 Ele Fab"),
|
||||
icon = 'techage_filling_ta3.png^techage_appl_electronic_fab.png^techage_frame_ta3.png',
|
||||
width = 2,
|
||||
height = 2,
|
||||
})
|
||||
techage.recipes.register_craft_type("ta4_electronic_fab", {
|
||||
description = S("TA4 Ele Fab"),
|
||||
icon = 'techage_filling_ta4.png^techage_appl_electronic_fab.png^techage_frame_ta4.png',
|
||||
width = 2,
|
||||
height = 2,
|
||||
})
|
308
techage/basic_machines/forceload.lua
Normal file
308
techage/basic_machines/forceload.lua
Normal file
@ -0,0 +1,308 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019-2020 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
Forceload block
|
||||
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local M = minetest.get_meta
|
||||
local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local S = techage.S
|
||||
|
||||
local function calc_area(pos)
|
||||
local xpos = (math.floor(pos.x / 16) * 16)
|
||||
local ypos = (math.floor(pos.y / 16) * 16)
|
||||
local zpos = (math.floor(pos.z / 16) * 16)
|
||||
local pos1 = {x=xpos, y=ypos, z=zpos}
|
||||
local pos2 = {x=xpos+15, y=ypos+15, z=zpos+15}
|
||||
return pos1, pos2
|
||||
end
|
||||
|
||||
local function in_list(list, x)
|
||||
local pos1 = calc_area(x)
|
||||
for _,v in ipairs(list) do
|
||||
local pos2 = calc_area(v)
|
||||
if vector.equals(pos1, pos2) then return true end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function remove_list_elem(list, x)
|
||||
local n = nil
|
||||
for idx, v in ipairs(list) do
|
||||
if vector.equals(v, x) then
|
||||
n = idx
|
||||
break
|
||||
end
|
||||
end
|
||||
if n then
|
||||
table.remove(list, n)
|
||||
end
|
||||
return list
|
||||
end
|
||||
|
||||
local function chat(player, text)
|
||||
minetest.chat_send_player(player:get_player_name(), "[Techage] "..text)
|
||||
end
|
||||
|
||||
local function postload_area(pos)
|
||||
minetest.log("warning", "[FLB] area "..P2S(pos).." not loaded!")
|
||||
if not minetest.forceload_block(pos, true) then
|
||||
minetest.after(60, postload_area, pos)
|
||||
end
|
||||
end
|
||||
|
||||
local function add_pos(pos, player)
|
||||
local meta = player:get_meta()
|
||||
local lPos = minetest.deserialize(meta:get_string("techage_forceload_blocks")) or {}
|
||||
if not in_list(lPos, pos) and (#lPos < techage.max_num_forceload_blocks or
|
||||
minetest.global_exists("creative") and creative.is_enabled_for and
|
||||
creative.is_enabled_for(player:get_player_name())) then
|
||||
lPos[#lPos+1] = pos
|
||||
local meta = player:get_meta()
|
||||
meta:set_string("techage_forceload_blocks", minetest.serialize(lPos))
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function del_pos(pos, player)
|
||||
local meta = player:get_meta()
|
||||
local lPos = minetest.deserialize(meta:get_string("techage_forceload_blocks")) or {}
|
||||
lPos = remove_list_elem(lPos, pos)
|
||||
meta:set_string("techage_forceload_blocks", minetest.serialize(lPos))
|
||||
end
|
||||
|
||||
local function get_pos_list(player)
|
||||
local meta = player:get_meta()
|
||||
return minetest.deserialize(meta:get_string("techage_forceload_blocks")) or {}
|
||||
end
|
||||
|
||||
local function set_pos_list(player, lPos)
|
||||
local meta = player:get_meta()
|
||||
meta:set_string("techage_forceload_blocks", minetest.serialize(lPos))
|
||||
end
|
||||
|
||||
local function show_flbs(pos, name, range)
|
||||
local pos1 = {x=pos.x-range, y=pos.y-range, z=pos.z-range}
|
||||
local pos2 = {x=pos.x+range, y=pos.y+range, z=pos.z+range}
|
||||
for _,npos in ipairs(minetest.find_nodes_in_area(pos1, pos2, {"techage:forceload", "techage:forceloadtile"})) do
|
||||
local _pos1, _pos2 = calc_area(npos)
|
||||
local owner = M(npos):get_string("owner")
|
||||
techage.mark_region(name, _pos1, _pos2, owner .. " " .. P2S(npos))
|
||||
end
|
||||
end
|
||||
|
||||
local function get_data(pos, player)
|
||||
local pos1, pos2 = calc_area(pos)
|
||||
local meta = player:get_meta()
|
||||
local num = #minetest.deserialize(meta:get_string("techage_forceload_blocks")) or 0
|
||||
local max = techage.max_num_forceload_blocks
|
||||
return pos1, pos2, num, max
|
||||
end
|
||||
|
||||
local function formspec(name)
|
||||
local player = minetest.get_player_by_name(name)
|
||||
if player then
|
||||
local lPos = get_pos_list(player)
|
||||
local tRes = {}
|
||||
for idx,pos in ipairs(lPos) do
|
||||
local pos1, pos2 = calc_area(pos)
|
||||
tRes[#tRes+1] = idx
|
||||
tRes[#tRes+1] = minetest.formspec_escape(P2S(pos1))
|
||||
tRes[#tRes+1] = "to"
|
||||
tRes[#tRes+1] = minetest.formspec_escape(P2S(pos2))
|
||||
end
|
||||
return "size[7,9]"..
|
||||
default.gui_bg..
|
||||
default.gui_bg_img..
|
||||
default.gui_slots..
|
||||
"label[0,0;"..S("List of your Forceload Blocks:").."]"..
|
||||
"tablecolumns[text,width=1.2;text,width=12;text,width=1.6;text,width=12]"..
|
||||
"table[0,0.6;6.8,8.4;output;"..table.concat(tRes, ",")..";1]"
|
||||
end
|
||||
end
|
||||
|
||||
local function on_place(itemstack, placer, pointed_thing)
|
||||
if pointed_thing.type ~= "node" then
|
||||
return itemstack
|
||||
end
|
||||
return minetest.rotate_and_place(itemstack, placer, pointed_thing)
|
||||
end
|
||||
|
||||
local function after_place_node(pos, placer, itemstack)
|
||||
if add_pos(pos, placer) then
|
||||
minetest.forceload_block(pos, true)
|
||||
local pos1, pos2, num, max = get_data(pos, placer)
|
||||
M(pos):set_string("infotext", "Area "..P2S(pos1).." to "..P2S(pos2).." "..S("loaded").."!\n"..
|
||||
S("Punch the block to make the area visible."))
|
||||
chat(placer, "Area ("..num.."/"..max..") "..P2S(pos1).." to "..P2S(pos2).." "..S("loaded").."!")
|
||||
techage.mark_region(placer:get_player_name(), pos1, pos2)
|
||||
M(pos):set_string("owner", placer:get_player_name())
|
||||
else
|
||||
chat(placer, S("Area already loaded or max. number of Forceload Blocks reached!"))
|
||||
minetest.remove_node(pos)
|
||||
return itemstack
|
||||
end
|
||||
end
|
||||
|
||||
local function after_dig_node(pos, oldnode, oldmetadata, digger)
|
||||
local player = minetest.get_player_by_name(oldmetadata.fields.owner)
|
||||
if player then
|
||||
del_pos(pos, player)
|
||||
end
|
||||
minetest.forceload_free_block(pos, true)
|
||||
techage.unmark_region(oldmetadata.fields.owner)
|
||||
end
|
||||
|
||||
local function on_rightclick(pos, node, clicker, itemstack, pointed_thing)
|
||||
local owner = M(pos):get_string("owner")
|
||||
local name = clicker:get_player_name()
|
||||
if name == owner or minetest.check_player_privs(name, "server") then
|
||||
local s = formspec(owner)
|
||||
if s then
|
||||
minetest.show_formspec(name, "techage:forceload", s)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function on_punch(pos, node, puncher, pointed_thing)
|
||||
local pos1, pos2 = calc_area(pos)
|
||||
techage.switch_region(puncher:get_player_name(), pos1, pos2)
|
||||
end
|
||||
|
||||
minetest.register_node("techage:forceload", {
|
||||
description = S("Techage Forceload Block"),
|
||||
tiles = {
|
||||
-- up, down, right, left, back, front
|
||||
'techage_filling_ta2.png^techage_frame_ta2_top.png',
|
||||
'techage_filling_ta2.png^techage_frame_ta2_top.png',
|
||||
{
|
||||
name = "techage_filling_ta2.png^techage_frame_ta2_top.png^techage_appl_forceload.png",
|
||||
backface_culling = false,
|
||||
animation = {
|
||||
type = "vertical_frames",
|
||||
aspect_w = 32,
|
||||
aspect_h = 32,
|
||||
length = 0.5,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
after_place_node = after_place_node,
|
||||
after_dig_node = after_dig_node,
|
||||
on_rightclick = on_rightclick,
|
||||
on_punch = on_punch,
|
||||
|
||||
paramtype = "light",
|
||||
sunlight_propagates = true,
|
||||
use_texture_alpha = techage.CLIP,
|
||||
groups = {choppy=2, cracky=2, crumbly=2,
|
||||
digtron_protected = 1,
|
||||
not_in_creative_inventory = techage.max_num_forceload_blocks == 0 and 1 or 0},
|
||||
is_ground_content = false,
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
})
|
||||
|
||||
minetest.register_node("techage:forceloadtile", {
|
||||
description = S("Techage Forceload Tile"),
|
||||
tiles = {
|
||||
-- up, down, right, left, back, front
|
||||
{
|
||||
name = "techage_filling_ta2.png^techage_frame_ta2_top.png^techage_appl_forceload.png",
|
||||
backface_culling = false,
|
||||
animation = {
|
||||
type = "vertical_frames",
|
||||
aspect_w = 32,
|
||||
aspect_h = 32,
|
||||
length = 0.5,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
drawtype = "nodebox",
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
--{-5/16, -7/16, -5/16, 5/16, -5/16, 5/16},
|
||||
{-4/16, -8/16, -4/16, 4/16, -15/32, 4/16},
|
||||
},
|
||||
},
|
||||
|
||||
on_place = on_place,
|
||||
after_place_node = after_place_node,
|
||||
after_dig_node = after_dig_node,
|
||||
on_rightclick = on_rightclick,
|
||||
on_punch = on_punch,
|
||||
|
||||
paramtype = "light",
|
||||
paramtype2 = "facedir",
|
||||
sunlight_propagates = true,
|
||||
groups = {choppy=2, cracky=2, crumbly=2,
|
||||
not_in_creative_inventory = techage.max_num_forceload_blocks == 0 and 1 or 0},
|
||||
is_ground_content = false,
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
})
|
||||
|
||||
if techage.max_num_forceload_blocks > 0 then
|
||||
minetest.register_craft({
|
||||
output = "techage:forceload",
|
||||
recipe = {
|
||||
{"group:wood", "", "group:wood"},
|
||||
{"default:mese_crystal_fragment", "techage:usmium_nuggets", "default:mese_crystal_fragment"},
|
||||
{"group:wood", "techage:iron_ingot", "group:wood"},
|
||||
},
|
||||
})
|
||||
minetest.register_craft({
|
||||
type = "shapeless",
|
||||
output = "techage:forceloadtile",
|
||||
recipe = {"techage:forceload"},
|
||||
})
|
||||
minetest.register_craft({
|
||||
type = "shapeless",
|
||||
output = "techage:forceload",
|
||||
recipe = {"techage:forceloadtile"},
|
||||
})
|
||||
end
|
||||
|
||||
minetest.register_on_joinplayer(function(player)
|
||||
local lPos = {}
|
||||
for _,pos in ipairs(get_pos_list(player)) do
|
||||
local node = techage.get_node_lvm(pos)
|
||||
if node.name == "techage:forceload" or node.name == "techage:forceloadtile" then
|
||||
if not minetest.forceload_block(pos, true) then
|
||||
minetest.after(60, postload_area, pos)
|
||||
end
|
||||
lPos[#lPos+1] = pos
|
||||
end
|
||||
end
|
||||
set_pos_list(player, lPos)
|
||||
end)
|
||||
|
||||
minetest.register_on_leaveplayer(function(player)
|
||||
for _,pos in ipairs(get_pos_list(player)) do
|
||||
minetest.forceload_free_block(pos, true)
|
||||
end
|
||||
end)
|
||||
|
||||
|
||||
minetest.register_chatcommand("forceload", {
|
||||
params = "",
|
||||
description = S("Show all forceload blocks in a 64x64x64 range"),
|
||||
func = function(name, param)
|
||||
local player = minetest.get_player_by_name(name)
|
||||
if player then
|
||||
local pos = player:get_pos()
|
||||
pos = vector.round(pos)
|
||||
show_flbs(pos, name, 64)
|
||||
end
|
||||
end,
|
||||
})
|
155
techage/basic_machines/foreign_nodes.lua
Normal file
155
techage/basic_machines/foreign_nodes.lua
Normal file
@ -0,0 +1,155 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019-2020 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
Tube support for default chests and furnace
|
||||
|
||||
]]--
|
||||
|
||||
local OwnerCache = {
|
||||
}
|
||||
|
||||
-- Check if the chest is in the protected area of the owner
|
||||
local function is_owner(pos, meta)
|
||||
local owner = meta:get_string("owner")
|
||||
local key = minetest.hash_node_position(pos)
|
||||
-- If successfull, store info in cache
|
||||
if OwnerCache[key] ~= owner then
|
||||
if not minetest.is_protected(pos, owner) then
|
||||
OwnerCache[key] = owner
|
||||
end
|
||||
end
|
||||
return OwnerCache[key] == owner
|
||||
end
|
||||
|
||||
|
||||
techage.register_node({"default:chest", "default:chest_open"}, {
|
||||
on_inv_request = function(pos, in_dir, access_type)
|
||||
local meta = minetest.get_meta(pos)
|
||||
return meta:get_inventory(), "main"
|
||||
end,
|
||||
on_pull_item = function(pos, in_dir, num)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
return techage.get_items(pos, inv, "main", num)
|
||||
end,
|
||||
on_push_item = function(pos, in_dir, stack)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
return techage.put_items(inv, "main", stack)
|
||||
end,
|
||||
on_unpull_item = function(pos, in_dir, stack)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
return techage.put_items(inv, "main", stack)
|
||||
end,
|
||||
})
|
||||
|
||||
techage.register_node({"default:chest_locked", "default:chest_locked_open"}, {
|
||||
on_inv_request = function(pos, in_dir, access_type)
|
||||
local meta = minetest.get_meta(pos)
|
||||
if is_owner(pos, meta) then
|
||||
return meta:get_inventory(), "main"
|
||||
end
|
||||
end,
|
||||
on_pull_item = function(pos, in_dir, num)
|
||||
local meta = minetest.get_meta(pos)
|
||||
if is_owner(pos, meta) then
|
||||
local inv = meta:get_inventory()
|
||||
return techage.get_items(pos, inv, "main", num)
|
||||
end
|
||||
end,
|
||||
on_push_item = function(pos, in_dir, stack)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
return techage.put_items(inv, "main", stack)
|
||||
end,
|
||||
on_unpull_item = function(pos, in_dir, stack)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
return techage.put_items(inv, "main", stack)
|
||||
end,
|
||||
})
|
||||
|
||||
techage.register_node({"shop:shop"}, {
|
||||
on_inv_request = function(pos, in_dir, access_type)
|
||||
local meta = minetest.get_meta(pos)
|
||||
if is_owner(pos, meta) then
|
||||
return meta:get_inventory(), "main"
|
||||
end
|
||||
end,
|
||||
on_pull_item = function(pos, in_dir, num)
|
||||
local meta = minetest.get_meta(pos)
|
||||
if is_owner(pos, meta) then
|
||||
local inv = meta:get_inventory()
|
||||
return techage.get_items(pos, inv, "register", num)
|
||||
end
|
||||
end,
|
||||
on_push_item = function(pos, in_dir, stack)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
return techage.put_items(inv, "stock", stack)
|
||||
end,
|
||||
on_unpull_item = function(pos, in_dir, stack)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
return techage.put_items(inv, "register", stack)
|
||||
end,
|
||||
})
|
||||
|
||||
techage.register_node({"default:furnace", "default:furnace_active"}, {
|
||||
on_pull_item = function(pos, in_dir, num)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
return techage.get_items(pos, inv, "dst", num)
|
||||
end,
|
||||
on_push_item = function(pos, in_dir, stack)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
minetest.get_node_timer(pos):start(1.0)
|
||||
if in_dir == 5 then
|
||||
return techage.put_items(inv, "src", stack)
|
||||
elseif minetest.get_craft_result({method="fuel", width=1, items={stack}}).time ~= 0 then
|
||||
return techage.put_items(inv, "fuel", stack)
|
||||
else
|
||||
return techage.put_items(inv, "src", stack)
|
||||
end
|
||||
end,
|
||||
on_unpull_item = function(pos, in_dir, stack)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
return techage.put_items(inv, "dst", stack)
|
||||
end,
|
||||
})
|
||||
|
||||
techage.register_node({"mobs:beehive"}, {
|
||||
on_pull_item = function(pos, in_dir, num)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
return techage.get_items(pos, inv, "beehive", num)
|
||||
end,
|
||||
on_unpull_item = function(pos, in_dir, stack)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
return techage.put_items(inv, "beehive", stack)
|
||||
end,
|
||||
})
|
||||
|
||||
techage.register_node({"xdecor:hive"}, {
|
||||
on_pull_item = function(pos, in_dir, num)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
return techage.get_items(pos, inv, "honey", num)
|
||||
end,
|
||||
on_unpull_item = function(pos, in_dir, stack)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
return techage.put_items(inv, "honey", stack)
|
||||
end,
|
||||
})
|
341
techage/basic_machines/gravelrinser.lua
Normal file
341
techage/basic_machines/gravelrinser.lua
Normal file
@ -0,0 +1,341 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019-2022 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
TA2 Gravel Rinser, washing sieved gravel to find more ores
|
||||
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local M = minetest.get_meta
|
||||
local S = techage.S
|
||||
|
||||
-- Consumer Related Data
|
||||
local CRD = function(pos) return (minetest.registered_nodes[techage.get_node_lvm(pos).name] or {}).consumer end
|
||||
|
||||
local STANDBY_TICKS = 3
|
||||
local COUNTDOWN_TICKS = 4
|
||||
local CYCLE_TIME = 4
|
||||
|
||||
local Probability = {}
|
||||
|
||||
local function formspec(self, pos, nvm)
|
||||
return "size[8,8]"..
|
||||
default.gui_bg..
|
||||
default.gui_bg_img..
|
||||
default.gui_slots..
|
||||
"list[context;src;0,0;3,3;]"..
|
||||
"item_image[0,0;1,1;default:gravel]"..
|
||||
"image[0,0;1,1;techage_form_mask.png]"..
|
||||
"image[3.5,0;1,1;"..techage.get_power_image(pos, nvm).."]"..
|
||||
"image[3.5,1;1,1;techage_form_arrow.png]"..
|
||||
"image_button[3.5,2;1,1;".. self:get_state_button_image(nvm) ..";state_button;]"..
|
||||
"tooltip[3.5,2;1,1;"..self:get_state_tooltip(nvm).."]"..
|
||||
"list[context;dst;5,0;3,3;]"..
|
||||
"list[current_player;main;0,4;8,4;]"..
|
||||
"listring[context;dst]"..
|
||||
"listring[current_player;main]"..
|
||||
"listring[context;src]"..
|
||||
"listring[current_player;main]"..
|
||||
default.get_hotbar_bg(0, 4)
|
||||
end
|
||||
|
||||
local function allow_metadata_inventory_put(pos, listname, index, stack, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return 0
|
||||
end
|
||||
if listname == "src" then
|
||||
CRD(pos).State:start_if_standby(pos)
|
||||
return stack:get_count()
|
||||
elseif listname == "dst" then
|
||||
return 0
|
||||
end
|
||||
end
|
||||
|
||||
local function allow_metadata_inventory_move(pos, from_list, from_index, to_list, to_index, count, player)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
local stack = inv:get_stack(from_list, from_index)
|
||||
return allow_metadata_inventory_put(pos, to_list, to_index, stack, player)
|
||||
end
|
||||
|
||||
local function allow_metadata_inventory_take(pos, listname, index, stack, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return 0
|
||||
end
|
||||
return stack:get_count()
|
||||
end
|
||||
|
||||
|
||||
local function get_water_level(pos)
|
||||
local node = techage.get_node_lvm(pos)
|
||||
if minetest.get_item_group(node.name, "water") > 0 then
|
||||
local ndef = minetest.registered_nodes[node.name]
|
||||
if ndef and ndef.liquidtype == "flowing" then
|
||||
return node.param2
|
||||
end
|
||||
end
|
||||
return 99
|
||||
end
|
||||
|
||||
local function determine_water_dir(pos)
|
||||
local lvl = get_water_level(pos)
|
||||
if lvl > get_water_level({x=pos.x+1, y=pos.y, z=pos.z}) then
|
||||
return 2
|
||||
end
|
||||
if lvl > get_water_level({x=pos.x-1, y=pos.y, z=pos.z}) then
|
||||
return 4
|
||||
end
|
||||
if lvl > get_water_level({x=pos.x, y=pos.y, z=pos.z+1}) then
|
||||
return 1
|
||||
end
|
||||
if lvl > get_water_level({x=pos.x, y=pos.y, z=pos.z-1}) then
|
||||
return 3
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
local function set_velocity(obj, pos, vel)
|
||||
if obj then
|
||||
obj:set_acceleration({x = 0, y = 0, z = 0})
|
||||
local p = obj:get_pos()
|
||||
if p then
|
||||
obj:set_pos({x=p.x, y=p.y-0.3, z=p.z})
|
||||
obj:set_velocity(vel)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function add_object(pos, name)
|
||||
local dir = determine_water_dir(pos)
|
||||
if dir > 0 then
|
||||
local obj = minetest.add_item(pos, ItemStack(name))
|
||||
local vel = vector.multiply(tubelib2.Dir6dToVector[dir], 0.3)
|
||||
minetest.after(0.3, set_velocity, obj, pos, vel)
|
||||
end
|
||||
end
|
||||
|
||||
local function get_random_gravel_ore()
|
||||
for ore, probability in pairs(Probability) do
|
||||
if math.random(probability) == 1 then
|
||||
return ore
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function remove_objects(pos)
|
||||
for _, object in pairs(minetest.get_objects_inside_radius(pos, 1)) do
|
||||
local lua_entity = object:get_luaentity()
|
||||
if not object:is_player() and lua_entity and lua_entity.name == "__builtin:item" then
|
||||
object:remove()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function washing(pos, crd, nvm, inv)
|
||||
-- for testing purposes
|
||||
if inv:contains_item("src", ItemStack("default:stick")) then
|
||||
add_object({x=pos.x, y=pos.y+1, z=pos.z}, "default:stick")
|
||||
inv:remove_item("src", ItemStack("default:stick"))
|
||||
crd.State:keep_running(pos, nvm, COUNTDOWN_TICKS)
|
||||
return
|
||||
end
|
||||
|
||||
local src = ItemStack("techage:sieved_gravel")
|
||||
local dst = ItemStack("default:sand")
|
||||
if inv:contains_item("src", src) then
|
||||
if not inv:room_for_item("dst", dst) then
|
||||
crd.State:blocked(pos, nvm)
|
||||
return
|
||||
end
|
||||
local ore = get_random_gravel_ore()
|
||||
if ore then
|
||||
add_object({x=pos.x, y=pos.y+1, z=pos.z}, ore)
|
||||
end
|
||||
inv:add_item("dst", dst)
|
||||
inv:remove_item("src", src)
|
||||
crd.State:keep_running(pos, nvm, COUNTDOWN_TICKS)
|
||||
return
|
||||
else
|
||||
crd.State:idle(pos, nvm)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local function keep_running(pos, elapsed)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
local crd = CRD(pos)
|
||||
local inv = M(pos):get_inventory()
|
||||
washing(pos, crd, nvm, inv)
|
||||
end
|
||||
|
||||
local function on_receive_fields(pos, formname, fields, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return
|
||||
end
|
||||
local nvm = techage.get_nvm(pos)
|
||||
CRD(pos).State:state_button_event(pos, nvm, fields)
|
||||
end
|
||||
|
||||
local function can_dig(pos, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return false
|
||||
end
|
||||
local inv = M(pos):get_inventory()
|
||||
return inv:is_empty("dst") and inv:is_empty("src")
|
||||
end
|
||||
|
||||
|
||||
local tiles = {}
|
||||
-- '#' will be replaced by the stage number
|
||||
-- '{power}' will be replaced by the power PNG
|
||||
tiles.pas = {
|
||||
-- up, down, right, left, back, front
|
||||
"techage_appl_rinser_top.png^techage_frame_ta#_top.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_outp.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_inp.png",
|
||||
"techage_filling_ta#.png^techage_appl_rinser.png^techage_frame_ta#.png",
|
||||
"techage_filling_ta#.png^techage_appl_rinser.png^techage_frame_ta#.png",
|
||||
}
|
||||
tiles.act = {
|
||||
-- up, down, right, left, back, front
|
||||
{
|
||||
name = "techage_appl_rinser4_top.png^techage_frame4_ta#_top.png",
|
||||
backface_culling = false,
|
||||
animation = {
|
||||
type = "vertical_frames",
|
||||
aspect_w = 32,
|
||||
aspect_h = 32,
|
||||
length = 2.0,
|
||||
},
|
||||
},
|
||||
"techage_filling_ta#.png^techage_frame_ta#.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_outp.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_inp.png",
|
||||
"techage_filling_ta#.png^techage_appl_rinser.png^techage_frame_ta#.png",
|
||||
"techage_filling_ta#.png^techage_appl_rinser.png^techage_frame_ta#.png",
|
||||
}
|
||||
|
||||
local tubing = {
|
||||
on_pull_item = function(pos, in_dir, num)
|
||||
local meta = minetest.get_meta(pos)
|
||||
if meta:get_int("pull_dir") == in_dir then
|
||||
local inv = M(pos):get_inventory()
|
||||
return techage.get_items(pos, inv, "dst", num)
|
||||
end
|
||||
end,
|
||||
on_push_item = function(pos, in_dir, stack)
|
||||
local meta = minetest.get_meta(pos)
|
||||
if meta:get_int("push_dir") == in_dir or in_dir == 5 then
|
||||
local inv = M(pos):get_inventory()
|
||||
-- CRD(pos).State:start_if_standby(pos) -- would need power!
|
||||
return techage.put_items(inv, "src", stack)
|
||||
end
|
||||
end,
|
||||
on_unpull_item = function(pos, in_dir, stack)
|
||||
local meta = minetest.get_meta(pos)
|
||||
if meta:get_int("pull_dir") == in_dir then
|
||||
local inv = M(pos):get_inventory()
|
||||
return techage.put_items(inv, "dst", stack)
|
||||
end
|
||||
end,
|
||||
on_recv_message = function(pos, src, topic, payload)
|
||||
return CRD(pos).State:on_receive_message(pos, topic, payload)
|
||||
end,
|
||||
on_beduino_receive_cmnd = function(pos, src, topic, payload)
|
||||
return CRD(pos).State:on_beduino_receive_cmnd(pos, topic, payload)
|
||||
end,
|
||||
on_beduino_request_data = function(pos, src, topic, payload)
|
||||
return CRD(pos).State:on_beduino_request_data(pos, topic, payload)
|
||||
end,
|
||||
on_node_load = function(pos)
|
||||
remove_objects({x=pos.x, y=pos.y+1, z=pos.z})
|
||||
CRD(pos).State:on_node_load(pos)
|
||||
end,
|
||||
}
|
||||
|
||||
local node_name_ta2, node_name_ta3, node_name_ta4 =
|
||||
techage.register_consumer("rinser", S("Gravel Rinser"), tiles, {
|
||||
drawtype = "nodebox",
|
||||
paramtype = "light",
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{-8/16, -8/16, -8/16, 8/16, 8/16, -6/16},
|
||||
{-8/16, -8/16, 6/16, 8/16, 8/16, 8/16},
|
||||
{-8/16, -8/16, -8/16, -6/16, 8/16, 8/16},
|
||||
{ 6/16, -8/16, -8/16, 8/16, 8/16, 8/16},
|
||||
{-6/16, -8/16, -6/16, 6/16, 6/16, 6/16},
|
||||
{-6/16, 6/16, -1/16, 6/16, 8/16, 1/16},
|
||||
{-1/16, 6/16, -6/16, 1/16, 8/16, 6/16},
|
||||
},
|
||||
},
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
fixed = {-8/16, -8/16, -8/16, 8/16, 8/16, 8/16},
|
||||
},
|
||||
cycle_time = CYCLE_TIME,
|
||||
standby_ticks = STANDBY_TICKS,
|
||||
formspec = formspec,
|
||||
tubing = tubing,
|
||||
after_place_node = function(pos, placer)
|
||||
local inv = M(pos):get_inventory()
|
||||
inv:set_size('src', 9)
|
||||
inv:set_size('dst', 9)
|
||||
end,
|
||||
can_dig = can_dig,
|
||||
node_timer = keep_running,
|
||||
on_receive_fields = on_receive_fields,
|
||||
allow_metadata_inventory_put = allow_metadata_inventory_put,
|
||||
allow_metadata_inventory_move = allow_metadata_inventory_move,
|
||||
allow_metadata_inventory_take = allow_metadata_inventory_take,
|
||||
groups = {choppy=2, cracky=2, crumbly=2},
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
num_items = {0,1,1,1},
|
||||
power_consumption = {0,3,3,3},
|
||||
tube_sides = {L=1, R=1, U=1},
|
||||
},
|
||||
{false, true, true, false}) -- TA2/TA3
|
||||
|
||||
minetest.register_craft({
|
||||
output = node_name_ta2,
|
||||
recipe = {
|
||||
{"group:wood", "default:mese_crystal", "group:wood"},
|
||||
{"techage:tubeS", "techage:sieve", "techage:tubeS"},
|
||||
{"group:wood", "default:tin_ingot", "group:wood"},
|
||||
},
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = node_name_ta3,
|
||||
recipe = {
|
||||
{"", "default:mese_crystal", ""},
|
||||
{"", node_name_ta2, ""},
|
||||
{"", "techage:vacuum_tube", ""},
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
techage.recipes.register_craft_type("rinsing", {
|
||||
description = S("Rinsing"),
|
||||
icon = "techage_appl_rinser_top.png^techage_frame_ta2_top.png",
|
||||
width = 2,
|
||||
height = 2,
|
||||
})
|
||||
|
||||
function techage.add_rinser_recipe(recipe)
|
||||
Probability[recipe.output] = recipe.probability
|
||||
recipe.items = {recipe.input}
|
||||
recipe.type = "rinsing"
|
||||
techage.recipes.register_craft(recipe)
|
||||
end
|
||||
|
||||
techage.add_rinser_recipe({input="techage:sieved_gravel", output="techage:usmium_nuggets", probability=30})
|
||||
techage.add_rinser_recipe({input="techage:sieved_gravel", output="default:copper_lump", probability=15})
|
287
techage/basic_machines/gravelsieve.lua
Normal file
287
techage/basic_machines/gravelsieve.lua
Normal file
@ -0,0 +1,287 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019-2022 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
TA2/TA3/TA4 Gravel Sieve, sieving gravel to find ores
|
||||
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local M = minetest.get_meta
|
||||
local S = techage.S
|
||||
|
||||
-- Consumer Related Data
|
||||
local CRD = function(pos) return (minetest.registered_nodes[techage.get_node_lvm(pos).name] or {}).consumer end
|
||||
|
||||
local STANDBY_TICKS = 3
|
||||
local COUNTDOWN_TICKS = 4
|
||||
local CYCLE_TIME = 4
|
||||
|
||||
local get_random_gravel_ore = techage.gravelsieve_get_random_gravel_ore
|
||||
local get_random_basalt_ore = techage.gravelsieve_get_random_basalt_ore
|
||||
|
||||
|
||||
local function formspec(self, pos, nvm)
|
||||
return "size[8,8]"..
|
||||
default.gui_bg..
|
||||
default.gui_bg_img..
|
||||
default.gui_slots..
|
||||
"list[context;src;0,0;3,3;]"..
|
||||
"item_image[0,0;1,1;default:gravel]"..
|
||||
"image[0,0;1,1;techage_form_mask.png]"..
|
||||
"image[3.5,0;1,1;"..techage.get_power_image(pos, nvm).."]"..
|
||||
"image[3.5,1;1,1;techage_form_arrow.png]"..
|
||||
"image_button[3.5,2;1,1;".. self:get_state_button_image(nvm) ..";state_button;]"..
|
||||
"tooltip[3.5,2;1,1;"..self:get_state_tooltip(nvm).."]"..
|
||||
"list[context;dst;5,0;3,3;]"..
|
||||
"list[current_player;main;0,4;8,4;]"..
|
||||
"listring[context;dst]"..
|
||||
"listring[current_player;main]"..
|
||||
"listring[context;src]"..
|
||||
"listring[current_player;main]"..
|
||||
default.get_hotbar_bg(0, 4)
|
||||
end
|
||||
|
||||
local function allow_metadata_inventory_put(pos, listname, index, stack, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return 0
|
||||
end
|
||||
if listname == "src" then
|
||||
CRD(pos).State:start_if_standby(pos)
|
||||
return stack:get_count()
|
||||
elseif listname == "dst" then
|
||||
return 0
|
||||
end
|
||||
end
|
||||
|
||||
local function allow_metadata_inventory_move(pos, from_list, from_index, to_list, to_index, count, player)
|
||||
local inv = M(pos):get_inventory()
|
||||
local stack = inv:get_stack(from_list, from_index)
|
||||
return allow_metadata_inventory_put(pos, to_list, to_index, stack, player)
|
||||
end
|
||||
|
||||
local function allow_metadata_inventory_take(pos, listname, index, stack, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return 0
|
||||
end
|
||||
return stack:get_count()
|
||||
end
|
||||
|
||||
local function sieving(pos, crd, nvm, inv)
|
||||
local src, dst
|
||||
for i = 1, crd.num_items do
|
||||
if inv:contains_item("src", ItemStack("techage:basalt_gravel")) then
|
||||
dst, src = get_random_basalt_ore(), ItemStack("techage:basalt_gravel")
|
||||
elseif inv:contains_item("src", ItemStack("default:gravel")) then
|
||||
dst, src = get_random_gravel_ore(), ItemStack("default:gravel")
|
||||
else
|
||||
crd.State:idle(pos, nvm)
|
||||
return
|
||||
end
|
||||
if not inv:room_for_item("dst", dst) then
|
||||
crd.State:idle(pos, nvm)
|
||||
return
|
||||
end
|
||||
inv:add_item("dst", dst)
|
||||
inv:remove_item("src", src)
|
||||
end
|
||||
crd.State:keep_running(pos, nvm, COUNTDOWN_TICKS)
|
||||
end
|
||||
|
||||
local function keep_running(pos, elapsed)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
local crd = CRD(pos)
|
||||
local inv = M(pos):get_inventory()
|
||||
sieving(pos, crd, nvm, inv)
|
||||
end
|
||||
|
||||
local function on_receive_fields(pos, formname, fields, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return
|
||||
end
|
||||
local nvm = techage.get_nvm(pos)
|
||||
CRD(pos).State:state_button_event(pos, nvm, fields)
|
||||
end
|
||||
|
||||
local function can_dig(pos, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return false
|
||||
end
|
||||
local inv = M(pos):get_inventory()
|
||||
return inv:is_empty("dst") and inv:is_empty("src")
|
||||
end
|
||||
|
||||
|
||||
local tiles = {}
|
||||
-- '#' will be replaced by the stage number
|
||||
-- '{power}' will be replaced by the power PNG
|
||||
tiles.pas = {
|
||||
-- up, down, right, left, back, front
|
||||
"techage_appl_sieve_top.png^techage_frame_ta#_top.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_outp.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_inp.png",
|
||||
"techage_filling_ta#.png^techage_appl_sieve.png^techage_frame_ta#.png",
|
||||
"techage_filling_ta#.png^techage_appl_sieve.png^techage_frame_ta#.png",
|
||||
}
|
||||
tiles.act = {
|
||||
-- up, down, right, left, back, front
|
||||
{
|
||||
name = "techage_appl_sieve4_top.png^techage_frame4_ta#_top.png",
|
||||
backface_culling = false,
|
||||
animation = {
|
||||
type = "vertical_frames",
|
||||
aspect_w = 32,
|
||||
aspect_h = 32,
|
||||
length = 2.0,
|
||||
},
|
||||
},
|
||||
"techage_filling_ta#.png^techage_frame_ta#.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_outp.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_inp.png",
|
||||
"techage_filling_ta#.png^techage_appl_sieve.png^techage_frame_ta#.png",
|
||||
"techage_filling_ta#.png^techage_appl_sieve.png^techage_frame_ta#.png",
|
||||
}
|
||||
|
||||
local tubing = {
|
||||
on_pull_item = function(pos, in_dir, num)
|
||||
local meta = minetest.get_meta(pos)
|
||||
if meta:get_int("pull_dir") == in_dir then
|
||||
local inv = M(pos):get_inventory()
|
||||
return techage.get_items(pos, inv, "dst", num)
|
||||
end
|
||||
end,
|
||||
on_push_item = function(pos, in_dir, stack)
|
||||
local meta = minetest.get_meta(pos)
|
||||
if meta:get_int("push_dir") == in_dir or in_dir == 5 then
|
||||
local inv = M(pos):get_inventory()
|
||||
return techage.put_items(inv, "src", stack)
|
||||
end
|
||||
end,
|
||||
on_unpull_item = function(pos, in_dir, stack)
|
||||
local meta = minetest.get_meta(pos)
|
||||
if meta:get_int("pull_dir") == in_dir then
|
||||
local inv = M(pos):get_inventory()
|
||||
return techage.put_items(inv, "dst", stack)
|
||||
end
|
||||
end,
|
||||
on_recv_message = function(pos, src, topic, payload)
|
||||
return CRD(pos).State:on_receive_message(pos, topic, payload)
|
||||
end,
|
||||
on_beduino_receive_cmnd = function(pos, src, topic, payload)
|
||||
return CRD(pos).State:on_beduino_receive_cmnd(pos, topic, payload)
|
||||
end,
|
||||
on_beduino_request_data = function(pos, src, topic, payload)
|
||||
return CRD(pos).State:on_beduino_request_data(pos, topic, payload)
|
||||
end,
|
||||
on_node_load = function(pos)
|
||||
CRD(pos).State:on_node_load(pos)
|
||||
end,
|
||||
}
|
||||
|
||||
local node_name_ta2, node_name_ta3, node_name_ta4 =
|
||||
techage.register_consumer("gravelsieve", S("Gravel Sieve"), tiles, {
|
||||
drawtype = "nodebox",
|
||||
paramtype = "light",
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{-8/16, -8/16, -8/16, 8/16, 8/16, -6/16},
|
||||
{-8/16, -8/16, 6/16, 8/16, 8/16, 8/16},
|
||||
{-8/16, -8/16, -8/16, -6/16, 8/16, 8/16},
|
||||
{ 6/16, -8/16, -8/16, 8/16, 8/16, 8/16},
|
||||
{-6/16, -8/16, -6/16, 6/16, 4/16, 6/16},
|
||||
},
|
||||
},
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
fixed = {-8/16, -8/16, -8/16, 8/16, 8/16, 8/16},
|
||||
},
|
||||
cycle_time = CYCLE_TIME,
|
||||
standby_ticks = STANDBY_TICKS,
|
||||
formspec = formspec,
|
||||
tubing = tubing,
|
||||
after_place_node = function(pos, placer)
|
||||
local inv = M(pos):get_inventory()
|
||||
inv:set_size('src', 9)
|
||||
inv:set_size('dst', 9)
|
||||
end,
|
||||
can_dig = can_dig,
|
||||
node_timer = keep_running,
|
||||
on_receive_fields = on_receive_fields,
|
||||
allow_metadata_inventory_put = allow_metadata_inventory_put,
|
||||
allow_metadata_inventory_move = allow_metadata_inventory_move,
|
||||
allow_metadata_inventory_take = allow_metadata_inventory_take,
|
||||
groups = {choppy=2, cracky=2, crumbly=2},
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
num_items = {0,1,2,4},
|
||||
power_consumption = {0,3,4,5},
|
||||
tube_sides = {L=1, R=1, U=1},
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = node_name_ta2,
|
||||
recipe = {
|
||||
{"group:wood", "default:mese_crystal", "group:wood"},
|
||||
{"techage:tubeS", "techage:sieve", "techage:tubeS"},
|
||||
{"group:wood", "techage:iron_ingot", "group:wood"},
|
||||
},
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = node_name_ta3,
|
||||
recipe = {
|
||||
{"", "default:mese_crystal", ""},
|
||||
{"", node_name_ta2, ""},
|
||||
{"", "techage:vacuum_tube", ""},
|
||||
},
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = node_name_ta4,
|
||||
recipe = {
|
||||
{"", "default:mese_crystal", ""},
|
||||
{"", node_name_ta3, ""},
|
||||
{"", "techage:ta4_wlanchip", ""},
|
||||
},
|
||||
})
|
||||
|
||||
techage.recipes.register_craft_type("ta2_gravelsieve", {
|
||||
description = S("TA2 Gravel Sieve"),
|
||||
icon = 'techage_sieve_sieve_ta1.png',
|
||||
width = 1,
|
||||
height = 1,
|
||||
})
|
||||
techage.recipes.register_craft_type("ta3_gravelsieve", {
|
||||
description = S("TA3 Gravel Sieve"),
|
||||
icon = 'techage_filling_ta3.png^techage_appl_sieve.png^techage_frame_ta3.png',
|
||||
width = 1,
|
||||
height = 1,
|
||||
})
|
||||
techage.recipes.register_craft_type("ta4_gravelsieve", {
|
||||
description = S("TA4 Gravel Sieve"),
|
||||
icon = 'techage_filling_ta4.png^techage_appl_sieve.png^techage_frame_ta4.png',
|
||||
width = 1,
|
||||
height = 1,
|
||||
})
|
||||
techage.recipes.register_craft({
|
||||
output = "techage:sieved_basalt_gravel",
|
||||
items = {"techage:basalt_gravel"},
|
||||
type = "ta2_gravelsieve",
|
||||
})
|
||||
techage.recipes.register_craft({
|
||||
output = "techage:sieved_basalt_gravel",
|
||||
items = {"techage:basalt_gravel"},
|
||||
type = "ta3_gravelsieve",
|
||||
})
|
||||
techage.recipes.register_craft({
|
||||
output = "techage:sieved_basalt_gravel",
|
||||
items = {"techage:basalt_gravel"},
|
||||
type = "ta4_gravelsieve",
|
||||
})
|
435
techage/basic_machines/grinder.lua
Normal file
435
techage/basic_machines/grinder.lua
Normal file
@ -0,0 +1,435 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019-2022 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
TA2/TA3/TA4 Grinder, grinding Cobble/Basalt to Gravel
|
||||
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local M = minetest.get_meta
|
||||
local S = techage.S
|
||||
|
||||
-- Consumer Related Data
|
||||
local CRD = function(pos) return (minetest.registered_nodes[techage.get_node_lvm(pos).name] or {}).consumer or {} end
|
||||
|
||||
local STANDBY_TICKS = 3
|
||||
local COUNTDOWN_TICKS = 4
|
||||
local CYCLE_TIME = 4
|
||||
|
||||
-- Grinder recipes TA1
|
||||
local RecipesTa1 = {}
|
||||
-- Grinder recipes TA2 - TA4
|
||||
local Recipes = {}
|
||||
|
||||
local function formspec(self, pos, nvm)
|
||||
return "size[8,8]"..
|
||||
default.gui_bg..
|
||||
default.gui_bg_img..
|
||||
default.gui_slots..
|
||||
"list[context;src;0,0;3,3;]"..
|
||||
"item_image[0,0;1,1;default:cobble]"..
|
||||
"image[0,0;1,1;techage_form_mask.png]"..
|
||||
"image[3.5,0;1,1;"..techage.get_power_image(pos, nvm).."]"..
|
||||
"image[3.5,1;1,1;techage_form_arrow.png]"..
|
||||
"image_button[3.5,2;1,1;"..self:get_state_button_image(nvm)..";state_button;]"..
|
||||
"tooltip[3.5,2;1,1;"..self:get_state_tooltip(nvm).."]"..
|
||||
"list[context;dst;5,0;3,3;]"..
|
||||
"item_image[5,0;1,1;default:gravel]"..
|
||||
"image[5,0;1,1;techage_form_mask.png]"..
|
||||
"list[current_player;main;0,4;8,4;]"..
|
||||
"listring[context;dst]"..
|
||||
"listring[current_player;main]"..
|
||||
"listring[context;src]"..
|
||||
"listring[current_player;main]"..
|
||||
default.get_hotbar_bg(0, 4)
|
||||
end
|
||||
|
||||
local function allow_metadata_inventory_put(pos, listname, index, stack, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return 0
|
||||
end
|
||||
if listname == "src" then
|
||||
local state = CRD(pos).State
|
||||
if state then
|
||||
state:start_if_standby(pos)
|
||||
end
|
||||
end
|
||||
return stack:get_count()
|
||||
end
|
||||
|
||||
local function allow_metadata_inventory_move(pos, from_list, from_index, to_list, to_index, count, player)
|
||||
local inv = M(pos):get_inventory()
|
||||
local stack = inv:get_stack(from_list, from_index)
|
||||
return allow_metadata_inventory_put(pos, to_list, to_index, stack, player)
|
||||
end
|
||||
|
||||
local function allow_metadata_inventory_take(pos, listname, index, stack, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return 0
|
||||
end
|
||||
return stack:get_count()
|
||||
end
|
||||
|
||||
-- Grinder normaly handles 'num_items' per cycle. 'num_items' is node stage dependent.
|
||||
-- But if 'inp_num' > 1 (wheat recipes), use 'inp_num' and produce one output item.
|
||||
local function src_to_dst(src_stack, idx, src_name, num_items, inp_num, inv, dst_name)
|
||||
if inp_num > 1 then
|
||||
local input = ItemStack(src_name)
|
||||
input:set_count(inp_num)
|
||||
local output = ItemStack(dst_name)
|
||||
if inv:contains_item("src", input) and inv:room_for_item("dst", output) then
|
||||
inv:remove_item("src", input)
|
||||
inv:add_item("dst", output)
|
||||
return true
|
||||
end
|
||||
else
|
||||
local taken = src_stack:take_item(num_items)
|
||||
local output = ItemStack(dst_name)
|
||||
output:set_count(output:get_count() * taken:get_count())
|
||||
if inv:room_for_item("dst", output) then
|
||||
inv:set_stack("src", idx, src_stack)
|
||||
inv:add_item("dst", output)
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function grinding(pos, crd, nvm, inv)
|
||||
local blocked = false -- idle
|
||||
for idx,stack in ipairs(inv:get_list("src")) do
|
||||
if not stack:is_empty() then
|
||||
local name = stack:get_name()
|
||||
if Recipes[name] then
|
||||
local recipe = Recipes[name]
|
||||
if src_to_dst(stack, idx, name, crd.num_items, recipe.inp_num, inv, recipe.output) then
|
||||
crd.State:keep_running(pos, nvm, COUNTDOWN_TICKS)
|
||||
return
|
||||
else
|
||||
blocked = true
|
||||
end
|
||||
else
|
||||
crd.State:fault(pos, nvm)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
if blocked then
|
||||
crd.State:blocked(pos, nvm)
|
||||
else
|
||||
crd.State:idle(pos, nvm)
|
||||
end
|
||||
end
|
||||
|
||||
local function keep_running(pos, elapsed)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
local crd = CRD(pos)
|
||||
local inv = M(pos):get_inventory()
|
||||
grinding(pos, crd, nvm, inv)
|
||||
end
|
||||
|
||||
local function on_receive_fields(pos, formname, fields, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return
|
||||
end
|
||||
local nvm = techage.get_nvm(pos)
|
||||
CRD(pos).State:state_button_event(pos, nvm, fields)
|
||||
end
|
||||
|
||||
local function can_dig(pos, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return false
|
||||
end
|
||||
local inv = M(pos):get_inventory()
|
||||
return inv:is_empty("dst") and inv:is_empty("src")
|
||||
end
|
||||
|
||||
|
||||
local tiles = {}
|
||||
-- '#' will be replaced by the stage number
|
||||
-- '{power}' will be replaced by the power PNG
|
||||
tiles.pas = {
|
||||
-- up, down, right, left, back, front
|
||||
"techage_appl_grinder.png^techage_frame_ta#_top.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_outp.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_inp.png",
|
||||
"techage_filling_ta#.png^techage_appl_grinder2.png^techage_frame_ta#.png",
|
||||
"techage_filling_ta#.png^techage_appl_grinder2.png^techage_frame_ta#.png",
|
||||
}
|
||||
tiles.act = {
|
||||
-- up, down, right, left, back, front
|
||||
{
|
||||
name = "techage_appl_grinder4.png^techage_frame4_ta#_top.png",
|
||||
backface_culling = false,
|
||||
animation = {
|
||||
type = "vertical_frames",
|
||||
aspect_w = 32,
|
||||
aspect_h = 32,
|
||||
length = 1.0,
|
||||
},
|
||||
},
|
||||
"techage_filling_ta#.png^techage_frame_ta#.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_outp.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_inp.png",
|
||||
"techage_filling_ta#.png^techage_appl_grinder2.png^techage_frame_ta#.png",
|
||||
"techage_filling_ta#.png^techage_appl_grinder2.png^techage_frame_ta#.png",
|
||||
}
|
||||
|
||||
local tubing = {
|
||||
on_pull_item = function(pos, in_dir, num)
|
||||
local meta = minetest.get_meta(pos)
|
||||
if meta:get_int("pull_dir") == in_dir then
|
||||
local inv = M(pos):get_inventory()
|
||||
return techage.get_items(pos, inv, "dst", num)
|
||||
end
|
||||
end,
|
||||
on_push_item = function(pos, in_dir, stack)
|
||||
local meta = minetest.get_meta(pos)
|
||||
if meta:get_int("push_dir") == in_dir or in_dir == 5 then
|
||||
local inv = M(pos):get_inventory()
|
||||
--CRD(pos).State:start_if_standby(pos) -- would need power!
|
||||
return techage.put_items(inv, "src", stack)
|
||||
end
|
||||
end,
|
||||
on_unpull_item = function(pos, in_dir, stack)
|
||||
local meta = minetest.get_meta(pos)
|
||||
if meta:get_int("pull_dir") == in_dir then
|
||||
local inv = M(pos):get_inventory()
|
||||
return techage.put_items(inv, "dst", stack)
|
||||
end
|
||||
end,
|
||||
on_recv_message = function(pos, src, topic, payload)
|
||||
return CRD(pos).State:on_receive_message(pos, topic, payload)
|
||||
end,
|
||||
on_beduino_receive_cmnd = function(pos, src, topic, payload)
|
||||
return CRD(pos).State:on_beduino_receive_cmnd(pos, topic, payload)
|
||||
end,
|
||||
on_beduino_request_data = function(pos, src, topic, payload)
|
||||
return CRD(pos).State:on_beduino_request_data(pos, topic, payload)
|
||||
end,
|
||||
on_node_load = function(pos)
|
||||
CRD(pos).State:on_node_load(pos)
|
||||
end,
|
||||
}
|
||||
|
||||
local node_name_ta2, node_name_ta3, node_name_ta4 =
|
||||
techage.register_consumer("grinder", S("Grinder"), tiles, {
|
||||
drawtype = "nodebox",
|
||||
paramtype = "light",
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{-8/16, -8/16, -8/16, 8/16, 8/16, -6/16},
|
||||
{-8/16, -8/16, 6/16, 8/16, 8/16, 8/16},
|
||||
{-8/16, -8/16, -8/16, -6/16, 8/16, 8/16},
|
||||
{ 6/16, -8/16, -8/16, 8/16, 8/16, 8/16},
|
||||
{-6/16, -8/16, -6/16, 6/16, 6/16, 6/16},
|
||||
},
|
||||
},
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
fixed = {-8/16, -8/16, -8/16, 8/16, 8/16, 8/16},
|
||||
},
|
||||
cycle_time = CYCLE_TIME,
|
||||
standby_ticks = STANDBY_TICKS,
|
||||
formspec = formspec,
|
||||
tubing = tubing,
|
||||
after_place_node = function(pos, placer)
|
||||
local inv = M(pos):get_inventory()
|
||||
inv:set_size('src', 9)
|
||||
inv:set_size('dst', 9)
|
||||
end,
|
||||
can_dig = can_dig,
|
||||
node_timer = keep_running,
|
||||
on_receive_fields = on_receive_fields,
|
||||
allow_metadata_inventory_put = allow_metadata_inventory_put,
|
||||
allow_metadata_inventory_move = allow_metadata_inventory_move,
|
||||
allow_metadata_inventory_take = allow_metadata_inventory_take,
|
||||
groups = {choppy=2, cracky=2, crumbly=2},
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
num_items = {0,1,2,4},
|
||||
power_consumption = {0,4,6,9},
|
||||
tube_sides = {L=1, R=1, U=1},
|
||||
})
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- TA1 Mill (watermill)
|
||||
-------------------------------------------------------------------------------
|
||||
local formspecStr = "size[8,8]"..
|
||||
default.gui_bg..
|
||||
default.gui_bg_img..
|
||||
default.gui_slots..
|
||||
"list[context;src;1,1;1,1;]"..
|
||||
"item_image[1,1;1,1;farming:wheat]"..
|
||||
"image[1,1;1,1;techage_form_mask.png]"..
|
||||
"image[3.5,1;1,1;techage_form_arrow.png]"..
|
||||
"list[context;dst;6,1;1,1;]"..
|
||||
"item_image[6,1;1,1;farming:flour]"..
|
||||
"image[6,1;1,1;techage_form_mask.png]"..
|
||||
"list[current_player;main;0,4;8,4;]"..
|
||||
"listring[context;dst]"..
|
||||
"listring[current_player;main]"..
|
||||
"listring[context;src]"..
|
||||
"listring[current_player;main]"..
|
||||
default.get_hotbar_bg(0, 4)
|
||||
|
||||
local function node_timer(pos, elapsed)
|
||||
if techage.ta1_mill_has_power(pos, 2) then
|
||||
local inv = M(pos):get_inventory()
|
||||
local stack = inv:get_stack("src", 1)
|
||||
if not stack:is_empty() then
|
||||
local name = stack:get_name()
|
||||
if RecipesTa1[name] then
|
||||
local recipe = RecipesTa1[name]
|
||||
src_to_dst(stack, 1, name, 1, recipe.inp_num, inv, recipe.output)
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
minetest.register_node("techage:ta1_mill_base", {
|
||||
description = S("TA1 Mill Base"),
|
||||
tiles = {
|
||||
"techage_mill_base.png",
|
||||
"default_stone_brick.png",
|
||||
},
|
||||
after_place_node = function(pos, placer)
|
||||
M(pos):set_string("formspec", formspecStr)
|
||||
local inv = M(pos):get_inventory()
|
||||
inv:set_size('src', 1)
|
||||
inv:set_size('dst', 1)
|
||||
minetest.get_node_timer(pos):start(4)
|
||||
end,
|
||||
can_dig = can_dig,
|
||||
on_timer = node_timer,
|
||||
allow_metadata_inventory_put = allow_metadata_inventory_take,
|
||||
allow_metadata_inventory_move = allow_metadata_inventory_move,
|
||||
allow_metadata_inventory_take = allow_metadata_inventory_take,
|
||||
is_ground_content = false,
|
||||
groups = {cracky = 2, crumbly = 2, choppy = 2},
|
||||
})
|
||||
|
||||
techage.register_node({"techage:ta1_mill_base"}, {
|
||||
on_node_load = function(pos, node)
|
||||
minetest.get_node_timer(pos):start(4)
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "techage:ta1_mill_base",
|
||||
recipe = {
|
||||
{"default:stonebrick", "", "default:stonebrick"},
|
||||
{"", "techage:iron_ingot", ""},
|
||||
{"default:stonebrick", "", "default:stonebrick"},
|
||||
},
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = node_name_ta2,
|
||||
recipe = {
|
||||
{"group:wood", "default:mese_crystal", "group:wood"},
|
||||
{"techage:tubeS", "techage:hammer_steel", "techage:tubeS"},
|
||||
{"group:wood", "techage:iron_ingot", "group:wood"},
|
||||
},
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = node_name_ta3,
|
||||
recipe = {
|
||||
{"", "default:mese_crystal", ""},
|
||||
{"", node_name_ta2, ""},
|
||||
{"", "techage:vacuum_tube", ""},
|
||||
},
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = node_name_ta4,
|
||||
recipe = {
|
||||
{"", "default:mese_crystal", ""},
|
||||
{"", node_name_ta3, ""},
|
||||
{"", "techage:ta4_wlanchip", ""},
|
||||
},
|
||||
})
|
||||
|
||||
techage.recipes.register_craft_type("grinding", {
|
||||
description = S("Grinding"),
|
||||
icon = 'techage_appl_grinder.png',
|
||||
width = 2,
|
||||
height = 2,
|
||||
})
|
||||
techage.recipes.register_craft_type("milling", {
|
||||
description = S("Milling"),
|
||||
icon = 'techage_mill_inv.png',
|
||||
width = 2,
|
||||
height = 2,
|
||||
})
|
||||
|
||||
function techage.add_grinder_recipe(recipe, ta1_permitted)
|
||||
local name, num = unpack(string.split(recipe.input, " ", false, 1))
|
||||
if minetest.registered_items[name] then
|
||||
if ta1_permitted then
|
||||
RecipesTa1[name] = {input = name,inp_num = tonumber(num) or 1, output = recipe.output}
|
||||
|
||||
recipe.items = {recipe.input}
|
||||
recipe.type = "milling"
|
||||
techage.recipes.register_craft(table.copy(recipe))
|
||||
end
|
||||
|
||||
Recipes[name] = {input = name,inp_num = tonumber(num) or 1, output = recipe.output}
|
||||
|
||||
recipe.items = {recipe.input}
|
||||
recipe.type = "grinding"
|
||||
techage.recipes.register_craft(recipe)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
techage.add_grinder_recipe({input="default:cobble", output="default:gravel"})
|
||||
techage.add_grinder_recipe({input="default:desert_cobble", output="default:gravel"})
|
||||
techage.add_grinder_recipe({input="default:mossycobble", output="default:gravel"})
|
||||
techage.add_grinder_recipe({input="default:gravel", output="default:sand"})
|
||||
techage.add_grinder_recipe({input="techage:sieved_gravel", output="default:sand"})
|
||||
techage.add_grinder_recipe({input="default:coral_skeleton", output="default:silver_sand"})
|
||||
|
||||
if minetest.global_exists("skytest") then
|
||||
techage.add_grinder_recipe({input="default:desert_sand", output="skytest:dust"})
|
||||
techage.add_grinder_recipe({input="default:silver_sand", output="skytest:dust"})
|
||||
techage.add_grinder_recipe({input="default:sand", output="skytest:dust"})
|
||||
else
|
||||
techage.add_grinder_recipe({input="default:desert_sand", output="default:clay"})
|
||||
techage.add_grinder_recipe({input="default:silver_sand", output="default:clay"})
|
||||
techage.add_grinder_recipe({input="default:sand", output="default:clay"})
|
||||
end
|
||||
|
||||
techage.add_grinder_recipe({input="default:sandstone", output="default:sand 4"})
|
||||
techage.add_grinder_recipe({input="default:desert_sandstone", output="default:desert_sand 4"})
|
||||
techage.add_grinder_recipe({input="default:silver_sandstone", output="default:silver_sand 4"})
|
||||
|
||||
techage.add_grinder_recipe({input="default:tree", output="default:leaves 8"})
|
||||
techage.add_grinder_recipe({input="default:jungletree", output="default:jungleleaves 8"})
|
||||
techage.add_grinder_recipe({input="default:pine_tree", output="default:pine_needles 8"})
|
||||
techage.add_grinder_recipe({input="default:acacia_tree", output="default:acacia_leaves 8"})
|
||||
techage.add_grinder_recipe({input="default:aspen_tree", output="default:aspen_leaves 8"})
|
||||
|
||||
if minetest.global_exists("farming") then
|
||||
techage.add_grinder_recipe({input="farming:wheat 3", output="farming:flour"}, true)
|
||||
techage.add_grinder_recipe({input="farming:seed_wheat 6", output="farming:flour"}, true)
|
||||
techage.add_grinder_recipe({input="farming:barley 3", output="farming:flour"}, true)
|
||||
techage.add_grinder_recipe({input="farming:seed_barley 6", output="farming:flour"}, true)
|
||||
techage.add_grinder_recipe({input="farming:rye 3", output="farming:flour"}, true)
|
||||
techage.add_grinder_recipe({input="farming:seed_rye 6", output="farming:flour"}, true)
|
||||
techage.add_grinder_recipe({input="farming:rice 3", output="farming:rice_flour"}, true)
|
||||
techage.add_grinder_recipe({input="farming:seed_rice 6", output="farming:rice_flour"}, true)
|
||||
techage.add_grinder_recipe({input="farming:oat 3", output="farming:flour"}, true)
|
||||
techage.add_grinder_recipe({input="farming:seed_oat 6", output="farming:flour"}, true)
|
||||
techage.add_grinder_recipe({input="farming:seed_cotton 3", output="basic_materials:oil_extract"}, true)
|
||||
end
|
106
techage/basic_machines/itemsource.lua
Normal file
106
techage/basic_machines/itemsource.lua
Normal file
@ -0,0 +1,106 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019-2020 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
Item Source Block
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local M = minetest.get_meta
|
||||
local S = techage.S
|
||||
|
||||
local CYCLE_TIME = 30
|
||||
|
||||
local function formspec()
|
||||
return "size[8,7.2]"..
|
||||
default.gui_bg..
|
||||
default.gui_bg_img..
|
||||
default.gui_slots..
|
||||
"list[context;main;3.5,0.8;1,1;]"..
|
||||
"list[current_player;main;0,3.5;8,4;]"..
|
||||
"listring[context;main]"..
|
||||
"listring[current_player;main]"
|
||||
end
|
||||
|
||||
local function allow_metadata_inventory_put(pos, listname, index, stack, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return 0
|
||||
end
|
||||
|
||||
return stack:get_count()
|
||||
end
|
||||
|
||||
local function allow_metadata_inventory_take(pos, listname, index, stack, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return 0
|
||||
end
|
||||
|
||||
return stack:get_count()
|
||||
end
|
||||
|
||||
minetest.register_node("techage:itemsource", {
|
||||
description = "Techage Item Source",
|
||||
tiles = {
|
||||
-- up, down, right, left, back, front
|
||||
"techage_filling_ta3.png^techage_frame_ta3_top.png^techage_appl_arrow.png",
|
||||
"techage_filling_ta3.png^techage_frame_ta3.png",
|
||||
"techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_outp.png",
|
||||
"techage_filling_ta3.png^techage_frame_ta3.png",
|
||||
"techage_filling_ta3.png^techage_appl_nodedetector.png^techage_frame_ta3.png",
|
||||
"techage_filling_ta3.png^techage_appl_nodedetector.png^techage_frame_ta3.png",
|
||||
},
|
||||
|
||||
after_place_node = function(pos, placer)
|
||||
local meta = M(pos)
|
||||
local node = minetest.get_node(pos)
|
||||
meta:set_int("push_dir", techage.side_to_outdir("R", node.param2))
|
||||
local inv = meta:get_inventory()
|
||||
inv:set_size('main', 1)
|
||||
minetest.get_node_timer(pos):start(CYCLE_TIME)
|
||||
meta:set_string("infotext", "Techage Item Source")
|
||||
meta:set_string("formspec", formspec())
|
||||
end,
|
||||
|
||||
on_timer = function(pos, elapsed)
|
||||
local meta = M(pos)
|
||||
local inv = meta:get_inventory()
|
||||
local stack = inv:get_stack('main', 1)
|
||||
if stack:get_count() > 0 then
|
||||
local push_dir = meta:get_int("push_dir")
|
||||
local leftover = techage.push_items(pos, push_dir, stack)
|
||||
local pushed
|
||||
if not leftover then
|
||||
pushed = 0
|
||||
elseif leftover ~= true then
|
||||
pushed = stack:get_count() - leftover:get_count()
|
||||
else -- leftover == true
|
||||
pushed = stack:get_count()
|
||||
end
|
||||
meta:set_int("counter", pushed)
|
||||
meta:set_string("infotext", "Techage Item Source: "..pushed)
|
||||
end
|
||||
return true
|
||||
end,
|
||||
|
||||
allow_metadata_inventory_put = allow_metadata_inventory_put,
|
||||
allow_metadata_inventory_take = allow_metadata_inventory_take,
|
||||
|
||||
paramtype2 = "facedir", -- important!
|
||||
on_rotate = screwdriver.disallow, -- important!
|
||||
is_ground_content = false,
|
||||
drop = "",
|
||||
groups = {crumbly = 3, cracky = 3, snappy = 3},
|
||||
sounds = default.node_sound_glass_defaults(),
|
||||
})
|
||||
|
||||
techage.register_node({"techage:itemsource"}, {
|
||||
on_node_load = function(pos)
|
||||
minetest.get_node_timer(pos):start(CYCLE_TIME)
|
||||
end,
|
||||
})
|
228
techage/basic_machines/liquidsampler.lua
Normal file
228
techage/basic_machines/liquidsampler.lua
Normal file
@ -0,0 +1,228 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019-2022 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
TA2/TA3 Bucket based Liquid Sampler
|
||||
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local M = minetest.get_meta
|
||||
-- Consumer Related Data
|
||||
local CRD = function(pos) return (minetest.registered_nodes[techage.get_node_lvm(pos).name] or {}).consumer end
|
||||
|
||||
local S = techage.S
|
||||
|
||||
local STANDBY_TICKS = 2
|
||||
local COUNTDOWN_TICKS = 3
|
||||
local CYCLE_TIME = 8
|
||||
|
||||
local function formspec(self, pos, nvm)
|
||||
return "size[9,8.5]"..
|
||||
default.gui_bg..
|
||||
default.gui_bg_img..
|
||||
default.gui_slots..
|
||||
"list[context;src;0,0;1,4;]"..
|
||||
"image[0,0;1,1;bucket.png]"..
|
||||
"image[1,0;1,1;"..techage.get_power_image(pos, nvm).."]"..
|
||||
"image[1,1.5;1,1;techage_form_arrow.png]"..
|
||||
"image_button[1,3;1,1;".. self:get_state_button_image(nvm) ..";state_button;]"..
|
||||
"tooltip[1,3;1,1;"..self:get_state_tooltip(nvm).."]"..
|
||||
"list[context;dst;2,0;7,4;]"..
|
||||
"list[current_player;main;0.5,4.5;8,4;]"..
|
||||
"listring[current_player;main]"..
|
||||
"listring[context;src]" ..
|
||||
"listring[current_player;main]"..
|
||||
"listring[context;dst]" ..
|
||||
"listring[current_player;main]"
|
||||
end
|
||||
|
||||
local function allow_metadata_inventory_put(pos, listname, index, stack, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return 0
|
||||
end
|
||||
if listname == "src" then
|
||||
CRD(pos).State:start_if_standby(pos)
|
||||
end
|
||||
return stack:get_count()
|
||||
end
|
||||
|
||||
local function allow_metadata_inventory_move(pos, from_list, from_index, to_list, to_index, count, player)
|
||||
local inv = M(pos):get_inventory()
|
||||
local stack = inv:get_stack(from_list, from_index)
|
||||
return allow_metadata_inventory_put(pos, to_list, to_index, stack, player)
|
||||
end
|
||||
|
||||
local function allow_metadata_inventory_take(pos, listname, index, stack, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return 0
|
||||
end
|
||||
return stack:get_count()
|
||||
end
|
||||
|
||||
local function is_water(pos)
|
||||
local node = minetest.get_node(pos)
|
||||
local liquiddef = bucket.liquids[node.name]
|
||||
if liquiddef ~= nil and liquiddef.itemname ~= nil and node.name == liquiddef.source then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
local function can_start(pos, nvm, state)
|
||||
local water_pos = minetest.string_to_pos(M(pos):get_string("water_pos"))
|
||||
if not is_water(water_pos) then
|
||||
return S("no usable water")
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local function sample_liquid(pos, crd, nvm, inv)
|
||||
if inv:room_for_item("dst", {name = "bucket:bucket_water"}) and
|
||||
inv:contains_item("src", {name = "bucket:bucket_empty"}) then
|
||||
inv:remove_item("src", {name = "bucket:bucket_empty"})
|
||||
inv:add_item("dst", {name = "bucket:bucket_water"})
|
||||
crd.State:keep_running(pos, nvm, COUNTDOWN_TICKS)
|
||||
else
|
||||
crd.State:idle(pos, nvm)
|
||||
end
|
||||
end
|
||||
|
||||
local function keep_running(pos, elapsed)
|
||||
--if tubelib.data_not_corrupted(pos) then
|
||||
local nvm = techage.get_nvm(pos)
|
||||
local crd = CRD(pos)
|
||||
local inv = M(pos):get_inventory()
|
||||
sample_liquid(pos, crd, nvm, inv)
|
||||
end
|
||||
|
||||
local function on_receive_fields(pos, formname, fields, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return
|
||||
end
|
||||
local nvm = techage.get_nvm(pos)
|
||||
CRD(pos).State:state_button_event(pos, nvm, fields)
|
||||
end
|
||||
|
||||
local function can_dig(pos, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return false
|
||||
end
|
||||
local inv = M(pos):get_inventory()
|
||||
return inv:is_empty("dst") and inv:is_empty("src")
|
||||
end
|
||||
|
||||
local tiles = {}
|
||||
-- '#' will be replaced by the stage number
|
||||
-- '{power}' will be replaced by the power PNG
|
||||
tiles.pas = {
|
||||
-- up, down, right, left, back, front
|
||||
"techage_filling_ta#.png^{power}^techage_frame_ta#_top.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_outp.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_inp.png",
|
||||
"techage_filling_ta#.png^techage_liquidsampler.png^techage_frame_ta#.png",
|
||||
"techage_filling_ta#.png^techage_appl_liquidsampler.png^techage_frame_ta#.png",
|
||||
}
|
||||
tiles.act = {
|
||||
-- up, down, right, left, back, front
|
||||
"techage_filling_ta#.png^{power}^techage_frame_ta#_top.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_outp.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_inp.png",
|
||||
{
|
||||
name = "techage_filling4_ta#.png^techage_liquidsampler4.png^techage_frame4_ta#.png",
|
||||
backface_culling = false,
|
||||
animation = {
|
||||
type = "vertical_frames",
|
||||
aspect_w = 32,
|
||||
aspect_h = 32,
|
||||
length = 1.0,
|
||||
},
|
||||
},
|
||||
"techage_filling_ta#.png^techage_appl_liquidsampler.png^techage_frame_ta#.png",
|
||||
}
|
||||
|
||||
local tubing = {
|
||||
on_pull_item = function(pos, in_dir, num)
|
||||
local meta = minetest.get_meta(pos)
|
||||
if meta:get_int("pull_dir") == in_dir then
|
||||
local inv = M(pos):get_inventory()
|
||||
return techage.get_items(pos, inv, "dst", num)
|
||||
end
|
||||
end,
|
||||
on_push_item = function(pos, in_dir, stack)
|
||||
local meta = minetest.get_meta(pos)
|
||||
if meta:get_int("push_dir") == in_dir then
|
||||
local inv = M(pos):get_inventory()
|
||||
return techage.put_items(inv, "src", stack)
|
||||
end
|
||||
end,
|
||||
on_unpull_item = function(pos, in_dir, stack)
|
||||
local meta = minetest.get_meta(pos)
|
||||
if meta:get_int("pull_dir") == in_dir then
|
||||
local inv = M(pos):get_inventory()
|
||||
return techage.put_items(inv, "dst", stack)
|
||||
end
|
||||
end,
|
||||
on_recv_message = function(pos, src, topic, payload)
|
||||
return CRD(pos).State:on_receive_message(pos, topic, payload)
|
||||
end,
|
||||
on_beduino_receive_cmnd = function(pos, src, topic, payload)
|
||||
return CRD(pos).State:on_beduino_receive_cmnd(pos, topic, payload)
|
||||
end,
|
||||
on_beduino_request_data = function(pos, src, topic, payload)
|
||||
return CRD(pos).State:on_beduino_request_data(pos, topic, payload)
|
||||
end,
|
||||
}
|
||||
|
||||
local node_name_ta2, node_name_ta3, _ =
|
||||
techage.register_consumer("liquidsampler", S("Liquid Sampler"), tiles, {
|
||||
cycle_time = CYCLE_TIME,
|
||||
standby_ticks = STANDBY_TICKS,
|
||||
formspec = formspec,
|
||||
tubing = tubing,
|
||||
can_start = can_start,
|
||||
after_place_node = function(pos, placer)
|
||||
local inv = M(pos):get_inventory()
|
||||
inv:set_size("src", 4)
|
||||
inv:set_size("dst", 28)
|
||||
local water_pos = techage.get_pos(pos, "B")
|
||||
M(pos):set_string("water_pos", minetest.pos_to_string(water_pos))
|
||||
end,
|
||||
can_dig = can_dig,
|
||||
node_timer = keep_running,
|
||||
on_receive_fields = on_receive_fields,
|
||||
allow_metadata_inventory_put = allow_metadata_inventory_put,
|
||||
allow_metadata_inventory_move = allow_metadata_inventory_move,
|
||||
allow_metadata_inventory_take = allow_metadata_inventory_take,
|
||||
groups = {choppy=2, cracky=2, crumbly=2},
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
num_items = {0,1,2,4},
|
||||
power_consumption = {0,3,5,8},
|
||||
power_sides = {U=1},
|
||||
},
|
||||
{false, true, true, false}) -- TA2/A3
|
||||
|
||||
minetest.register_craft({
|
||||
output = node_name_ta2,
|
||||
recipe = {
|
||||
{"group:wood", "default:mese_crystal", "group:wood"},
|
||||
{"techage:tubeS", "bucket:bucket_empty", "techage:tubeS"},
|
||||
{"group:wood", "techage:iron_ingot", "group:wood"},
|
||||
},
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = node_name_ta3,
|
||||
recipe = {
|
||||
{"", "default:mese_crystal", ""},
|
||||
{"", node_name_ta2, ""},
|
||||
{"", "techage:vacuum_tube", ""},
|
||||
},
|
||||
})
|
131
techage/basic_machines/mods_support.lua
Normal file
131
techage/basic_machines/mods_support.lua
Normal file
@ -0,0 +1,131 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019-2020 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
Tube support for digtron and protector chests
|
||||
|
||||
]]--
|
||||
|
||||
|
||||
-- for lazy programmers
|
||||
local M = minetest.get_meta
|
||||
|
||||
local CacheForFuelNodeNames = {}
|
||||
|
||||
local function is_fuel(stack)
|
||||
local name = stack:get_name()
|
||||
if CacheForFuelNodeNames[name] then
|
||||
return true
|
||||
end
|
||||
if minetest.get_craft_result({method="fuel", width=1, items={stack}}).time ~= 0 then
|
||||
CacheForFuelNodeNames[name] = true
|
||||
end
|
||||
return CacheForFuelNodeNames[name]
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
-- digtron
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
techage.register_node({"digtron:inventory"}, {
|
||||
on_inv_request = function(pos, in_dir, access_type)
|
||||
local meta = minetest.get_meta(pos)
|
||||
return meta:get_inventory(), "main"
|
||||
end,
|
||||
on_pull_item = function(pos, in_dir, num)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
return techage.get_items(pos, inv, "main", num)
|
||||
end,
|
||||
on_push_item = function(pos, in_dir, stack)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
return techage.put_items(inv, "main", stack)
|
||||
end,
|
||||
on_unpull_item = function(pos, in_dir, stack)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
return techage.put_items(inv, "main", stack)
|
||||
end,
|
||||
})
|
||||
|
||||
techage.register_node({"digtron:fuelstore"}, {
|
||||
on_inv_request = function(pos, in_dir, access_type)
|
||||
local meta = minetest.get_meta(pos)
|
||||
return meta:get_inventory(), "fuel"
|
||||
end,
|
||||
on_pull_item = function(pos, in_dir, num)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
return techage.get_items(pos, inv, "fuel", num)
|
||||
end,
|
||||
on_push_item = function(pos, in_dir, stack)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
return techage.put_items(inv, "fuel", stack)
|
||||
end,
|
||||
on_unpull_item = function(pos, in_dir, stack)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
return techage.put_items(inv, "fuel", stack)
|
||||
end,
|
||||
})
|
||||
|
||||
techage.register_node({"digtron:combined_storage"}, {
|
||||
on_inv_request = function(pos, in_dir, access_type)
|
||||
local meta = minetest.get_meta(pos)
|
||||
return meta:get_inventory(), "main"
|
||||
end,
|
||||
on_pull_item = function(pos, in_dir, num)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
return techage.get_items(pos, inv, "main", num)
|
||||
end,
|
||||
on_push_item = function(pos, side, stack)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
minetest.get_node_timer(pos):start(1.0)
|
||||
if is_fuel(stack) then
|
||||
return techage.put_items(inv, "fuel", stack)
|
||||
else
|
||||
return techage.put_items(inv, "main", stack)
|
||||
end
|
||||
end,
|
||||
on_unpull_item = function(pos, side, stack)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
return techage.put_items(inv, "main", stack)
|
||||
end,
|
||||
})
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
-- protector
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
techage.register_node({"protector:chest"}, {
|
||||
on_inv_request = function(pos, in_dir, access_type)
|
||||
local meta = minetest.get_meta(pos)
|
||||
return meta:get_inventory(), "main"
|
||||
end,
|
||||
on_pull_item = function(pos, in_dir, num)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
return techage.get_items(pos, inv, "main", num)
|
||||
end,
|
||||
on_push_item = function(pos, in_dir, stack)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
return techage.put_items(inv, "main", stack)
|
||||
end,
|
||||
on_unpull_item = function(pos, in_dir, stack)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
return techage.put_items(inv, "main", stack)
|
||||
end,
|
||||
})
|
438
techage/basic_machines/pusher.lua
Normal file
438
techage/basic_machines/pusher.lua
Normal file
@ -0,0 +1,438 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019-2022 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
TA2/TA3/TA4 Pusher
|
||||
Nodes for push/pull operation of StackItems from chests or other
|
||||
inventory/server nodes to tubes or other inventory/server nodes.
|
||||
|
||||
+--------+
|
||||
/ /|
|
||||
+--------+ |
|
||||
IN (L) -->| |X--> OUT (R)
|
||||
| PUSHER | +
|
||||
| |/
|
||||
+--------+
|
||||
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local M = minetest.get_meta
|
||||
local S = techage.S
|
||||
|
||||
-- Consumer Related Data
|
||||
local CRD = function(pos) return (minetest.registered_nodes[techage.get_node_lvm(pos).name] or {}).consumer end
|
||||
local Tube = techage.Tube
|
||||
|
||||
local STANDBY_TICKS = 2
|
||||
local COUNTDOWN_TICKS = 4
|
||||
local CYCLE_TIME = 2
|
||||
|
||||
local WRENCH_MENU = {
|
||||
{
|
||||
type = "number",
|
||||
name = "limit",
|
||||
label = S("Number of items"),
|
||||
tooltip = S("Number of items that are allowed to be pushed"),
|
||||
default = "0",
|
||||
},
|
||||
}
|
||||
|
||||
local function ta4_formspec(self, pos, nvm)
|
||||
if CRD(pos).stage == 4 then -- TA4 node?
|
||||
return "size[8,7.2]"..
|
||||
default.gui_bg..
|
||||
default.gui_bg_img..
|
||||
default.gui_slots..
|
||||
"box[0,-0.1;7.8,0.5;#c6e8ff]"..
|
||||
"label[3,-0.1;"..minetest.colorize("#000000", S("Pusher")).."]"..
|
||||
techage.question_mark_help(7.5, S("Optionally configure\nthe pusher with one item"))..
|
||||
techage.wrench_image(7.4, -0.05) ..
|
||||
"list[context;main;3.5,0.8;1,1;]"..
|
||||
"image_button[3.5,2;1,1;".. self:get_state_button_image(nvm) ..";state_button;]"..
|
||||
"tooltip[3.5,2;1,1;"..self:get_state_tooltip(nvm).."]"..
|
||||
"list[current_player;main;0,3.5;8,4;]"..
|
||||
"listring[context;main]"..
|
||||
"listring[current_player;main]"
|
||||
end
|
||||
end
|
||||
|
||||
local function allow_metadata_inventory_put(pos, listname, index, stack, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return 0
|
||||
end
|
||||
|
||||
local nvm = techage.get_nvm(pos)
|
||||
if CRD(pos).State:get_state(nvm) ~= techage.STOPPED then
|
||||
return 0
|
||||
end
|
||||
|
||||
local inv = M(pos):get_inventory()
|
||||
local list = inv:get_list(listname)
|
||||
if list[index]:get_count() == 0 then
|
||||
stack:set_count(1)
|
||||
inv:set_stack(listname, index, stack)
|
||||
return 0
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
local function allow_metadata_inventory_take(pos, listname, index, stack, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return 0
|
||||
end
|
||||
|
||||
local nvm = techage.get_nvm(pos)
|
||||
if CRD(pos).State:get_state(nvm) ~= techage.STOPPED then
|
||||
return 0
|
||||
end
|
||||
|
||||
local inv = M(pos):get_inventory()
|
||||
inv:set_stack(listname, index, nil)
|
||||
return 0
|
||||
end
|
||||
|
||||
local function set_limit(pos, nvm, val)
|
||||
val = tonumber(val) or 0
|
||||
if val > 0 then
|
||||
nvm.limit = val
|
||||
nvm.num_items = 0
|
||||
M(pos):set_int("limit", val)
|
||||
else
|
||||
nvm.limit = nil
|
||||
nvm.num_items = nil
|
||||
M(pos):set_string("limit", "")
|
||||
end
|
||||
end
|
||||
|
||||
-- Function returns the number of pushed items
|
||||
local function push(pos, crd, meta, nvm, pull_dir, push_dir, num)
|
||||
local items = techage.pull_items(pos, pull_dir, num, nvm.item_name)
|
||||
if items ~= nil then
|
||||
local taken = items:get_count()
|
||||
local leftover = techage.push_items(pos, push_dir, items)
|
||||
if not leftover then
|
||||
-- place item back
|
||||
techage.unpull_items(pos, pull_dir, items)
|
||||
crd.State:blocked(pos, nvm)
|
||||
return 0
|
||||
elseif leftover ~= true then
|
||||
-- place item back
|
||||
taken = taken - leftover:get_count()
|
||||
techage.unpull_items(pos, pull_dir, leftover)
|
||||
crd.State:blocked(pos, nvm)
|
||||
return taken
|
||||
end
|
||||
return taken
|
||||
end
|
||||
crd.State:idle(pos, nvm)
|
||||
return 0
|
||||
end
|
||||
|
||||
local function pushing(pos, crd, meta, nvm)
|
||||
local pull_dir = meta:get_int("pull_dir")
|
||||
local push_dir = meta:get_int("push_dir")
|
||||
|
||||
if not nvm.limit then
|
||||
local num = nvm.item_count or nvm.num_items or crd.num_items
|
||||
num = push(pos, crd, meta, nvm, pull_dir, push_dir, num)
|
||||
if num > 0 then
|
||||
if nvm.item_count then
|
||||
nvm.item_count = nvm.item_count - num
|
||||
if nvm.item_count <= 0 then
|
||||
crd.State:stop(pos, nvm)
|
||||
nvm.item_count = nil
|
||||
end
|
||||
end
|
||||
crd.State:keep_running(pos, nvm, COUNTDOWN_TICKS)
|
||||
end
|
||||
elseif nvm.num_items < nvm.limit then
|
||||
local num = math.min(crd.num_items, nvm.limit - nvm.num_items)
|
||||
num = push(pos, crd, meta, nvm, pull_dir, push_dir, num)
|
||||
if num > 0 then
|
||||
nvm.num_items = nvm.num_items + num
|
||||
if nvm.num_items >= nvm.limit then
|
||||
crd.State:stop(pos, nvm)
|
||||
else
|
||||
crd.State:keep_running(pos, nvm, COUNTDOWN_TICKS)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function keep_running(pos, elapsed)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
local crd = CRD(pos)
|
||||
pushing(pos, crd, M(pos), nvm)
|
||||
crd.State:is_active(nvm)
|
||||
end
|
||||
|
||||
local function on_rightclick(pos, node, clicker)
|
||||
if CRD(pos).stage ~= 4 then -- Not TA4 node?
|
||||
local nvm = techage.get_nvm(pos)
|
||||
if not minetest.is_protected(pos, clicker:get_player_name()) then
|
||||
if CRD(pos).State:get_state(nvm) == techage.STOPPED then
|
||||
CRD(pos).State:start(pos, nvm)
|
||||
else
|
||||
CRD(pos).State:stop(pos, nvm)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function on_receive_fields(pos, formname, fields, player)
|
||||
if CRD(pos).stage == 4 then -- TA4 node?
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return
|
||||
end
|
||||
local nvm = techage.get_nvm(pos)
|
||||
CRD(pos).State:state_button_event(pos, nvm, fields)
|
||||
M(pos):set_string("formspec", ta4_formspec(CRD(pos).State, pos, nvm))
|
||||
end
|
||||
end
|
||||
|
||||
local function tubelib2_on_update2(pos, outdir, tlib2, node)
|
||||
local pull_dir = M(pos):get_int("pull_dir")
|
||||
local push_dir = M(pos):get_int("push_dir")
|
||||
local is_ta4_tube = true
|
||||
|
||||
for i, pos, node in Tube:get_tube_line(pos, pull_dir) do
|
||||
is_ta4_tube = is_ta4_tube and techage.TA4tubes[node.name]
|
||||
end
|
||||
for i, pos, node in Tube:get_tube_line(pos, push_dir) do
|
||||
is_ta4_tube = is_ta4_tube and techage.TA4tubes[node.name]
|
||||
end
|
||||
|
||||
local nvm = techage.get_nvm(pos)
|
||||
local crd = CRD(pos)
|
||||
if CRD(pos).stage == 4 and not is_ta4_tube then
|
||||
nvm.num_items = crd.num_items / 2
|
||||
else
|
||||
nvm.num_items = crd.num_items
|
||||
end
|
||||
end
|
||||
|
||||
local function can_start(pos, nvm, state)
|
||||
if CRD(pos).stage == 4 then -- TA4 node?
|
||||
local inv = M(pos):get_inventory()
|
||||
local name = inv:get_stack("main", 1):get_name()
|
||||
if name ~= "" then
|
||||
nvm.item_name = name
|
||||
else
|
||||
nvm.item_name = nil
|
||||
end
|
||||
else
|
||||
nvm.item_name = nil
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local function ta_after_formspec(pos, fields, playername)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
set_limit(pos, nvm, fields.limit)
|
||||
end
|
||||
|
||||
local function on_state_change(pos, old_state, new_state)
|
||||
if old_state == techage.STOPPED and new_state == techage.RUNNING then
|
||||
local nvm = techage.get_nvm(pos)
|
||||
set_limit(pos, nvm, M(pos):get_int("limit"))
|
||||
end
|
||||
end
|
||||
|
||||
local function config_item(pos, payload)
|
||||
if type(payload) == "string" then
|
||||
if payload == "" then
|
||||
local inv = M(pos):get_inventory()
|
||||
inv:set_stack("main", 1, nil)
|
||||
return 0
|
||||
else
|
||||
local name, count = unpack(payload:split(" "))
|
||||
if name and (minetest.registered_nodes[name] or minetest.registered_items[name]
|
||||
or minetest.registered_craftitems[name]) then
|
||||
count = tonumber(count) or 1
|
||||
local inv = M(pos):get_inventory()
|
||||
inv:set_stack("main", 1, {name = name, count = 1})
|
||||
return count
|
||||
end
|
||||
end
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
local tiles = {}
|
||||
-- '#' will be replaced by the stage number
|
||||
-- '{power}' will be replaced by the power PNG
|
||||
tiles.pas = {
|
||||
"techage_filling_ta#.png^techage_frame_ta#_top.png^techage_appl_arrow.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#_bottom.png^techage_appl_arrow.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_outp.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_inp.png",
|
||||
"techage_appl_pusher.png^[transformR180]^techage_frame_ta#.png",
|
||||
"techage_appl_pusher.png^techage_frame_ta#.png",
|
||||
}
|
||||
tiles.act = {
|
||||
-- up, down, right, left, back, front
|
||||
"techage_filling_ta#.png^techage_frame_ta#_top.png^techage_appl_arrow.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#_bottom.png^techage_appl_arrow.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_outp.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_inp.png",
|
||||
{
|
||||
name = "techage_appl_pusher14.png^[transformR180]^techage_frame14_ta#.png",
|
||||
backface_culling = false,
|
||||
animation = {
|
||||
type = "vertical_frames",
|
||||
aspect_w = 32,
|
||||
aspect_h = 32,
|
||||
length = 2.0,
|
||||
},
|
||||
},
|
||||
{
|
||||
name = "techage_appl_pusher14.png^techage_frame14_ta#.png",
|
||||
backface_culling = false,
|
||||
animation = {
|
||||
type = "vertical_frames",
|
||||
aspect_w = 32,
|
||||
aspect_h = 32,
|
||||
length = 2.0,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
local tubing = {
|
||||
-- push item through the pusher in opposit direction
|
||||
on_push_item = function(pos, in_dir, stack)
|
||||
return in_dir == M(pos):get_int("pull_dir") and techage.safe_push_items(pos, in_dir, stack)
|
||||
end,
|
||||
is_pusher = true, -- is a pulling/pushing node
|
||||
|
||||
on_recv_message = function(pos, src, topic, payload)
|
||||
if topic == "pull" then -- Deprecated command, use config/limit/start instead
|
||||
local nvm = techage.get_nvm(pos)
|
||||
CRD(pos).State:stop(pos, nvm)
|
||||
nvm.item_count = math.min(config_item(pos, payload), 12)
|
||||
nvm.rmt_num = src
|
||||
CRD(pos).State:start(pos, nvm)
|
||||
return true
|
||||
elseif topic == "config" then -- Set item type
|
||||
local nvm = techage.get_nvm(pos)
|
||||
CRD(pos).State:stop(pos, nvm)
|
||||
config_item(pos, payload)
|
||||
return true
|
||||
elseif topic == "limit" then -- Set push limit
|
||||
local nvm = techage.get_nvm(pos)
|
||||
CRD(pos).State:stop(pos, nvm)
|
||||
set_limit(pos, nvm, payload)
|
||||
return true
|
||||
elseif topic == "count" then -- Get number of push items
|
||||
local nvm = techage.get_nvm(pos)
|
||||
return nvm.num_items or 0
|
||||
else
|
||||
return CRD(pos).State:on_receive_message(pos, topic, payload)
|
||||
end
|
||||
end,
|
||||
on_beduino_receive_cmnd = function(pos, src, topic, payload)
|
||||
if topic == 65 then -- Set item type
|
||||
local nvm = techage.get_nvm(pos)
|
||||
CRD(pos).State:stop(pos, nvm)
|
||||
config_item(pos, payload)
|
||||
return 0
|
||||
elseif topic == 68 or topic == 20 then -- Set push limit
|
||||
local nvm = techage.get_nvm(pos)
|
||||
CRD(pos).State:stop(pos, nvm)
|
||||
set_limit(pos, nvm, payload[1])
|
||||
return 0
|
||||
else
|
||||
local nvm = techage.get_nvm(pos)
|
||||
if nvm.limit then
|
||||
nvm.num_items = 0
|
||||
end
|
||||
return CRD(pos).State:on_beduino_receive_cmnd(pos, topic, payload)
|
||||
end
|
||||
end,
|
||||
on_beduino_request_data = function(pos, src, topic, payload)
|
||||
if topic == 150 then -- Get number of pushed items
|
||||
local nvm = techage.get_nvm(pos)
|
||||
return 0, {nvm.num_items or 0}
|
||||
else
|
||||
return CRD(pos).State:on_beduino_request_data(pos, topic, payload)
|
||||
end
|
||||
end,
|
||||
}
|
||||
|
||||
local node_name_ta2, node_name_ta3, node_name_ta4 =
|
||||
techage.register_consumer("pusher", S("Pusher"), tiles, {
|
||||
cycle_time = CYCLE_TIME,
|
||||
standby_ticks = STANDBY_TICKS,
|
||||
formspec = ta4_formspec,
|
||||
tubing = tubing,
|
||||
can_start = can_start,
|
||||
on_state_change = on_state_change,
|
||||
after_place_node = function(pos, placer)
|
||||
local meta = M(pos)
|
||||
local node = minetest.get_node(pos)
|
||||
meta:set_int("pull_dir", techage.side_to_outdir("L", node.param2))
|
||||
meta:set_int("push_dir", techage.side_to_outdir("R", node.param2))
|
||||
if CRD(pos).stage == 4 then -- TA4 node?
|
||||
local inv = M(pos):get_inventory()
|
||||
inv:set_size('main', 1)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
M(pos):set_string("formspec", ta4_formspec(CRD(pos).State, pos, nvm))
|
||||
end
|
||||
end,
|
||||
ta_rotate_node = function(pos, node, new_param2)
|
||||
Tube:after_dig_node(pos)
|
||||
minetest.swap_node(pos, {name = node.name, param2 = new_param2})
|
||||
Tube:after_place_node(pos)
|
||||
local meta = M(pos)
|
||||
meta:set_int("pull_dir", techage.side_to_outdir("L", new_param2))
|
||||
meta:set_int("push_dir", techage.side_to_outdir("R", new_param2))
|
||||
end,
|
||||
allow_metadata_inventory_put = allow_metadata_inventory_put,
|
||||
allow_metadata_inventory_take = allow_metadata_inventory_take,
|
||||
on_rightclick = on_rightclick,
|
||||
on_receive_fields = on_receive_fields,
|
||||
node_timer = keep_running,
|
||||
on_rotate = screwdriver.disallow,
|
||||
tubelib2_on_update2 = tubelib2_on_update2,
|
||||
ta4_formspec = WRENCH_MENU,
|
||||
ta_after_formspec = ta_after_formspec,
|
||||
|
||||
groups = {choppy=2, cracky=2, crumbly=2},
|
||||
is_ground_content = false,
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
num_items = {0,2,6,12},
|
||||
tube_sides = {L=1, R=1},
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = node_name_ta2.." 2",
|
||||
recipe = {
|
||||
{"group:wood", "wool:dark_green", "group:wood"},
|
||||
{"techage:tubeS", "default:mese_crystal", "techage:tubeS"},
|
||||
{"group:wood", "techage:iron_ingot", "group:wood"},
|
||||
},
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = node_name_ta3,
|
||||
recipe = {
|
||||
{"", "techage:iron_ingot", ""},
|
||||
{"", node_name_ta2, ""},
|
||||
{"", "techage:vacuum_tube", ""},
|
||||
},
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = node_name_ta4,
|
||||
recipe = {
|
||||
{"", "techage:iron_ingot", ""},
|
||||
{"", node_name_ta3, ""},
|
||||
{"", "techage:ta4_wlanchip", ""},
|
||||
},
|
||||
})
|
457
techage/basic_machines/quarry.lua
Normal file
457
techage/basic_machines/quarry.lua
Normal file
@ -0,0 +1,457 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019-2023 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
Quarry machine to dig stones and other ground blocks.
|
||||
|
||||
The Quarry digs a hole (default) 5x5 blocks large and up to 80 blocks deep.
|
||||
It starts at the given level (0 is same level as the quarry block,
|
||||
1 is one level higher and so on)) and goes down to the given depth number.
|
||||
It digs one block every 4 seconds.
|
||||
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local S2P = minetest.string_to_pos
|
||||
local M = minetest.get_meta
|
||||
-- Consumer Related Data
|
||||
local CRD = function(pos) return (minetest.registered_nodes[techage.get_node_lvm(pos).name] or {}).consumer end
|
||||
|
||||
local S = techage.S
|
||||
|
||||
local CYCLE_TIME = 4
|
||||
local STANDBY_TICKS = 4
|
||||
local COUNTDOWN_TICKS = 4
|
||||
|
||||
local Side2Facedir = {F=0, R=1, B=2, L=3, D=4, U=5}
|
||||
local Depth2Idx = {[1]=1 ,[2]=2, [3]=3, [5]=4, [7]=5, [10]=6, [15]=7, [20]=8, [25]=9, [40]=10, [60]=11, [80]=12}
|
||||
local Holesize2Idx = {["3x3"] = 1, ["5x5"] = 2, ["7x7"] = 3, ["9x9"] = 4, ["11x11"] = 5}
|
||||
local Holesize2Diameter = {["3x3"] = 3, ["5x5"] = 5, ["7x7"] = 7, ["9x9"] = 9, ["11x11"] = 11}
|
||||
local Level2Idx = {[2]=1, [1]=2, [0]=3, [-1]=4, [-2]=5, [-3]=6,
|
||||
[-5]=7, [-10]=8, [-15]=9, [-20]=10}
|
||||
|
||||
local function formspec(self, pos, nvm)
|
||||
local tooltip = S("Start level = 0\nmeans the same level\nas the quarry is placed")
|
||||
local level_idx = Level2Idx[nvm.start_level or 1] or 2
|
||||
local depth_idx = Depth2Idx[nvm.quarry_depth or 1] or 1
|
||||
local hsize_idx = Holesize2Idx[nvm.hole_size or "5x5"] or 2
|
||||
local level = nvm.level or "-"
|
||||
local hsize_list = "5x5"
|
||||
if CRD(pos).stage == 4 then
|
||||
hsize_list = "3x3,5x5,7x7,9x9,11x11"
|
||||
elseif CRD(pos).stage == 3 then
|
||||
hsize_list = "3x3,5x5,7x7"
|
||||
end
|
||||
local depth_list = "1,2,3,5,7,10,15,20,25,40,60,80"
|
||||
if CRD(pos).stage == 3 then
|
||||
depth_list = "1,2,3,5,7,10,15,20,25,40"
|
||||
elseif CRD(pos).stage == 2 then
|
||||
depth_list = "1,2,3,5,7,10,15,20"
|
||||
end
|
||||
|
||||
return "size[8,8]"..
|
||||
default.gui_bg..
|
||||
default.gui_bg_img..
|
||||
default.gui_slots..
|
||||
"box[0,-0.1;7.8,0.5;#c6e8ff]"..
|
||||
"label[3.5,-0.1;"..minetest.colorize( "#000000", S("Quarry")).."]"..
|
||||
techage.question_mark_help(8, tooltip)..
|
||||
"dropdown[0,0.8;1.5;level;2,1,0,-1,-2,-3,-5,-10,-15,-20;"..level_idx.."]"..
|
||||
"label[1.6,0.9;"..S("Start level").."]"..
|
||||
"dropdown[0,1.8;1.5;depth;"..depth_list..";"..depth_idx.."]"..
|
||||
"label[1.6,1.9;"..S("Digging depth").." ("..level..")]"..
|
||||
"dropdown[0,2.8;1.5;hole_size;"..hsize_list..";"..hsize_idx.."]"..
|
||||
"label[1.6,2.9;"..S("Hole size").."]"..
|
||||
"list[context;main;5,0.8;3,3;]"..
|
||||
"image[4,0.8;1,1;"..techage.get_power_image(pos, nvm).."]"..
|
||||
"image_button[4,2.8;1,1;".. self:get_state_button_image(nvm) ..";state_button;]"..
|
||||
"tooltip[4,2.8;1,1;"..self:get_state_tooltip(nvm).."]"..
|
||||
"list[current_player;main;0,4.3;8,4;]"..
|
||||
"listring[context;main]"..
|
||||
"listring[current_player;main]"
|
||||
end
|
||||
|
||||
local function play_sound(pos)
|
||||
local mem = techage.get_mem(pos)
|
||||
if not mem.handle or mem.handle == -1 then
|
||||
mem.handle = minetest.sound_play("techage_quarry", {
|
||||
pos = pos,
|
||||
gain = 1.5,
|
||||
max_hear_distance = 15,
|
||||
loop = true})
|
||||
if mem.handle == -1 then
|
||||
minetest.after(1, play_sound, pos)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function stop_sound(pos)
|
||||
local mem = techage.get_mem(pos)
|
||||
if mem.handle then
|
||||
minetest.sound_stop(mem.handle)
|
||||
mem.handle = nil
|
||||
end
|
||||
end
|
||||
|
||||
local function on_node_state_change(pos, old_state, new_state)
|
||||
local mem = techage.get_mem(pos)
|
||||
local owner = M(pos):get_string("owner")
|
||||
mem.co = nil
|
||||
techage.unmark_position(owner)
|
||||
if new_state == techage.RUNNING then
|
||||
play_sound(pos)
|
||||
else
|
||||
stop_sound(pos)
|
||||
end
|
||||
end
|
||||
|
||||
local function get_pos(pos, facedir, side, steps)
|
||||
facedir = (facedir + Side2Facedir[side]) % 4
|
||||
local dir = vector.multiply(minetest.facedir_to_dir(facedir), steps or 1)
|
||||
return vector.add(pos, dir)
|
||||
end
|
||||
|
||||
local function allow_metadata_inventory_put(pos, listname, index, stack, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return 0
|
||||
end
|
||||
return stack:get_count()
|
||||
end
|
||||
|
||||
local function allow_metadata_inventory_take(pos, listname, index, stack, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return 0
|
||||
end
|
||||
return stack:get_count()
|
||||
end
|
||||
|
||||
|
||||
local function get_quarry_pos(pos, xoffs, zoffs)
|
||||
return {x = pos.x + xoffs - 1, y = pos.y, z = pos.z + zoffs - 1}
|
||||
end
|
||||
|
||||
-- pos is the quarry pos
|
||||
local function get_corner_positions(pos, facedir, hole_diameter)
|
||||
local _pos = get_pos(pos, facedir, "L")
|
||||
local pos1 = get_pos(_pos, facedir, "F", math.floor((hole_diameter - 1) / 2))
|
||||
local pos2 = get_pos(_pos, facedir, "B", math.floor((hole_diameter - 1) / 2))
|
||||
pos2 = get_pos(pos2, facedir, "L", hole_diameter - 1)
|
||||
if pos1.x > pos2.x then pos1.x, pos2.x = pos2.x, pos1.x end
|
||||
if pos1.y > pos2.y then pos1.y, pos2.y = pos2.y, pos1.y end
|
||||
if pos1.z > pos2.z then pos1.z, pos2.z = pos2.z, pos1.z end
|
||||
return pos1, pos2
|
||||
end
|
||||
|
||||
local function is_air_level(pos1, pos2, hole_diameter)
|
||||
return #minetest.find_nodes_in_area(pos1, pos2, {"air"}) == hole_diameter * hole_diameter
|
||||
end
|
||||
|
||||
local function mark_area(pos1, pos2, owner)
|
||||
pos1.y = pos1.y + 0.2
|
||||
techage.mark_cube(owner, pos1, pos2, "quarry", "#FF0000", 20)
|
||||
pos1.y = pos1.y - 0.2
|
||||
end
|
||||
|
||||
local function quarry_task(pos, crd, nvm)
|
||||
nvm.start_level = nvm.start_level or 0
|
||||
nvm.quarry_depth = nvm.quarry_depth or 1
|
||||
nvm.hole_diameter = nvm.hole_diameter or 5
|
||||
local y_first = pos.y + nvm.start_level
|
||||
local y_last = y_first - nvm.quarry_depth + 1
|
||||
local facedir = minetest.get_node(pos).param2
|
||||
local owner = M(pos):get_string("owner")
|
||||
local fake_player = techage.Fake_player:new()
|
||||
fake_player.get_pos = function (...)
|
||||
return pos
|
||||
end
|
||||
fake_player.get_inventory = function(...)
|
||||
return M(pos):get_inventory()
|
||||
end
|
||||
|
||||
local add_to_inv = function(itemstacks)
|
||||
local at_least_one_added = false
|
||||
local inv = M(pos):get_inventory()
|
||||
if #itemstacks == 0 then
|
||||
return true
|
||||
end
|
||||
for _,stack in ipairs(itemstacks) do
|
||||
if inv:room_for_item("main", stack) then
|
||||
inv:add_item("main", stack)
|
||||
at_least_one_added = true
|
||||
elseif at_least_one_added then
|
||||
minetest.add_item({x=pos.x,y=pos.y+1,z=pos.z}, stack)
|
||||
end
|
||||
end
|
||||
return at_least_one_added
|
||||
end
|
||||
|
||||
local pos1, pos2 = get_corner_positions(pos, facedir, nvm.hole_diameter)
|
||||
nvm.level = 1
|
||||
for y_curr = y_first, y_last, -1 do
|
||||
pos1.y = y_curr
|
||||
pos2.y = y_curr
|
||||
|
||||
-- Restarting the server can detach the coroutine data.
|
||||
-- Therefore, read nvm again.
|
||||
nvm = techage.get_nvm(pos)
|
||||
nvm.level = y_first - y_curr
|
||||
|
||||
if minetest.is_area_protected(pos1, pos2, owner, 5) then
|
||||
crd.State:fault(pos, nvm, S("area is protected"))
|
||||
return
|
||||
end
|
||||
|
||||
if not is_air_level(pos1, pos2, nvm.hole_diameter) then
|
||||
mark_area(pos1, pos2, owner)
|
||||
M(pos):set_string("formspec", formspec(CRD(pos).State, pos, nvm))
|
||||
coroutine.yield()
|
||||
|
||||
for zoffs = 1, nvm.hole_diameter do
|
||||
for xoffs = 1, nvm.hole_diameter do
|
||||
local qpos = get_quarry_pos(pos1, xoffs, zoffs)
|
||||
local dig_state = techage.dig_like_player(qpos, fake_player, add_to_inv)
|
||||
|
||||
if dig_state == techage.dig_states.INV_FULL then
|
||||
crd.State:blocked(pos, nvm, S("inventory full"))
|
||||
coroutine.yield()
|
||||
elseif dig_state == techage.dig_states.DUG then
|
||||
crd.State:keep_running(pos, nvm, COUNTDOWN_TICKS)
|
||||
coroutine.yield()
|
||||
end
|
||||
end
|
||||
end
|
||||
techage.unmark_position(owner)
|
||||
end
|
||||
end
|
||||
crd.State:stop(pos, nvm, S("finished"))
|
||||
end
|
||||
|
||||
local function keep_running(pos, elapsed)
|
||||
local mem = techage.get_mem(pos)
|
||||
if not mem.co then
|
||||
mem.co = coroutine.create(quarry_task)
|
||||
end
|
||||
|
||||
local nvm = techage.get_nvm(pos)
|
||||
local crd = CRD(pos)
|
||||
local _, err = coroutine.resume(mem.co, pos, crd, nvm)
|
||||
if err then
|
||||
minetest.log("error", "[TA4 Quarry Coroutine Error] at pos " .. minetest.pos_to_string(pos) .. " " .. err)
|
||||
end
|
||||
|
||||
if techage.is_activeformspec(pos) then
|
||||
M(pos):set_string("formspec", formspec(crd.State, pos, nvm))
|
||||
end
|
||||
if nvm.techage_state ~= techage.RUNNING then
|
||||
stop_sound(pos)
|
||||
end
|
||||
end
|
||||
|
||||
local function on_rightclick(pos, node, clicker)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
techage.set_activeformspec(pos, clicker)
|
||||
M(pos):set_string("formspec", formspec(CRD(pos).State, pos, nvm))
|
||||
end
|
||||
|
||||
local function can_dig(pos, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return false
|
||||
end
|
||||
local inv = M(pos):get_inventory()
|
||||
return inv:is_empty("main")
|
||||
end
|
||||
|
||||
local function on_receive_fields(pos, formname, fields, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return
|
||||
end
|
||||
local nvm = techage.get_nvm(pos)
|
||||
local mem = techage.get_mem(pos)
|
||||
|
||||
if fields.depth then
|
||||
if tonumber(fields.depth) ~= nvm.quarry_depth then
|
||||
nvm.quarry_depth = tonumber(fields.depth)
|
||||
if CRD(pos).stage == 2 then
|
||||
nvm.quarry_depth = math.min(nvm.quarry_depth, 20)
|
||||
elseif CRD(pos).stage == 3 then
|
||||
nvm.quarry_depth = math.min(nvm.quarry_depth, 40)
|
||||
end
|
||||
mem.co = nil
|
||||
CRD(pos).State:stop(pos, nvm)
|
||||
end
|
||||
end
|
||||
|
||||
if fields.level then
|
||||
if tonumber(fields.level) ~= nvm.start_level then
|
||||
nvm.start_level = tonumber(fields.level)
|
||||
mem.co = nil
|
||||
CRD(pos).State:stop(pos, nvm)
|
||||
end
|
||||
end
|
||||
|
||||
if fields.hole_size then
|
||||
if CRD(pos).stage == 4 then
|
||||
if fields.hole_size ~= nvm.hole_size then
|
||||
nvm.hole_size = fields.hole_size
|
||||
nvm.hole_diameter = Holesize2Diameter[fields.hole_size or "5x5"] or 5
|
||||
mem.co = nil
|
||||
CRD(pos).State:stop(pos, nvm)
|
||||
end
|
||||
elseif CRD(pos).stage == 3 then
|
||||
if fields.hole_size ~= nvm.hole_size then
|
||||
nvm.hole_size = fields.hole_size
|
||||
nvm.hole_diameter = Holesize2Diameter[fields.hole_size or "7x7"] or 7
|
||||
mem.co = nil
|
||||
CRD(pos).State:stop(pos, nvm)
|
||||
end
|
||||
else
|
||||
nvm.hole_size = "5x5"
|
||||
nvm.hole_diameter = 5
|
||||
end
|
||||
end
|
||||
|
||||
CRD(pos).State:state_button_event(pos, nvm, fields)
|
||||
end
|
||||
|
||||
local tiles = {}
|
||||
-- '#' will be replaced by the stage number
|
||||
tiles.pas = {
|
||||
-- up, down, right, left, back, front
|
||||
"techage_filling_ta#.png^techage_frame_ta#_top.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_outp.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#.png^techage_quarry_left.png",
|
||||
"techage_filling_ta#.png^techage_appl_quarry.png^techage_frame_ta#.png",
|
||||
"techage_filling_ta#.png^techage_appl_quarry.png^techage_frame_ta#.png",
|
||||
}
|
||||
tiles.act = {
|
||||
-- up, down, right, left, back, front
|
||||
"techage_filling_ta#.png^techage_frame_ta#_top.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_outp.png",
|
||||
{
|
||||
name = "techage_frame14_ta#.png^techage_quarry_left14.png",
|
||||
backface_culling = false,
|
||||
animation = {
|
||||
type = "vertical_frames",
|
||||
aspect_w = 32,
|
||||
aspect_h = 32,
|
||||
length = 2.0,
|
||||
},
|
||||
},
|
||||
"techage_filling_ta#.png^techage_appl_quarry.png^techage_frame_ta#.png",
|
||||
"techage_filling_ta#.png^techage_appl_quarry.png^techage_frame_ta#.png",
|
||||
}
|
||||
|
||||
local tubing = {
|
||||
on_pull_item = function(pos, in_dir, num)
|
||||
local meta = minetest.get_meta(pos)
|
||||
if meta:get_int("pull_dir") == in_dir then
|
||||
local inv = M(pos):get_inventory()
|
||||
return techage.get_items(pos, inv, "main", num)
|
||||
end
|
||||
end,
|
||||
on_push_item = function(pos, in_dir, stack)
|
||||
local meta = minetest.get_meta(pos)
|
||||
if meta:get_int("push_dir") == in_dir or in_dir == 5 then
|
||||
local inv = M(pos):get_inventory()
|
||||
--CRD(pos).State:start_if_standby(pos) -- would need power!
|
||||
return techage.put_items(inv, "main", stack)
|
||||
end
|
||||
end,
|
||||
on_unpull_item = function(pos, in_dir, stack)
|
||||
local meta = minetest.get_meta(pos)
|
||||
if meta:get_int("pull_dir") == in_dir then
|
||||
local inv = M(pos):get_inventory()
|
||||
return techage.put_items(inv, "main", stack)
|
||||
end
|
||||
end,
|
||||
on_recv_message = function(pos, src, topic, payload)
|
||||
if topic == "depth" then
|
||||
local nvm = techage.get_nvm(pos)
|
||||
return nvm.level or 0
|
||||
else
|
||||
return CRD(pos).State:on_receive_message(pos, topic, payload)
|
||||
end
|
||||
end,
|
||||
on_beduino_receive_cmnd = function(pos, src, topic, payload)
|
||||
return CRD(pos).State:on_beduino_receive_cmnd(pos, topic, payload)
|
||||
end,
|
||||
on_beduino_request_data = function(pos, src, topic, payload)
|
||||
if topic == 133 then -- Quarry Depth
|
||||
local nvm = techage.get_nvm(pos)
|
||||
return 0, {nvm.level or 0}
|
||||
else
|
||||
return CRD(pos).State:on_beduino_request_data(pos, topic, payload)
|
||||
end
|
||||
end,
|
||||
on_node_load = function(pos)
|
||||
CRD(pos).State:on_node_load(pos)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
if nvm.techage_state == techage.RUNNING then
|
||||
stop_sound(pos)
|
||||
play_sound(pos)
|
||||
end
|
||||
end,
|
||||
}
|
||||
|
||||
local node_name_ta2, node_name_ta3, node_name_ta4 =
|
||||
techage.register_consumer("quarry", S("Quarry"), tiles, {
|
||||
drawtype = "normal",
|
||||
cycle_time = CYCLE_TIME,
|
||||
standby_ticks = STANDBY_TICKS,
|
||||
formspec = formspec,
|
||||
tubing = tubing,
|
||||
on_state_change = on_node_state_change,
|
||||
after_place_node = function(pos, placer)
|
||||
local inv = M(pos):get_inventory()
|
||||
local nvm = techage.get_nvm(pos)
|
||||
inv:set_size('main', 9)
|
||||
M(pos):set_string("owner", placer:get_player_name())
|
||||
end,
|
||||
can_dig = can_dig,
|
||||
node_timer = keep_running,
|
||||
on_receive_fields = on_receive_fields,
|
||||
on_rightclick = on_rightclick,
|
||||
allow_metadata_inventory_put = allow_metadata_inventory_put,
|
||||
allow_metadata_inventory_take = allow_metadata_inventory_take,
|
||||
groups = {choppy=2, cracky=2, crumbly=2},
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
num_items = {0,1,1,1},
|
||||
power_consumption = {0,10,12,14},
|
||||
}
|
||||
)
|
||||
|
||||
minetest.register_craft({
|
||||
output = node_name_ta2,
|
||||
recipe = {
|
||||
{"group:wood", "default:mese_crystal", "group:wood"},
|
||||
{"techage:tubeS", "default:pick_diamond", "techage:iron_ingot"},
|
||||
{"group:wood", "techage:iron_ingot", "group:wood"},
|
||||
},
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = node_name_ta3,
|
||||
recipe = {
|
||||
{"", "default:mese_crystal", ""},
|
||||
{"", node_name_ta2, ""},
|
||||
{"", "techage:vacuum_tube", ""},
|
||||
},
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = node_name_ta4,
|
||||
recipe = {
|
||||
{"", "default:mese_crystal", ""},
|
||||
{"", node_name_ta3, ""},
|
||||
{"", "techage:ta4_wlanchip", ""},
|
||||
},
|
||||
})
|
243
techage/basic_machines/recipeblock.lua
Normal file
243
techage/basic_machines/recipeblock.lua
Normal file
@ -0,0 +1,243 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019-2021 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
TA4 Recipe Block for the TA4 Autocrafter
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local S2P = minetest.string_to_pos
|
||||
local M = minetest.get_meta
|
||||
local S = techage.S
|
||||
|
||||
local MAX_RECIPE = 10
|
||||
|
||||
local function recipes_formspec(x, y, idx)
|
||||
return "container[" .. x .. "," .. y .. "]" ..
|
||||
"background[0,0;8,3.2;techage_form_grey.png]" ..
|
||||
"list[context;input;0.1,0.1;3,3;]" ..
|
||||
"image[3,1.1;1,1;techage_form_arrow.png]" ..
|
||||
"list[context;output;3.9,1.1;1,1;]" ..
|
||||
"button[5.5,1.1;1,1;priv;<<]" ..
|
||||
"button[6.5,1.1;1,1;next;>>]" ..
|
||||
"label[5.5,0.5;"..S("Recipe") .. ": " .. idx .. "/" .. MAX_RECIPE .. "]" ..
|
||||
"container_end[]"
|
||||
end
|
||||
|
||||
local function formspec(pos, nvm)
|
||||
return "size[8,7.4]"..
|
||||
recipes_formspec(0, 0, nvm.recipe_idx or 1) ..
|
||||
"list[current_player;main;0,3.6;8,4;]" ..
|
||||
"listring[current_player;main]"..
|
||||
"listring[context;src]" ..
|
||||
"listring[current_player;main]"..
|
||||
"listring[context;dst]" ..
|
||||
"listring[current_player;main]"
|
||||
end
|
||||
|
||||
local function determine_new_input(pos, inv)
|
||||
local output = inv:get_stack("output", 1):get_name()
|
||||
if output and output ~= "" then
|
||||
local recipe = minetest.get_craft_recipe(output)
|
||||
if recipe.items and recipe.type == "normal" then
|
||||
for i = 1, 9 do
|
||||
local name = recipe.items[i]
|
||||
if name then
|
||||
if minetest.registered_items[name] then
|
||||
inv:set_stack("input", i, name)
|
||||
end
|
||||
end
|
||||
end
|
||||
inv:set_stack("output", 1, recipe.output)
|
||||
end
|
||||
else
|
||||
for i = 1, 9 do
|
||||
inv:set_stack("input", i, nil)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function determine_new_output(pos, inv)
|
||||
local items = {}
|
||||
for i = 1, 9 do
|
||||
items[i] = inv:get_stack("input", i):get_name()
|
||||
end
|
||||
local input = {
|
||||
method = "normal",
|
||||
width = 3,
|
||||
items = items,
|
||||
}
|
||||
local output, _ = minetest.get_craft_result(input)
|
||||
inv:set_stack("output", 1, output.item)
|
||||
end
|
||||
|
||||
local function get_recipe(inv)
|
||||
local items = {}
|
||||
local last_idx = 0
|
||||
for i = 1, 9 do
|
||||
local name = inv:get_stack("input", i):get_name()
|
||||
if name ~= "" then
|
||||
last_idx = i
|
||||
end
|
||||
items[i] = name
|
||||
end
|
||||
local input = table.concat(items, ",", 1, last_idx)
|
||||
local stack = inv:get_stack("output", 1)
|
||||
return {
|
||||
input = input,
|
||||
output = stack:get_name() .. " " .. stack:get_count()
|
||||
}
|
||||
end
|
||||
|
||||
local function after_recipe_change(pos, inv, listname)
|
||||
if listname == "input" then
|
||||
determine_new_output(pos, inv)
|
||||
else
|
||||
determine_new_input(pos, inv)
|
||||
end
|
||||
local nvm = techage.get_nvm(pos)
|
||||
nvm.recipes = nvm.recipes or {}
|
||||
nvm.recipes[nvm.recipe_idx or 1] = get_recipe(inv)
|
||||
end
|
||||
|
||||
local function update_inventor(pos, inv, idx)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
nvm.recipes = nvm.recipes or {}
|
||||
local recipe = nvm.recipes[idx]
|
||||
if recipe then
|
||||
local items = string.split(recipe.input, ",", true)
|
||||
for i = 1, 9 do
|
||||
inv:set_stack("input", i, items[i] or "")
|
||||
end
|
||||
inv:set_stack("output", 1, recipe.output)
|
||||
else
|
||||
for i = 1, 9 do
|
||||
inv:set_stack("input", i, nil)
|
||||
end
|
||||
inv:set_stack("output", 1, nil)
|
||||
end
|
||||
end
|
||||
|
||||
local function allow_metadata_inventory_put(pos, listname, index, stack, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return 0
|
||||
end
|
||||
|
||||
local inv = M(pos):get_inventory()
|
||||
local list = inv:get_list(listname)
|
||||
stack:set_count(1)
|
||||
inv:set_stack(listname, index, stack)
|
||||
after_recipe_change(pos, inv, listname)
|
||||
return 0
|
||||
end
|
||||
|
||||
local function allow_metadata_inventory_take(pos, listname, index, stack, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return 0
|
||||
end
|
||||
|
||||
local inv = M(pos):get_inventory()
|
||||
inv:set_stack(listname, index, nil)
|
||||
after_recipe_change(pos, inv, listname)
|
||||
return 0
|
||||
end
|
||||
|
||||
local function allow_metadata_inventory_move(pos, from_list, from_index, to_list, to_index, count, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return 0
|
||||
end
|
||||
|
||||
local inv = M(pos):get_inventory()
|
||||
if from_list == to_list then
|
||||
minetest.after(0.1, after_recipe_change, pos, inv, from_list)
|
||||
return 1
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
minetest.register_node("techage:ta4_recipeblock", {
|
||||
description = S("TA4 Recipe Block"),
|
||||
tiles = {
|
||||
-- up, down, right, left, back, front
|
||||
"techage_filling_ta4.png^techage_frame_ta4_top.png",
|
||||
"techage_filling_ta4.png^techage_frame_ta4_top.png",
|
||||
"techage_filling_ta4.png^techage_frame_ta4.png^techage_appl_recipeblock.png",
|
||||
},
|
||||
|
||||
on_construct = function(pos)
|
||||
local inv = M(pos):get_inventory()
|
||||
inv:set_size('input', 9)
|
||||
inv:set_size('output', 1)
|
||||
end,
|
||||
|
||||
after_place_node = function(pos, placer, itemstack)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
local number = techage.add_node(pos, "techage:ta4_chest")
|
||||
M(pos):set_string("owner", placer:get_player_name())
|
||||
M(pos):set_string("node_number", number)
|
||||
M(pos):set_string("formspec", formspec(pos, nvm))
|
||||
M(pos):set_string("infotext", S("TA4 Recipe Block") .. " " .. number)
|
||||
end,
|
||||
|
||||
on_receive_fields = function(pos, formname, fields, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return
|
||||
end
|
||||
|
||||
local nvm = techage.get_nvm(pos)
|
||||
nvm.recipe_idx = nvm.recipe_idx or 1
|
||||
if fields.next == ">>" then
|
||||
nvm.recipe_idx = techage.in_range(nvm.recipe_idx + 1, 1, MAX_RECIPE)
|
||||
elseif fields.priv == "<<" then
|
||||
nvm.recipe_idx = techage.in_range(nvm.recipe_idx - 1, 1, MAX_RECIPE)
|
||||
end
|
||||
local inv = M(pos):get_inventory()
|
||||
update_inventor(pos, inv, nvm.recipe_idx or 1)
|
||||
M(pos):set_string("formspec", formspec(pos, nvm))
|
||||
end,
|
||||
|
||||
after_dig_node = function(pos, oldnode, oldmetadata, digger)
|
||||
techage.remove_node(pos, oldnode, oldmetadata)
|
||||
techage.del_mem(pos)
|
||||
end,
|
||||
|
||||
allow_metadata_inventory_put = allow_metadata_inventory_put,
|
||||
allow_metadata_inventory_take = allow_metadata_inventory_take,
|
||||
allow_metadata_inventory_move = allow_metadata_inventory_move,
|
||||
|
||||
paramtype2 = "facedir",
|
||||
groups = {choppy=2, cracky=2, crumbly=2},
|
||||
is_ground_content = false,
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
})
|
||||
|
||||
techage.register_node({"techage:ta4_recipeblock"}, {
|
||||
on_recv_message = function(pos, src, topic, payload)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
if topic == "input" and payload and payload ~= "" then
|
||||
nvm.recipes = nvm.recipes or {}
|
||||
local recipe = nvm.recipes[tonumber(payload) or 1]
|
||||
if recipe then
|
||||
return recipe.input
|
||||
end
|
||||
else
|
||||
return "unsupported"
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "techage:ta4_recipeblock",
|
||||
recipe = {
|
||||
{"techage:ta4_carbon_fiber", "dye:blue", "techage:aluminum"},
|
||||
{"", "basic_materials:ic", ""},
|
||||
{"default:steel_ingot", "techage:ta4_wlanchip", "default:steel_ingot"},
|
||||
},
|
||||
})
|
358
techage/basic_machines/recycler.lua
Normal file
358
techage/basic_machines/recycler.lua
Normal file
@ -0,0 +1,358 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019-2022 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
TA4 Recycler, recycling techage machines
|
||||
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local M = minetest.get_meta
|
||||
local S = techage.S
|
||||
|
||||
-- Consumer Related Data
|
||||
local CRD = function(pos) return (minetest.registered_nodes[techage.get_node_lvm(pos).name] or {}).consumer end
|
||||
|
||||
local STANDBY_TICKS = 3
|
||||
local COUNTDOWN_TICKS = 4
|
||||
local CYCLE_TIME = 8
|
||||
|
||||
local Recipes = {}
|
||||
|
||||
local SpecialItems = {
|
||||
["techage:sieved_gravel"] = "default:sand",
|
||||
["basic_materials:heating_element"] = "default:copper_ingot",
|
||||
["techage:ta4_wlanchip"] = "",
|
||||
["techage:basalt_cobble"] = "default:sand",
|
||||
["default:stone"] = "techage:sieved_gravel",
|
||||
["default:wood"] = "default:stick 5",
|
||||
["basic_materials:concrete_block"] = "techage:sieved_gravel",
|
||||
["dye:green"] = "",
|
||||
["dye:red"] = "",
|
||||
["dye:white"] = "",
|
||||
["dye:blue"] = "",
|
||||
["dye:brown"] = "",
|
||||
["dye:cyan"] = "",
|
||||
["dye:yellow"] = "",
|
||||
["dye:grey"] = "",
|
||||
["dye:orange"] = "",
|
||||
["dye:black"] = "",
|
||||
["techage:basalt_glass_thin"] = "",
|
||||
["group:stone"] = "techage:sieved_gravel",
|
||||
--["basic_materials:plastic_sheet"] = "",
|
||||
["group:wood"] = "default:stick 5",
|
||||
["techage:basalt_glass"] = "",
|
||||
["default:junglewood"] = "default:stick 5",
|
||||
["techage:ta4_silicon_wafer"] = "",
|
||||
["default:cobble"] = "techage:sieved_gravel",
|
||||
["default:pick_diamond"] = "default:stick",
|
||||
["techage:hammer_steel"] = "default:stick",
|
||||
["default:paper"] = "",
|
||||
["stairs:slab_basalt_glass2"] = "",
|
||||
["techage:basalt_stone"] = "techage:sieved_gravel",
|
||||
["techage:ta4_ramchip"] = "",
|
||||
["protector:chest"] = "default:chest",
|
||||
["techage:ta4_rotor_blade"] = "",
|
||||
["techage:ta4_carbon_fiber"] = "",
|
||||
["techage:ta4_round_ceramic"] = "",
|
||||
["techage:ta4_furnace_ceramic"] = "",
|
||||
["techage:ta5_aichip"] = "",
|
||||
["techage:ta4_leds"] = "",
|
||||
}
|
||||
|
||||
local function formspec(self, pos, nvm)
|
||||
return "size[8,8]"..
|
||||
default.gui_bg..
|
||||
default.gui_bg_img..
|
||||
default.gui_slots..
|
||||
"list[context;src;0,0;3,3;]"..
|
||||
--"item_image[0,0;1,1;default:cobble]"..
|
||||
"image[0,0;1,1;techage_form_mask.png]"..
|
||||
"image[3.5,0;1,1;"..techage.get_power_image(pos, nvm).."]"..
|
||||
"image[3.5,1;1,1;techage_form_arrow.png]"..
|
||||
"image_button[3.5,2;1,1;"..self:get_state_button_image(nvm)..";state_button;]"..
|
||||
"tooltip[3.5,2;1,1;"..self:get_state_tooltip(nvm).."]"..
|
||||
"list[context;dst;5,0;3,3;]"..
|
||||
--"item_image[5,0;1,1;default:gravel]"..
|
||||
"image[5,0;1,1;techage_form_mask.png]"..
|
||||
"list[current_player;main;0,4;8,4;]"..
|
||||
"listring[context;dst]"..
|
||||
"listring[current_player;main]"..
|
||||
"listring[context;src]"..
|
||||
"listring[current_player;main]"..
|
||||
default.get_hotbar_bg(0, 4)
|
||||
end
|
||||
|
||||
local function allow_metadata_inventory_put(pos, listname, index, stack, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return 0
|
||||
end
|
||||
if listname == "src" then
|
||||
CRD(pos).State:start_if_standby(pos)
|
||||
end
|
||||
return stack:get_count()
|
||||
end
|
||||
|
||||
local function allow_metadata_inventory_move(pos, from_list, from_index, to_list, to_index, count, player)
|
||||
local inv = M(pos):get_inventory()
|
||||
local stack = inv:get_stack(from_list, from_index)
|
||||
return allow_metadata_inventory_put(pos, to_list, to_index, stack, player)
|
||||
end
|
||||
|
||||
local function allow_metadata_inventory_take(pos, listname, index, stack, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return 0
|
||||
end
|
||||
return stack:get_count()
|
||||
end
|
||||
|
||||
local function cook_reverse(stack, inv, idx, recipe)
|
||||
-- check space
|
||||
for _,item in ipairs(recipe.items) do
|
||||
if not inv:room_for_item("dst", item) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
-- take item
|
||||
inv:remove_item("src", ItemStack(recipe.output))
|
||||
-- add items
|
||||
for _,item in ipairs(recipe.items) do
|
||||
inv:add_item("dst", item)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local function get_recipe(stack)
|
||||
local name = stack:get_name()
|
||||
local recipe = Recipes[name]
|
||||
if recipe then
|
||||
if stack:get_count() >= ItemStack(recipe.output):get_count() then
|
||||
return recipe
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function recycling(pos, crd, nvm, inv)
|
||||
for idx,stack in ipairs(inv:get_list("src")) do
|
||||
local recipe = not stack:is_empty() and get_recipe(stack)
|
||||
if recipe then
|
||||
if cook_reverse(stack, inv, idx, recipe) then
|
||||
crd.State:keep_running(pos, nvm, COUNTDOWN_TICKS)
|
||||
else
|
||||
crd.State:blocked(pos, nvm)
|
||||
end
|
||||
return
|
||||
end
|
||||
end
|
||||
crd.State:idle(pos, nvm)
|
||||
end
|
||||
|
||||
local function keep_running(pos, elapsed)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
local crd = CRD(pos)
|
||||
local inv = M(pos):get_inventory()
|
||||
recycling(pos, crd, nvm, inv)
|
||||
end
|
||||
|
||||
local function on_receive_fields(pos, formname, fields, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return
|
||||
end
|
||||
local nvm = techage.get_nvm(pos)
|
||||
CRD(pos).State:state_button_event(pos, nvm, fields)
|
||||
end
|
||||
|
||||
local function can_dig(pos, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return false
|
||||
end
|
||||
local inv = M(pos):get_inventory()
|
||||
return inv:is_empty("dst") and inv:is_empty("src")
|
||||
end
|
||||
|
||||
|
||||
local tiles = {}
|
||||
-- '#' will be replaced by the stage number
|
||||
-- '{power}' will be replaced by the power PNG
|
||||
tiles.pas = {
|
||||
-- up, down, right, left, back, front
|
||||
"techage_appl_grinder.png^[colorize:@@000000:100^techage_frame_ta#_top.png",
|
||||
--"techage_appl_grinder.png^techage_frame_ta#_top.png^[multiply:#FF0000",
|
||||
"techage_filling_ta#.png^techage_frame_ta#.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_outp.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_inp.png",
|
||||
"techage_filling_ta#.png^techage_appl_recycler.png^techage_frame_ta#.png",
|
||||
"techage_filling_ta#.png^techage_appl_recycler.png^techage_frame_ta#.png",
|
||||
}
|
||||
tiles.act = {
|
||||
-- up, down, right, left, back, front
|
||||
{
|
||||
name = "techage_appl_grinder4.png^[colorize:@@000000:100^techage_frame4_ta#_top.png",
|
||||
backface_culling = false,
|
||||
animation = {
|
||||
type = "vertical_frames",
|
||||
aspect_w = 32,
|
||||
aspect_h = 32,
|
||||
length = 1.0,
|
||||
},
|
||||
},
|
||||
"techage_filling_ta#.png^techage_frame_ta#.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_outp.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_inp.png",
|
||||
"techage_filling_ta#.png^techage_appl_recycler.png^techage_frame_ta#.png",
|
||||
"techage_filling_ta#.png^techage_appl_recycler.png^techage_frame_ta#.png",
|
||||
}
|
||||
|
||||
local tubing = {
|
||||
on_pull_item = function(pos, in_dir, num)
|
||||
local meta = minetest.get_meta(pos)
|
||||
if meta:get_int("pull_dir") == in_dir then
|
||||
local inv = M(pos):get_inventory()
|
||||
return techage.get_items(pos, inv, "dst", num)
|
||||
end
|
||||
end,
|
||||
on_push_item = function(pos, in_dir, stack)
|
||||
local meta = minetest.get_meta(pos)
|
||||
if meta:get_int("push_dir") == in_dir or in_dir == 5 then
|
||||
local inv = M(pos):get_inventory()
|
||||
--CRD(pos).State:start_if_standby(pos) -- would need power!
|
||||
return techage.put_items(inv, "src", stack)
|
||||
end
|
||||
end,
|
||||
on_unpull_item = function(pos, in_dir, stack)
|
||||
local meta = minetest.get_meta(pos)
|
||||
if meta:get_int("pull_dir") == in_dir then
|
||||
local inv = M(pos):get_inventory()
|
||||
return techage.put_items(inv, "dst", stack)
|
||||
end
|
||||
end,
|
||||
on_recv_message = function(pos, src, topic, payload)
|
||||
return CRD(pos).State:on_receive_message(pos, topic, payload)
|
||||
end,
|
||||
on_beduino_receive_cmnd = function(pos, src, topic, payload)
|
||||
return CRD(pos).State:on_beduino_receive_cmnd(pos, topic, payload)
|
||||
end,
|
||||
on_beduino_request_data = function(pos, src, topic, payload)
|
||||
return CRD(pos).State:on_beduino_request_data(pos, topic, payload)
|
||||
end,
|
||||
on_node_load = function(pos)
|
||||
CRD(pos).State:on_node_load(pos)
|
||||
end,
|
||||
}
|
||||
|
||||
local _, _, node_name_ta4 =
|
||||
techage.register_consumer("recycler", S("Recycler"), tiles, {
|
||||
drawtype = "nodebox",
|
||||
paramtype = "light",
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{-8/16, -8/16, -8/16, 8/16, 8/16, -6/16},
|
||||
{-8/16, -8/16, 6/16, 8/16, 8/16, 8/16},
|
||||
{-8/16, -8/16, -8/16, -6/16, 8/16, 8/16},
|
||||
{ 6/16, -8/16, -8/16, 8/16, 8/16, 8/16},
|
||||
{-6/16, -8/16, -6/16, 6/16, 6/16, 6/16},
|
||||
},
|
||||
},
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
fixed = {-8/16, -8/16, -8/16, 8/16, 8/16, 8/16},
|
||||
},
|
||||
cycle_time = CYCLE_TIME,
|
||||
standby_ticks = STANDBY_TICKS,
|
||||
formspec = formspec,
|
||||
tubing = tubing,
|
||||
after_place_node = function(pos, placer)
|
||||
local inv = M(pos):get_inventory()
|
||||
inv:set_size('src', 9)
|
||||
inv:set_size('dst', 9)
|
||||
end,
|
||||
can_dig = can_dig,
|
||||
node_timer = keep_running,
|
||||
on_receive_fields = on_receive_fields,
|
||||
allow_metadata_inventory_put = allow_metadata_inventory_put,
|
||||
allow_metadata_inventory_move = allow_metadata_inventory_move,
|
||||
allow_metadata_inventory_take = allow_metadata_inventory_take,
|
||||
groups = {choppy=2, cracky=2, crumbly=2},
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
num_items = {0,0,0,1},
|
||||
power_consumption = {0,0,0,16},
|
||||
},
|
||||
{false, false, false, true}) -- TA4 only
|
||||
|
||||
minetest.register_craft({
|
||||
output = node_name_ta4,
|
||||
recipe = {
|
||||
{"", "default:mese_crystal", ""},
|
||||
{"", "techage:ta4_grinder_pas", ""},
|
||||
{"", "techage:ta4_wlanchip", ""},
|
||||
},
|
||||
})
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Prepare recipes
|
||||
-------------------------------------------------------------------------------
|
||||
-- Nodes from mods that can be recycled
|
||||
local ModNames = {
|
||||
techage = true,
|
||||
hyperloop = true,
|
||||
}
|
||||
|
||||
local function get_item_list(inputs)
|
||||
local lst = {}
|
||||
for _,input in pairs(inputs or {}) do
|
||||
if SpecialItems[input] then
|
||||
input = SpecialItems[input]
|
||||
end
|
||||
if input and input ~= "" then
|
||||
if minetest.registered_nodes[input] or minetest.registered_items[input] then
|
||||
table.insert(lst, input)
|
||||
end
|
||||
end
|
||||
end
|
||||
return lst
|
||||
end
|
||||
|
||||
local function get_special_recipe(name)
|
||||
if SpecialItems[name] then
|
||||
return {
|
||||
output = name,
|
||||
items = {SpecialItems[name]}
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
local function collect_recipes()
|
||||
local add = function(name, ndef)
|
||||
local _, _, mod, _ = string.find(name, "([%w_]+):([%w_]+)")
|
||||
local recipe = get_special_recipe(name) or
|
||||
techage.recipes.get_recipe(name) or
|
||||
minetest.get_craft_recipe(name)
|
||||
local items = get_item_list(recipe.items)
|
||||
|
||||
if ModNames[mod]
|
||||
and ndef.groups.not_in_creative_inventory ~= 1
|
||||
and not ndef.tool_capabilities
|
||||
and recipe.output
|
||||
and next(items) then
|
||||
local s = table.concat(items, ", ")
|
||||
--print(string.format("%-36s {%s}", recipe.output, s))
|
||||
Recipes[name] = {output = recipe.output, items = items}
|
||||
end
|
||||
end
|
||||
|
||||
for name, ndef in pairs(minetest.registered_nodes) do
|
||||
add(name, ndef)
|
||||
end
|
||||
for name, ndef in pairs(minetest.registered_items) do
|
||||
add(name, ndef)
|
||||
end
|
||||
end
|
||||
|
||||
minetest.after(2, collect_recipes)
|
265
techage/basic_machines/source.lua
Normal file
265
techage/basic_machines/source.lua
Normal file
@ -0,0 +1,265 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019-2021 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
TA2/TA3 Power Test Source
|
||||
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local P = minetest.string_to_pos
|
||||
local M = minetest.get_meta
|
||||
local S = techage.S
|
||||
|
||||
local Axle = techage.Axle
|
||||
--local Pipe = techage.SteamPipe
|
||||
local Cable = techage.ElectricCable
|
||||
local power = networks.power
|
||||
local control = networks.control
|
||||
|
||||
local STANDBY_TICKS = 4
|
||||
local CYCLE_TIME = 2
|
||||
local PWR_PERF = 100
|
||||
|
||||
local function formspec(self, pos, nvm)
|
||||
return techage.generator_formspec(self, pos, nvm, S("Power Source"), nvm.provided, PWR_PERF)
|
||||
end
|
||||
|
||||
-- Axles texture animation
|
||||
local function switch_axles(pos, on)
|
||||
local outdir = M(pos):get_int("outdir")
|
||||
Axle:switch_tube_line(pos, outdir, on and "on" or "off")
|
||||
end
|
||||
|
||||
local function start_node2(pos, nvm, state)
|
||||
nvm.running = true
|
||||
nvm.provided = 0
|
||||
local outdir = M(pos):get_int("outdir")
|
||||
switch_axles(pos, true)
|
||||
power.start_storage_calc(pos, Axle, outdir)
|
||||
end
|
||||
|
||||
local function stop_node2(pos, nvm, state)
|
||||
nvm.running = false
|
||||
nvm.provided = 0
|
||||
nvm.load = 0
|
||||
local outdir = M(pos):get_int("outdir")
|
||||
switch_axles(pos, false)
|
||||
power.start_storage_calc(pos, Axle, outdir)
|
||||
end
|
||||
|
||||
local function start_node3(pos, nvm, state)
|
||||
local meta = M(pos)
|
||||
nvm.running = true
|
||||
nvm.provided = 0
|
||||
techage.evaluate_charge_termination(nvm, meta)
|
||||
local outdir = meta:get_int("outdir")
|
||||
power.start_storage_calc(pos, Cable, outdir)
|
||||
end
|
||||
|
||||
local function stop_node3(pos, nvm, state)
|
||||
nvm.running = false
|
||||
nvm.provided = 0
|
||||
local outdir = M(pos):get_int("outdir")
|
||||
power.start_storage_calc(pos, Cable, outdir)
|
||||
end
|
||||
|
||||
local State2 = techage.NodeStates:new({
|
||||
node_name_passive = "techage:t2_source",
|
||||
cycle_time = CYCLE_TIME,
|
||||
standby_ticks = STANDBY_TICKS,
|
||||
formspec_func = formspec,
|
||||
start_node = start_node2,
|
||||
stop_node = stop_node2,
|
||||
})
|
||||
|
||||
local State3 = techage.NodeStates:new({
|
||||
node_name_passive = "techage:t4_source",
|
||||
cycle_time = CYCLE_TIME,
|
||||
standby_ticks = STANDBY_TICKS,
|
||||
formspec_func = formspec,
|
||||
start_node = start_node3,
|
||||
stop_node = stop_node3,
|
||||
})
|
||||
|
||||
local function node_timer2(pos, elapsed)
|
||||
--print("node_timer2")
|
||||
local meta = M(pos)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
local outdir = meta:get_int("outdir")
|
||||
local tp1 = tonumber(meta:get_string("termpoint1"))
|
||||
local tp2 = tonumber(meta:get_string("termpoint2"))
|
||||
nvm.provided = power.provide_power(pos, Axle, outdir, PWR_PERF, tp1, tp2)
|
||||
nvm.load = power.get_storage_load(pos, Axle, outdir, PWR_PERF)
|
||||
if techage.is_activeformspec(pos) then
|
||||
meta:set_string("formspec", formspec(State2, pos, nvm))
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local function node_timer3(pos, elapsed)
|
||||
--print("node_timer4")
|
||||
local meta = M(pos)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
local outdir = M(pos):get_int("outdir")
|
||||
local tp1 = tonumber(meta:get_string("termpoint1"))
|
||||
local tp2 = tonumber(meta:get_string("termpoint2"))
|
||||
nvm.provided = power.provide_power(pos, Cable, outdir, PWR_PERF, tp1, tp2)
|
||||
nvm.load = power.get_storage_load(pos, Cable, outdir, PWR_PERF)
|
||||
if techage.is_activeformspec(pos) then
|
||||
meta:set_string("formspec", formspec(State3, pos, nvm))
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local function on_receive_fields2(pos, formname, fields, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return
|
||||
end
|
||||
local nvm = techage.get_nvm(pos)
|
||||
State2:state_button_event(pos, nvm, fields)
|
||||
M(pos):set_string("formspec", formspec(State2, pos, nvm))
|
||||
end
|
||||
|
||||
local function on_receive_fields3(pos, formname, fields, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return
|
||||
end
|
||||
local nvm = techage.get_nvm(pos)
|
||||
State3:state_button_event(pos, nvm, fields)
|
||||
M(pos):set_string("formspec", formspec(State3, pos, nvm))
|
||||
end
|
||||
|
||||
local function on_rightclick2(pos, node, clicker)
|
||||
techage.set_activeformspec(pos, clicker)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
M(pos):set_string("formspec", formspec(State2, pos, nvm))
|
||||
end
|
||||
|
||||
local function on_rightclick3(pos, node, clicker)
|
||||
techage.set_activeformspec(pos, clicker)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
M(pos):set_string("formspec", formspec(State3, pos, nvm))
|
||||
end
|
||||
|
||||
local function after_place_node2(pos)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
State2:node_init(pos, nvm, "")
|
||||
M(pos):set_int("outdir", networks.side_to_outdir(pos, "R"))
|
||||
M(pos):set_string("formspec", formspec(State2, pos, nvm))
|
||||
Axle:after_place_node(pos)
|
||||
end
|
||||
|
||||
local function after_place_node3(pos)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
local number = techage.add_node(pos, "techage:t4_source")
|
||||
State3:node_init(pos, nvm, number)
|
||||
M(pos):set_int("outdir", networks.side_to_outdir(pos, "R"))
|
||||
M(pos):set_string("formspec", formspec(State3, pos, nvm))
|
||||
Cable:after_place_node(pos)
|
||||
end
|
||||
|
||||
local function after_dig_node2(pos, oldnode)
|
||||
Axle:after_dig_node(pos)
|
||||
techage.del_mem(pos)
|
||||
end
|
||||
|
||||
local function after_dig_node3(pos, oldnode)
|
||||
Cable:after_dig_node(pos)
|
||||
techage.del_mem(pos)
|
||||
end
|
||||
|
||||
local function get_generator_data(pos, outdir, tlib2)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
if nvm.running then
|
||||
return {level = (nvm.load or 0) / PWR_PERF, perf = PWR_PERF, capa = PWR_PERF * 2}
|
||||
end
|
||||
end
|
||||
|
||||
minetest.register_node("techage:t2_source", {
|
||||
description = S("Axle Power Source"),
|
||||
tiles = {
|
||||
-- up, down, right, left, back, front
|
||||
"techage_filling_ta2.png^techage_frame_ta2_top.png",
|
||||
"techage_filling_ta2.png^techage_frame_ta2.png",
|
||||
"techage_filling_ta2.png^techage_axle_clutch.png^techage_frame_ta2.png",
|
||||
"techage_filling_ta2.png^techage_frame_ta2.png^techage_appl_source.png",
|
||||
"techage_filling_ta2.png^techage_frame_ta2.png^techage_appl_source.png",
|
||||
"techage_filling_ta2.png^techage_frame_ta2.png^techage_appl_source.png",
|
||||
},
|
||||
paramtype2 = "facedir",
|
||||
groups = {cracky=2, crumbly=2, choppy=2},
|
||||
on_rotate = screwdriver.disallow,
|
||||
is_ground_content = false,
|
||||
on_receive_fields = on_receive_fields2,
|
||||
on_rightclick = on_rightclick2,
|
||||
on_timer = node_timer2,
|
||||
after_place_node = after_place_node2,
|
||||
after_dig_node = after_dig_node2,
|
||||
get_generator_data = get_generator_data,
|
||||
})
|
||||
|
||||
minetest.register_node("techage:t4_source", {
|
||||
description = S("Ele Power Source"),
|
||||
tiles = {
|
||||
-- up, down, right, left, back, front
|
||||
"techage_filling_ta4.png^techage_frame_ta4_top.png",
|
||||
"techage_filling_ta4.png^techage_frame_ta4.png",
|
||||
"techage_filling_ta4.png^techage_appl_hole_electric.png^techage_frame_ta4.png",
|
||||
"techage_filling_ta4.png^techage_frame_ta4.png^techage_appl_source.png",
|
||||
"techage_filling_ta4.png^techage_frame_ta4.png^techage_appl_source.png",
|
||||
"techage_filling_ta4.png^techage_frame_ta4.png^techage_appl_source.png",
|
||||
},
|
||||
paramtype2 = "facedir",
|
||||
groups = {cracky=2, crumbly=2, choppy=2},
|
||||
on_rotate = screwdriver.disallow,
|
||||
is_ground_content = false,
|
||||
on_receive_fields = on_receive_fields3,
|
||||
on_rightclick = on_rightclick3,
|
||||
on_timer = node_timer3,
|
||||
after_place_node = after_place_node3,
|
||||
after_dig_node = after_dig_node3,
|
||||
get_generator_data = get_generator_data,
|
||||
ta3_formspec = techage.generator_settings("ta3", PWR_PERF),
|
||||
})
|
||||
|
||||
power.register_nodes({"techage:t2_source"}, Axle, "gen", {"R"})
|
||||
power.register_nodes({"techage:t4_source"}, Cable, "gen", {"R"})
|
||||
|
||||
techage.register_node({"techage:t4_source"}, {
|
||||
on_recv_message = function(pos, src, topic, payload)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
if topic == "delivered" then
|
||||
return nvm.provided or 0
|
||||
else
|
||||
return State3:on_receive_message(pos, topic, payload)
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
control.register_nodes({"techage:t4_source"}, {
|
||||
on_receive = function(pos, tlib2, topic, payload)
|
||||
end,
|
||||
on_request = function(pos, tlib2, topic)
|
||||
if topic == "info" then
|
||||
local nvm = techage.get_nvm(pos)
|
||||
local meta = M(pos)
|
||||
return {
|
||||
type = S("Ele Power Source"),
|
||||
number = meta:get_string("node_number") or "",
|
||||
running = nvm.running or false,
|
||||
available = PWR_PERF,
|
||||
provided = nvm.provided or 0,
|
||||
termpoint = meta:get_string("termpoint"),
|
||||
}
|
||||
end
|
||||
return false
|
||||
end,
|
||||
}
|
||||
)
|
715
techage/basic_machines/ta4_chest.lua
Normal file
715
techage/basic_machines/ta4_chest.lua
Normal file
@ -0,0 +1,715 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019-2023 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
TA4 8x2000 Chest
|
||||
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local M = minetest.get_meta
|
||||
local S = techage.S
|
||||
|
||||
local DESCRIPTION = S("TA4 8x2000 Chest")
|
||||
local STACK_SIZE = 2000
|
||||
|
||||
local function gen_stack(inv, idx)
|
||||
inv[idx] = {name = "", count = 0}
|
||||
return inv[idx]
|
||||
end
|
||||
|
||||
local function gen_inv(nvm)
|
||||
nvm.inventory = {}
|
||||
for i = 1,8 do
|
||||
gen_stack(nvm.inventory, i)
|
||||
end
|
||||
return nvm.inventory
|
||||
end
|
||||
|
||||
local function repair_inv(nvm)
|
||||
nvm.inventory = nvm.inventory or {}
|
||||
for i = 1,8 do
|
||||
local item = nvm.inventory[i]
|
||||
if not item or type(item) ~= "table"
|
||||
or not item.name or type(item.name) ~= "string" or item.name == ""
|
||||
or not item.count or type(item.count) ~= "number" or item.count < 1
|
||||
then
|
||||
gen_stack(nvm.inventory, i)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function get_stack(nvm, idx)
|
||||
nvm.inventory = nvm.inventory or {}
|
||||
return nvm.inventory[idx] or gen_stack(nvm.inventory, idx)
|
||||
end
|
||||
|
||||
local function get_count(nvm, idx)
|
||||
nvm.inventory = nvm.inventory or {}
|
||||
if idx and idx > 0 then
|
||||
return nvm.inventory[idx] and nvm.inventory[idx].count or 0
|
||||
else
|
||||
local count = 0
|
||||
for _,item in ipairs(nvm.inventory) do
|
||||
count = count + item.count or 0
|
||||
end
|
||||
return count
|
||||
end
|
||||
end
|
||||
|
||||
local function get_itemstring(nvm, idx)
|
||||
if idx and idx > 0 then
|
||||
nvm.inventory = nvm.inventory or {}
|
||||
return nvm.inventory[idx] and nvm.inventory[idx].name or ""
|
||||
end
|
||||
return ""
|
||||
end
|
||||
|
||||
local function inv_empty(nvm)
|
||||
for _,item in ipairs(nvm.inventory or {}) do
|
||||
if item.count and item.count > 0 then
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local function inv_state(nvm)
|
||||
local num = 0
|
||||
for _,item in ipairs(nvm.inventory or {}) do
|
||||
if item.count and item.count > 0 then
|
||||
num = num + 1
|
||||
end
|
||||
end
|
||||
if num == 0 then return "empty" end
|
||||
if num == 8 then return "full" end
|
||||
return "loaded"
|
||||
end
|
||||
|
||||
local function inv_state_num(nvm)
|
||||
local num = 0
|
||||
for _,item in ipairs(nvm.inventory or {}) do
|
||||
if item.count and item.count > 0 then
|
||||
num = num + 1
|
||||
end
|
||||
end
|
||||
if num == 0 then return 0 end
|
||||
if num == 8 then return 2 end
|
||||
return 1
|
||||
end
|
||||
|
||||
local function max_stacksize(item_name)
|
||||
-- It is sufficient to use minetest.registered_items as all registration
|
||||
-- functions (node, craftitems, tools) add the definitions there.
|
||||
local ndef = minetest.registered_items[item_name]
|
||||
-- Return 1 as fallback so that slots with unknown items can be emptied.
|
||||
return ndef and ndef.stack_max or 1
|
||||
end
|
||||
|
||||
local function get_stacksize(pos)
|
||||
local size = M(pos):get_int("stacksize")
|
||||
if size == 0 then
|
||||
return STACK_SIZE
|
||||
end
|
||||
return size
|
||||
end
|
||||
|
||||
-- Returns a boolean that indicates if an itemstack and nvmstack can be combined.
|
||||
-- The second return value is a string describing the reason.
|
||||
-- This function guarantees not to modify any of both stacks.
|
||||
local function doesItemStackMatchNvmStack(itemstack, nvmstack)
|
||||
if itemstack:get_count() == 0 or nvmstack.count == 0 then
|
||||
return true, "Empty stack"
|
||||
end
|
||||
if nvmstack.name and nvmstack.name ~= "" and nvmstack.name ~= itemstack:get_name() then
|
||||
return false, "Mismatching names"
|
||||
end
|
||||
|
||||
-- The following seems to be the most reliable approach to compare meta.
|
||||
local nvm_meta = ItemStack():get_meta()
|
||||
nvm_meta:from_table(minetest.deserialize(nvmstack.meta or ""))
|
||||
if not nvm_meta:equals(itemstack:get_meta()) then
|
||||
return false, "Mismatching meta"
|
||||
end
|
||||
if (nvmstack.wear or 0) ~= itemstack:get_wear() then
|
||||
return false, "Mismatching wear"
|
||||
end
|
||||
return true, "Stacks match"
|
||||
end
|
||||
|
||||
|
||||
-- Generic function for adding items to the 8x2000 Chest
|
||||
-- This function guarantees not to modify the itemstack.
|
||||
-- The number of items that were added to the chest is returned.
|
||||
local function add_to_chest(pos, input_stack, idx)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
local nvm_stack = get_stack(nvm, idx)
|
||||
if input_stack:get_count() == 0 then
|
||||
return 0
|
||||
end
|
||||
if not doesItemStackMatchNvmStack(input_stack, nvm_stack) then
|
||||
return 0
|
||||
end
|
||||
local count = math.min(input_stack:get_count(), get_stacksize(pos) - (nvm_stack.count or 0))
|
||||
if nvm_stack.count == 0 then
|
||||
nvm_stack.name = input_stack:get_name()
|
||||
nvm_stack.meta = minetest.serialize(input_stack:get_meta():to_table())
|
||||
nvm_stack.wear = input_stack:get_wear()
|
||||
end
|
||||
nvm_stack.count = nvm_stack.count + count
|
||||
return count
|
||||
end
|
||||
|
||||
local function stackOrNil(stack)
|
||||
if stack and stack.get_count and stack:get_count() > 0 then
|
||||
return stack
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Generic function for taking items from the 8x2000 Chest
|
||||
-- output_stack is directly modified; but nil can also be supplied.
|
||||
-- The resulting output_stack is returned from the function.
|
||||
-- keep_assignment indicates if the meta information for this function should be considered (manual vs. tubes).
|
||||
local function take_from_chest(pos, idx, output_stack, max_total_count, keep_assignment)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
local nvm_stack = get_stack(nvm, idx)
|
||||
output_stack = output_stack or ItemStack()
|
||||
local assignment_count = keep_assignment and M(pos):get_int("assignment") == 1 and 1 or 0
|
||||
local count = math.min(nvm_stack.count - assignment_count, max_stacksize(nvm_stack.name) - output_stack:get_count())
|
||||
if max_total_count then
|
||||
count = math.min(count, max_total_count - output_stack:get_count())
|
||||
end
|
||||
if count < 1 then
|
||||
return stackOrNil(output_stack)
|
||||
end
|
||||
if not doesItemStackMatchNvmStack(output_stack, nvm_stack) then
|
||||
return stackOrNil(output_stack)
|
||||
end
|
||||
output_stack:add_item(ItemStack({
|
||||
name = nvm_stack.name,
|
||||
count = count,
|
||||
wear = nvm_stack.wear,
|
||||
}))
|
||||
output_stack:get_meta():from_table(minetest.deserialize(nvm_stack.meta or ""))
|
||||
nvm_stack.count = nvm_stack.count - count
|
||||
if nvm_stack.count == 0 then
|
||||
gen_stack(nvm.inventory or {}, idx)
|
||||
end
|
||||
return stackOrNil(output_stack)
|
||||
end
|
||||
|
||||
-- Function for adding items to the 8x2000 Chest via automation, e.g. pushers
|
||||
local function tube_add_to_chest(pos, input_stack)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
nvm.inventory = nvm.inventory or {}
|
||||
|
||||
for idx = 1,8 do
|
||||
input_stack:take_item(add_to_chest(pos, input_stack, idx))
|
||||
end
|
||||
|
||||
if input_stack:get_count() > 0 then
|
||||
return input_stack -- Not all items were added to chest
|
||||
else
|
||||
return true -- All items were added
|
||||
end
|
||||
end
|
||||
|
||||
-- Function for taking items from the 8x2000 Chest via automation, e.g. pushers
|
||||
local function tube_take_from_chest(pos, item_name, count)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
local mem = techage.get_mem(pos)
|
||||
nvm.inventory = nvm.inventory or {}
|
||||
mem.startpos = mem.startpos or 1
|
||||
local prio = M(pos):get_int("priority") == 1
|
||||
local startpos = prio and 8 or mem.startpos
|
||||
local endpos = prio and 1 or mem.startpos + 8
|
||||
local step = prio and -1 or 1
|
||||
local itemstack = ItemStack()
|
||||
for idx = startpos,endpos,step do
|
||||
idx = ((idx - 1) % 8) + 1
|
||||
local nvmstack = get_stack(nvm, idx)
|
||||
if not item_name or item_name == nvmstack.name then
|
||||
take_from_chest(pos, idx, itemstack, count - itemstack:get_count(), true)
|
||||
if itemstack:get_count() == count then
|
||||
mem.startpos = idx + 1
|
||||
return itemstack
|
||||
end
|
||||
end
|
||||
mem.startpos = idx + 1
|
||||
end
|
||||
return stackOrNil(itemstack)
|
||||
end
|
||||
|
||||
-- Function for manually adding items to the 8x2000 Chest via the formspec
|
||||
local function inv_add_to_chest(pos, idx)
|
||||
local inv = M(pos):get_inventory()
|
||||
local inv_stack = inv:get_stack("main", idx)
|
||||
local count = add_to_chest(pos, inv_stack, idx)
|
||||
inv_stack:set_count(inv_stack:get_count() - count)
|
||||
inv:set_stack("main", idx, inv_stack)
|
||||
end
|
||||
|
||||
-- Function for manually taking items from the 8x2000 Chest via the formspec
|
||||
local function inv_take_from_chest(pos, idx)
|
||||
local inv = M(pos):get_inventory()
|
||||
local inv_stack = inv:get_stack("main", idx)
|
||||
if inv_stack:get_count() > 0 then
|
||||
return
|
||||
end
|
||||
local output_stack = take_from_chest(pos, idx)
|
||||
if output_stack then
|
||||
inv:set_stack("main", idx, output_stack)
|
||||
end
|
||||
end
|
||||
|
||||
local function formspec_container(x, y, nvm, inv)
|
||||
local tbl = {"container["..x..","..y.."]"}
|
||||
for i = 1,8 do
|
||||
local xpos = i - 1
|
||||
tbl[#tbl+1] = "box["..(xpos - 0.03)..",0;0.86,0.9;#808080]"
|
||||
local stack = get_stack(nvm, i)
|
||||
if stack.name ~= "" then
|
||||
local itemstack = ItemStack({
|
||||
name = stack.name,
|
||||
count = stack.count,
|
||||
wear = stack.wear,
|
||||
})
|
||||
local stack_meta_table = (minetest.deserialize(stack.meta) or {}).fields or {}
|
||||
for _, key in ipairs({"description", "short_description", "color", "palette_index"}) do
|
||||
if stack_meta_table[key] then
|
||||
itemstack:get_meta():set_string(key, stack_meta_table[key])
|
||||
end
|
||||
end
|
||||
local itemname = itemstack:to_string()
|
||||
--tbl[#tbl+1] = "item_image["..xpos..",1;1,1;"..itemname.."]"
|
||||
tbl[#tbl+1] = techage.item_image(xpos, 0, itemname, stack.count)
|
||||
end
|
||||
if inv:get_stack("main", i):get_count() == 0 then
|
||||
tbl[#tbl+1] = "image_button["..xpos..",1;1,1;techage_form_get_arrow.png;get"..i..";]"
|
||||
else
|
||||
tbl[#tbl+1] = "image_button["..xpos..",1;1,1;techage_form_add_arrow.png;add"..i..";]"
|
||||
end
|
||||
end
|
||||
tbl[#tbl+1] = "list[context;main;0,2;8,1;]"
|
||||
tbl[#tbl+1] = "container_end[]"
|
||||
return table.concat(tbl, "")
|
||||
end
|
||||
|
||||
local function formspec(pos)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
local inv = M(pos):get_inventory()
|
||||
local size = get_stacksize(pos)
|
||||
local assignment = M(pos):get_int("assignment") == 1 and "true" or "false"
|
||||
local priority = M(pos):get_int("priority") == 1 and "true" or "false"
|
||||
return "size[8,8.3]"..
|
||||
default.gui_bg..
|
||||
default.gui_bg_img..
|
||||
default.gui_slots..
|
||||
formspec_container(0, 0, nvm, inv)..
|
||||
"button[0,3.5;3,1;unlock;"..S("Unlock").."]"..
|
||||
"tooltip[0,3.5;3,1;"..S("Unlock connected chest\nif all slots are below 2000")..";#0C3D32;#FFFFFF]"..
|
||||
"label[0,3;"..S("Size")..": 8x"..size.."]"..
|
||||
"checkbox[4,3;assignment;"..S("keep assignment")..";"..assignment.."]"..
|
||||
"tooltip[4,3;2,0.6;"..S("Never completely empty the slots\nwith the pusher to keep the item assignment")..";#0C3D32;#FFFFFF]"..
|
||||
"checkbox[4,3.6;priority;"..S("right to left")..";"..priority.."]"..
|
||||
"tooltip[4,3.6;2,0.6;"..S("Empty the slots always \nfrom right to left")..";#0C3D32;#FFFFFF]"..
|
||||
"list[current_player;main;0,4.6;8,4;]"..
|
||||
"listring[context;main]"..
|
||||
"listring[current_player;main]"
|
||||
end
|
||||
|
||||
local function count_number_of_chests(pos)
|
||||
local node = techage.get_node_lvm(pos)
|
||||
local dir = techage.side_to_outdir("B", node.param2)
|
||||
local pos1 = tubelib2.get_pos(pos, dir)
|
||||
local param2 = node.param2
|
||||
local cnt = 1
|
||||
while cnt < 50 do
|
||||
node = techage.get_node_lvm(pos1)
|
||||
if node.name ~= "techage:ta4_chest_dummy" then
|
||||
break
|
||||
end
|
||||
local meta = M(pos1)
|
||||
if meta:contains("param2") and meta:get_int("param2") ~= param2 then
|
||||
break
|
||||
end
|
||||
pos1 = tubelib2.get_pos(pos1, dir)
|
||||
cnt = cnt + 1
|
||||
end
|
||||
M(pos):set_int("stacksize", STACK_SIZE * cnt)
|
||||
end
|
||||
|
||||
local function dummy_chest_behind(pos, node)
|
||||
local dir = techage.side_to_outdir("B", node.param2)
|
||||
local pos1 = tubelib2.get_pos(pos, dir)
|
||||
node = techage.get_node_lvm(pos1)
|
||||
return node.name == "techage:ta4_chest_dummy"
|
||||
end
|
||||
|
||||
local function part_of_a_chain(pos, node)
|
||||
local dir = techage.side_to_outdir("F", node.param2)
|
||||
local pos1 = tubelib2.get_pos(pos, dir)
|
||||
node = techage.get_node_lvm(pos1)
|
||||
return node.name == "techage:ta4_chest_dummy" or node.name == "techage:ta4_chest"
|
||||
end
|
||||
|
||||
local function search_chest_in_front(pos, node)
|
||||
local dir = techage.side_to_outdir("F", node.param2)
|
||||
local pos1 = tubelib2.get_pos(pos, dir)
|
||||
local param2 = node.param2
|
||||
local cnt = 1
|
||||
while cnt < 50 do
|
||||
node = techage.get_node_lvm(pos1)
|
||||
if node.name ~= "techage:ta4_chest_dummy" then
|
||||
break
|
||||
end
|
||||
local meta = M(pos1)
|
||||
if meta:contains("param2") and meta:get_int("param2") ~= param2 then
|
||||
break
|
||||
end
|
||||
pos1 = tubelib2.get_pos(pos1, dir)
|
||||
cnt = cnt + 1
|
||||
end
|
||||
if node.name == "techage:ta4_chest" and node.param2 == param2 then
|
||||
minetest.after(1, count_number_of_chests, pos1)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
nvm.front_chest_pos = pos1
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function get_front_chest_pos(pos)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
if nvm.front_chest_pos then
|
||||
return nvm.front_chest_pos
|
||||
end
|
||||
|
||||
local node = techage.get_node_lvm(pos)
|
||||
if search_chest_in_front(pos, node) then
|
||||
return nvm.front_chest_pos
|
||||
end
|
||||
|
||||
return pos
|
||||
end
|
||||
|
||||
local function convert_to_chest_again(pos, node, player)
|
||||
local dir = techage.side_to_outdir("B", node.param2)
|
||||
local pos1 = tubelib2.get_pos(pos, dir)
|
||||
local node1 = techage.get_node_lvm(pos1)
|
||||
if minetest.is_protected(pos1, player:get_player_name()) then
|
||||
return
|
||||
end
|
||||
if node1.name == "techage:ta4_chest_dummy" then
|
||||
node1.name = "techage:ta4_chest"
|
||||
minetest.swap_node(pos1, node1)
|
||||
--M(pos1):set_int("disabled", 1)
|
||||
local nvm = techage.get_nvm(pos1)
|
||||
gen_inv(nvm)
|
||||
local number = techage.add_node(pos1, "techage:ta4_chest")
|
||||
M(pos1):set_string("owner", player:get_player_name())
|
||||
M(pos1):set_string("formspec", formspec(pos1))
|
||||
M(pos1):set_string("infotext", DESCRIPTION.." "..number)
|
||||
end
|
||||
end
|
||||
|
||||
local function unlock_chests(pos, player)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
for idx = 1,8 do
|
||||
if get_count(nvm, idx) > STACK_SIZE then return end
|
||||
end
|
||||
local node = techage.get_node_lvm(pos)
|
||||
convert_to_chest_again(pos, node, player)
|
||||
M(pos):set_int("stacksize", STACK_SIZE)
|
||||
end
|
||||
|
||||
local function allow_metadata_inventory_put(pos, listname, index, stack, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return 0
|
||||
end
|
||||
return stack:get_count()
|
||||
end
|
||||
|
||||
local function allow_metadata_inventory_take(pos, listname, index, stack, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return 0
|
||||
end
|
||||
return stack:get_count()
|
||||
end
|
||||
|
||||
local function allow_metadata_inventory_move(pos, from_list, from_index, to_list, to_index, count, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return 0
|
||||
end
|
||||
return count
|
||||
end
|
||||
|
||||
local function on_metadata_inventory_put(pos, listname, index, stack, player)
|
||||
M(pos):set_string("formspec", formspec(pos))
|
||||
techage.set_activeformspec(pos, player)
|
||||
end
|
||||
|
||||
local function on_metadata_inventory_move(pos, from_list, from_index, to_list, to_index, count, player)
|
||||
M(pos):set_string("formspec", formspec(pos))
|
||||
techage.set_activeformspec(pos, player)
|
||||
end
|
||||
|
||||
local function on_metadata_inventory_take(pos, listname, index, stack, player)
|
||||
M(pos):set_string("formspec", formspec(pos))
|
||||
techage.set_activeformspec(pos, player)
|
||||
end
|
||||
|
||||
local function on_rightclick(pos, node, clicker)
|
||||
if M(pos):get_int("disabled") ~= 1 then
|
||||
local nvm = techage.get_nvm(pos)
|
||||
repair_inv(nvm)
|
||||
M(pos):set_string("formspec", formspec(pos))
|
||||
techage.set_activeformspec(pos, clicker)
|
||||
end
|
||||
end
|
||||
|
||||
local function on_receive_fields(pos, formname, fields, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return
|
||||
end
|
||||
|
||||
for i = 1,8 do
|
||||
if fields["get"..i] ~= nil then
|
||||
inv_take_from_chest(pos, i)
|
||||
break
|
||||
elseif fields["add"..i] ~= nil then
|
||||
inv_add_to_chest(pos, i)
|
||||
break
|
||||
end
|
||||
end
|
||||
if fields.unlock then
|
||||
unlock_chests(pos, player)
|
||||
end
|
||||
if fields.assignment then
|
||||
M(pos):set_int("assignment", fields.assignment == "true" and 1 or 0)
|
||||
end
|
||||
if fields.priority then
|
||||
M(pos):set_int("priority", fields.priority == "true" and 1 or 0)
|
||||
end
|
||||
|
||||
M(pos):set_string("formspec", formspec(pos))
|
||||
end
|
||||
|
||||
local function can_dig(pos, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return false
|
||||
end
|
||||
local inv = minetest.get_meta(pos):get_inventory()
|
||||
local nvm = techage.get_nvm(pos)
|
||||
return inv:is_empty("main") and inv_empty(nvm)
|
||||
end
|
||||
|
||||
local function on_rotate(pos, node, user, mode, new_param2)
|
||||
if get_stacksize(pos) == STACK_SIZE then
|
||||
return screwdriver.rotate_simple(pos, node, user, mode, new_param2)
|
||||
else
|
||||
return screwdriver.disallow(pos, node, user, mode, new_param2)
|
||||
end
|
||||
end
|
||||
|
||||
local function after_dig_node(pos, oldnode, oldmetadata, digger)
|
||||
techage.remove_node(pos, oldnode, oldmetadata)
|
||||
convert_to_chest_again(pos, oldnode, digger)
|
||||
end
|
||||
|
||||
minetest.register_node("techage:ta4_chest", {
|
||||
description = DESCRIPTION,
|
||||
tiles = {
|
||||
-- up, down, right, left, back, front
|
||||
"techage_filling_ta4.png^techage_frame_ta4_top.png",
|
||||
"techage_filling_ta4.png^techage_frame_ta4.png",
|
||||
"techage_filling_ta4.png^techage_frame_ta4.png^techage_appl_chest_back_ta4.png",
|
||||
"techage_filling_ta4.png^techage_frame_ta4.png^techage_appl_chest_back_ta4.png",
|
||||
"techage_filling_ta4.png^techage_frame_ta4.png^techage_appl_chest_back_ta4.png",
|
||||
"techage_filling_ta4.png^techage_frame_ta4.png^techage_appl_chest_front_ta4.png^techage_appl_warehouse.png",
|
||||
},
|
||||
|
||||
on_construct = function(pos)
|
||||
local inv = M(pos):get_inventory()
|
||||
inv:set_size('main', 8)
|
||||
end,
|
||||
|
||||
after_place_node = function(pos, placer)
|
||||
local node = minetest.get_node(pos)
|
||||
if dummy_chest_behind(pos, node) then
|
||||
minetest.remove_node(pos)
|
||||
return true
|
||||
end
|
||||
if search_chest_in_front(pos, node) then
|
||||
node.name = "techage:ta4_chest_dummy"
|
||||
minetest.swap_node(pos, node)
|
||||
M(pos):set_int("param2", node.param2)
|
||||
else
|
||||
local nvm = techage.get_nvm(pos)
|
||||
gen_inv(nvm)
|
||||
local number = techage.add_node(pos, "techage:ta4_chest")
|
||||
M(pos):set_string("owner", placer:get_player_name())
|
||||
M(pos):set_string("formspec", formspec(pos))
|
||||
M(pos):set_string("infotext", DESCRIPTION.." "..number)
|
||||
end
|
||||
end,
|
||||
|
||||
techage_set_numbers = function(pos, numbers, player_name)
|
||||
return techage.logic.set_numbers(pos, numbers, player_name, DESCRIPTION)
|
||||
end,
|
||||
|
||||
on_rotate = on_rotate,
|
||||
on_rightclick = on_rightclick,
|
||||
on_receive_fields = on_receive_fields,
|
||||
can_dig = can_dig,
|
||||
after_dig_node = after_dig_node,
|
||||
allow_metadata_inventory_put = allow_metadata_inventory_put,
|
||||
allow_metadata_inventory_take = allow_metadata_inventory_take,
|
||||
|
||||
on_metadata_inventory_put = on_metadata_inventory_put,
|
||||
on_metadata_inventory_move = on_metadata_inventory_move,
|
||||
on_metadata_inventory_take = on_metadata_inventory_take,
|
||||
|
||||
paramtype2 = "facedir",
|
||||
groups = {choppy=2, cracky=2, crumbly=2},
|
||||
is_ground_content = false,
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
})
|
||||
|
||||
minetest.register_node("techage:ta4_chest_dummy", {
|
||||
description = DESCRIPTION,
|
||||
tiles = {
|
||||
-- up, down, right, left, back, front
|
||||
"techage_filling_ta4.png^techage_frame_ta4_top.png",
|
||||
"techage_filling_ta4.png^techage_frame_ta4.png",
|
||||
"techage_filling_ta4.png^techage_frame_ta4.png^techage_appl_chest_back_ta4.png",
|
||||
"techage_filling_ta4.png^techage_frame_ta4.png^techage_appl_chest_back_ta4.png",
|
||||
"techage_filling_ta4.png^techage_frame_ta4.png^techage_appl_chest_back_ta4.png",
|
||||
"techage_filling_ta4.png^techage_frame_ta4.png^techage_appl_chest_front_ta4.png^techage_appl_warehouse.png",
|
||||
},
|
||||
|
||||
on_rightclick = function(pos, node, clicker)
|
||||
end,
|
||||
paramtype2 = "facedir",
|
||||
diggable = false,
|
||||
groups = {not_in_creative_inventory = 1},
|
||||
is_ground_content = false,
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
})
|
||||
|
||||
|
||||
techage.register_node({"techage:ta4_chest"}, {
|
||||
on_pull_item = function(pos, in_dir, num, item_name)
|
||||
local res = tube_take_from_chest(pos, item_name, num)
|
||||
if techage.is_activeformspec(pos) then
|
||||
M(pos):set_string("formspec", formspec(pos))
|
||||
end
|
||||
return res
|
||||
end,
|
||||
on_push_item = function(pos, in_dir, stack)
|
||||
local res = tube_add_to_chest(pos, stack)
|
||||
if techage.is_activeformspec(pos) then
|
||||
M(pos):set_string("formspec", formspec(pos))
|
||||
end
|
||||
return res
|
||||
end,
|
||||
on_unpull_item = function(pos, in_dir, stack)
|
||||
local res = tube_add_to_chest(pos, stack)
|
||||
if techage.is_activeformspec(pos) then
|
||||
M(pos):set_string("formspec", formspec(pos))
|
||||
end
|
||||
return res
|
||||
end,
|
||||
|
||||
on_recv_message = function(pos, src, topic, payload)
|
||||
if topic == "count" then
|
||||
local nvm = techage.get_nvm(pos)
|
||||
return get_count(nvm, tonumber(payload or 0) or 0)
|
||||
elseif topic == "itemstring" then
|
||||
local nvm = techage.get_nvm(pos)
|
||||
return get_itemstring(nvm, tonumber(payload or 0) or 0)
|
||||
elseif topic == "storesize" then
|
||||
return get_stacksize(pos)
|
||||
elseif topic == "state" then
|
||||
local nvm = techage.get_nvm(pos)
|
||||
return inv_state(nvm)
|
||||
else
|
||||
return "unsupported"
|
||||
end
|
||||
end,
|
||||
on_beduino_request_data = function(pos, src, topic, payload)
|
||||
if topic == 140 and payload[1] == 1 then -- Inventory Item Count
|
||||
local nvm = techage.get_nvm(pos)
|
||||
return 0, {get_count(nvm, tonumber(payload[2] or 0) or 0)}
|
||||
elseif topic == 140 and payload[1] == 2 then -- Inventory Item Name
|
||||
local nvm = techage.get_nvm(pos)
|
||||
return 0, get_itemstring(nvm, tonumber(payload[2] or 0) or 0)
|
||||
elseif topic == 140 and payload[1] == 3 then -- storesize
|
||||
return 0, {get_stacksize(pos)}
|
||||
elseif topic == 131 then -- Chest State
|
||||
local nvm = techage.get_nvm(pos)
|
||||
return 0, {inv_state_num(nvm)}
|
||||
else
|
||||
return 2, ""
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
techage.register_node({"techage:ta4_chest_dummy"}, {
|
||||
on_pull_item = function(pos, in_dir, num, item_name)
|
||||
local fc_pos = get_front_chest_pos(pos)
|
||||
local res = tube_take_from_chest(fc_pos, item_name, num)
|
||||
if techage.is_activeformspec(fc_pos) then
|
||||
M(fc_pos):set_string("formspec", formspec(fc_pos))
|
||||
end
|
||||
return res
|
||||
end,
|
||||
on_push_item = function(pos, in_dir, stack)
|
||||
local fc_pos = get_front_chest_pos(pos)
|
||||
local res = tube_add_to_chest(fc_pos, stack)
|
||||
if techage.is_activeformspec(fc_pos) then
|
||||
M(fc_pos):set_string("formspec", formspec(fc_pos))
|
||||
end
|
||||
return res
|
||||
end,
|
||||
on_unpull_item = function(pos, in_dir, stack)
|
||||
local fc_pos = get_front_chest_pos(pos)
|
||||
local res = tube_add_to_chest(fc_pos, stack)
|
||||
if techage.is_activeformspec(fc_pos) then
|
||||
M(fc_pos):set_string("formspec", formspec(fc_pos))
|
||||
end
|
||||
return res
|
||||
end
|
||||
})
|
||||
|
||||
minetest.register_lbm({
|
||||
label = "Repair Dummy Chests",
|
||||
name = "techage:chest_dummy",
|
||||
nodenames = {"techage:ta4_chest_dummy"},
|
||||
run_at_every_load = true,
|
||||
action = function(pos, node)
|
||||
if not part_of_a_chain(pos, node) then
|
||||
minetest.swap_node(pos, {name = "techage:ta4_chest", param2 = node.param2})
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
type = "shapeless",
|
||||
output = "techage:ta4_chest",
|
||||
recipe = {"techage:chest_ta4"}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
type = "shapeless",
|
||||
output = "techage:chest_ta4",
|
||||
recipe = {"techage:ta4_chest"}
|
||||
})
|
316
techage/basic_machines/ta4_injector.lua
Normal file
316
techage/basic_machines/ta4_injector.lua
Normal file
@ -0,0 +1,316 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019-2022 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
TA4 Injector
|
||||
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local M = minetest.get_meta
|
||||
local S = techage.S
|
||||
|
||||
-- Consumer Related Data
|
||||
local CRD = function(pos) return (minetest.registered_nodes[techage.get_node_lvm(pos).name] or {}).consumer end
|
||||
local tooltip = S("Switch to pull mode \nto pull items out of inventory slots \naccording the injector configuration")
|
||||
local Tube = techage.Tube
|
||||
|
||||
local STANDBY_TICKS = 2
|
||||
local COUNTDOWN_TICKS = 3
|
||||
local CYCLE_TIME = 4
|
||||
|
||||
local function formspec(self, pos, nvm)
|
||||
local pull_mode = dump(nvm.pull_mode or false)
|
||||
return "size[8,7.2]"..
|
||||
default.gui_bg..
|
||||
default.gui_bg_img..
|
||||
default.gui_slots..
|
||||
"box[0,-0.1;7.8,0.5;#c6e8ff]"..
|
||||
"label[3,-0.1;"..minetest.colorize("#000000", S("Injector")).."]"..
|
||||
techage.question_mark_help(8, S("Configure up to 8 items \nto be pushed by the injector"))..
|
||||
"list[context;filter;0,0.8;8,1;]"..
|
||||
"image_button[2,2;1,1;".. self:get_state_button_image(nvm) ..";state_button;]"..
|
||||
"tooltip[2,2;1,1;"..self:get_state_tooltip(nvm).."]"..
|
||||
"checkbox[3.5,1.9;pull_mode;"..S("pull mode")..";"..pull_mode.."]"..
|
||||
"tooltip[3.5,1.9;2,0.8;"..tooltip..";#0C3D32;#FFFFFF]"..
|
||||
"list[current_player;main;0,3.5;8,4;]"..
|
||||
"listring[context;filter]"..
|
||||
"listring[current_player;main]"
|
||||
end
|
||||
|
||||
local function allow_metadata_inventory_put(pos, listname, index, stack, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return 0
|
||||
end
|
||||
|
||||
local nvm = techage.get_nvm(pos)
|
||||
if CRD(pos).State:get_state(nvm) ~= techage.STOPPED then
|
||||
return 0
|
||||
end
|
||||
|
||||
local inv = M(pos):get_inventory()
|
||||
local list = inv:get_list(listname)
|
||||
local cdr = CRD(pos)
|
||||
if list[index]:get_count() < cdr.num_items then
|
||||
local num = math.min(cdr.num_items - list[index]:get_count(), stack:get_count()) + list[index]:get_count()
|
||||
stack:set_count(num)
|
||||
inv:set_stack(listname, index, stack)
|
||||
return 0
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
local function allow_metadata_inventory_take(pos, listname, index, stack, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return 0
|
||||
end
|
||||
|
||||
local nvm = techage.get_nvm(pos)
|
||||
if CRD(pos).State:get_state(nvm) ~= techage.STOPPED then
|
||||
return 0
|
||||
end
|
||||
|
||||
local inv = M(pos):get_inventory()
|
||||
inv:set_stack(listname, index, nil)
|
||||
return 0
|
||||
end
|
||||
|
||||
local function pull_items(pos, out_dir, idx, name, num)
|
||||
local inv, listname = techage.get_inv_access(pos, out_dir, "pull")
|
||||
if inv and listname then
|
||||
if idx and idx ~= 0 then
|
||||
local stack = inv:get_stack(listname, idx)
|
||||
if stack and not stack:is_empty() and stack:get_name() == name then
|
||||
local taken = stack:take_item(num)
|
||||
inv:set_stack(listname, idx, stack)
|
||||
return (taken:get_count() > 0) and taken or nil
|
||||
end
|
||||
else
|
||||
local taken = inv:remove_item(listname, {name = name, count = num})
|
||||
return (taken:get_count() > 0) and taken or nil
|
||||
end
|
||||
else
|
||||
return techage.pull_items(pos, out_dir, num, name)
|
||||
end
|
||||
end
|
||||
|
||||
local function push_items(pos, out_dir, idx, items)
|
||||
local inv, listname, callafter, dpos = techage.get_inv_access(pos, out_dir, "push")
|
||||
if inv and listname then
|
||||
if idx and idx ~= 0 then
|
||||
local stack = inv:get_stack(listname, idx)
|
||||
if stack:item_fits(items) then
|
||||
stack:add_item(items)
|
||||
inv:set_stack(listname, idx, stack)
|
||||
if callafter then
|
||||
callafter(dpos)
|
||||
end
|
||||
return true
|
||||
end
|
||||
else
|
||||
if inv:room_for_item(listname, items) then
|
||||
inv:add_item(listname, items)
|
||||
if callafter then
|
||||
callafter(dpos)
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
else
|
||||
return techage.push_items(pos, out_dir, items, idx)
|
||||
end
|
||||
end
|
||||
|
||||
local function unpull_items(pos, out_dir, idx, items)
|
||||
local inv, listname = techage.get_inv_access(pos, out_dir, "unpull")
|
||||
if inv and listname then
|
||||
if idx and idx ~= 0 then
|
||||
local stack = inv:get_stack(listname, idx)
|
||||
stack:add_item(items)
|
||||
inv:set_stack(listname, idx, stack)
|
||||
else
|
||||
inv:add_item(listname, items)
|
||||
end
|
||||
else
|
||||
techage.unpull_items(pos, out_dir, items)
|
||||
end
|
||||
end
|
||||
|
||||
local function pushing(pos, crd, meta, nvm)
|
||||
local pull_dir = meta:get_int("pull_dir")
|
||||
local push_dir = meta:get_int("push_dir")
|
||||
local inv = M(pos):get_inventory()
|
||||
local filter = inv:get_list("filter")
|
||||
local pushed = false
|
||||
local pulled = false
|
||||
|
||||
for idx, item in ipairs(filter) do
|
||||
local name = item:get_name()
|
||||
local num = math.min(item:get_count(), crd.num_items)
|
||||
if name ~= "" and num > 0 then
|
||||
local items = pull_items(pos, pull_dir, nvm.pull_mode and idx, name, num)
|
||||
if items ~= nil then
|
||||
pulled = true
|
||||
if push_items(pos, push_dir, not nvm.pull_mode and idx, items) then
|
||||
pushed = true
|
||||
else -- place item back
|
||||
unpull_items(pos, pull_dir, nvm.pull_mode and idx, items)
|
||||
pulled = false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not pulled then
|
||||
crd.State:idle(pos, nvm)
|
||||
elseif not pushed then
|
||||
crd.State:blocked(pos, nvm)
|
||||
else
|
||||
crd.State:keep_running(pos, nvm, COUNTDOWN_TICKS)
|
||||
end
|
||||
end
|
||||
|
||||
local function node_timer(pos, elapsed)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
local crd = CRD(pos)
|
||||
pushing(pos, crd, M(pos), nvm)
|
||||
end
|
||||
|
||||
local function on_receive_fields(pos, formname, fields, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return
|
||||
end
|
||||
local nvm = techage.get_nvm(pos)
|
||||
if fields.pull_mode then
|
||||
nvm.pull_mode = fields.pull_mode == "true"
|
||||
end
|
||||
CRD(pos).State:state_button_event(pos, nvm, fields)
|
||||
M(pos):set_string("formspec", formspec(CRD(pos).State, pos, nvm))
|
||||
end
|
||||
|
||||
local tiles = {}
|
||||
-- '#' will be replaced by the stage number
|
||||
-- '{power}' will be replaced by the power PNG
|
||||
tiles.pas = {
|
||||
"techage_filling_ta#.png^techage_frame_ta#_top.png^techage_appl_arrow.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_arrow.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_outp.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_inp.png",
|
||||
"techage_appl_pusher.png^[transformR180]^techage_frame_ta#.png^techage_appl_injector.png",
|
||||
"techage_appl_pusher.png^techage_frame_ta#.png^techage_appl_injector.png",
|
||||
}
|
||||
tiles.act = {
|
||||
-- up, down, right, left, back, front
|
||||
"techage_filling_ta#.png^techage_frame_ta#_top.png^techage_appl_arrow.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_arrow.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_outp.png",
|
||||
"techage_filling_ta#.png^techage_frame_ta#.png^techage_appl_inp.png",
|
||||
{
|
||||
name = "techage_appl_pusher14.png^[transformR180]^techage_frame14_ta#.png^techage_appl_injector14.png",
|
||||
backface_culling = false,
|
||||
animation = {
|
||||
type = "vertical_frames",
|
||||
aspect_w = 32,
|
||||
aspect_h = 32,
|
||||
length = 2.0,
|
||||
},
|
||||
},
|
||||
{
|
||||
name = "techage_appl_pusher14.png^techage_frame14_ta#.png^techage_appl_injector14.png",
|
||||
backface_culling = false,
|
||||
animation = {
|
||||
type = "vertical_frames",
|
||||
aspect_w = 32,
|
||||
aspect_h = 32,
|
||||
length = 2.0,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
local tubing = {
|
||||
-- push item through the injector in opposit direction
|
||||
on_push_item = function(pos, in_dir, stack)
|
||||
return in_dir == M(pos):get_int("pull_dir") and techage.safe_push_items(pos, in_dir, stack)
|
||||
end,
|
||||
is_pusher = true, -- is a pulling/pushing node
|
||||
|
||||
on_recv_message = function(pos, src, topic, payload)
|
||||
return CRD(pos).State:on_receive_message(pos, topic, payload)
|
||||
end,
|
||||
on_beduino_receive_cmnd = function(pos, src, topic, payload)
|
||||
return CRD(pos).State:on_beduino_receive_cmnd(pos, topic, payload)
|
||||
end,
|
||||
on_beduino_request_data = function(pos, src, topic, payload)
|
||||
return CRD(pos).State:on_beduino_request_data(pos, topic, payload)
|
||||
end,
|
||||
on_node_load = function(pos)
|
||||
CRD(pos).State:on_node_load(pos)
|
||||
end,
|
||||
}
|
||||
|
||||
local _, node_name_ta3, node_name_ta4 =
|
||||
techage.register_consumer("injector", S("Injector"), tiles, {
|
||||
cycle_time = CYCLE_TIME,
|
||||
standby_ticks = STANDBY_TICKS,
|
||||
formspec = formspec,
|
||||
tubing = tubing,
|
||||
quick_start = node_timer,
|
||||
after_place_node = function(pos, placer)
|
||||
local meta = M(pos)
|
||||
local node = minetest.get_node(pos)
|
||||
meta:set_int("pull_dir", techage.side_to_outdir("L", node.param2))
|
||||
meta:set_int("push_dir", techage.side_to_outdir("R", node.param2))
|
||||
local inv = M(pos):get_inventory()
|
||||
inv:set_size('filter', 8)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
M(pos):set_string("formspec", formspec(CRD(pos).State, pos, nvm))
|
||||
end,
|
||||
ta_rotate_node = function(pos, node, new_param2)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
if CRD(pos).State:get_state(nvm) == techage.STOPPED then
|
||||
Tube:after_dig_node(pos)
|
||||
minetest.swap_node(pos, {name = node.name, param2 = new_param2})
|
||||
Tube:after_place_node(pos)
|
||||
local meta = M(pos)
|
||||
meta:set_int("pull_dir", techage.side_to_outdir("L", new_param2))
|
||||
meta:set_int("push_dir", techage.side_to_outdir("R", new_param2))
|
||||
M(pos):set_string("formspec", formspec(CRD(pos).State, pos, nvm))
|
||||
end
|
||||
end,
|
||||
|
||||
allow_metadata_inventory_put = allow_metadata_inventory_put,
|
||||
allow_metadata_inventory_take = allow_metadata_inventory_take,
|
||||
allow_metadata_inventory_move = function() return 0 end,
|
||||
on_receive_fields = on_receive_fields,
|
||||
node_timer = node_timer,
|
||||
on_rotate = screwdriver.disallow,
|
||||
|
||||
groups = {choppy=2, cracky=2, crumbly=2},
|
||||
is_ground_content = false,
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
num_items = {0,0,1,4},
|
||||
}, {false, false, true, true})
|
||||
|
||||
minetest.register_craft({
|
||||
output = node_name_ta3,
|
||||
recipe = {
|
||||
{"", "default:steel_ingot", ""},
|
||||
{"", "techage:ta3_pusher_pas", ""},
|
||||
{"", "basic_materials:ic", ""},
|
||||
},
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = node_name_ta4,
|
||||
recipe = {
|
||||
{"", "techage:aluminum", ""},
|
||||
{"", "techage:ta4_pusher_pas", ""},
|
||||
{"", "basic_materials:ic", ""},
|
||||
},
|
||||
})
|
248
techage/basic_machines/ta5_chest.lua
Normal file
248
techage/basic_machines/ta5_chest.lua
Normal file
@ -0,0 +1,248 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019-2023 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
TA5 Hyperloop Chest
|
||||
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local S2P = minetest.string_to_pos
|
||||
local P2S = minetest.pos_to_string
|
||||
local M = minetest.get_meta
|
||||
local S = techage.S
|
||||
|
||||
local TA4_INV_SIZE = 32
|
||||
local EX_POINTS = 15
|
||||
|
||||
local hyperloop = techage.hyperloop
|
||||
local remote_pos = techage.hyperloop.remote_pos
|
||||
local shared_inv = techage.shared_inv
|
||||
local menu = techage.menu
|
||||
|
||||
local function formspec(pos)
|
||||
local ndef = minetest.registered_nodes["techage:ta5_hl_chest"]
|
||||
local status = M(pos):get_string("conn_status")
|
||||
if hyperloop.is_server(pos) then
|
||||
local title = ndef.description .. " " .. status
|
||||
return "size[8,9]"..
|
||||
"box[0,-0.1;7.8,0.5;#c6e8ff]" ..
|
||||
"label[0.2,-0.1;" .. minetest.colorize( "#000000", title) .. "]" ..
|
||||
"list[context;main;0,1;8,4;]"..
|
||||
"list[current_player;main;0,5.3;8,4;]"..
|
||||
"listring[context;main]"..
|
||||
"listring[current_player;main]"
|
||||
elseif hyperloop.is_client(pos) then
|
||||
local title = ndef.description .. " " .. status
|
||||
return "size[8,9]"..
|
||||
"box[0,-0.1;7.8,0.5;#c6e8ff]" ..
|
||||
"label[0.2,-0.1;" .. minetest.colorize( "#000000", title) .. "]" ..
|
||||
"label[0.2,2;Inventory access on this node is disabled\ndue to minetest engine issues!]" ..
|
||||
"list[current_player;main;0,5.3;8,4;]"
|
||||
else
|
||||
return menu.generate_formspec(pos, ndef, hyperloop.SUBMENU)
|
||||
end
|
||||
end
|
||||
|
||||
local function allow_metadata_inventory_put(pos, listname, index, stack, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return 0
|
||||
end
|
||||
if techage.hyperloop.is_client(pos) then
|
||||
return 0
|
||||
end
|
||||
shared_inv.before_inv_access(pos, listname)
|
||||
local inv = minetest.get_inventory({type="node", pos=pos})
|
||||
if inv and inv:room_for_item(listname, stack) then
|
||||
return stack:get_count()
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
local function allow_metadata_inventory_take(pos, listname, index, stack, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return 0
|
||||
end
|
||||
if techage.hyperloop.is_client(pos) then
|
||||
return 0
|
||||
end
|
||||
shared_inv.before_inv_access(pos, listname)
|
||||
local inv = minetest.get_inventory({type="node", pos=pos})
|
||||
if inv and inv:contains_item(listname, stack) then
|
||||
return stack:get_count()
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
local function allow_metadata_inventory_move(pos, from_list, from_index, to_list, to_index, count, player)
|
||||
if shared_inv.before_inv_access(pos, "main") then
|
||||
return 0
|
||||
end
|
||||
if techage.hyperloop.is_client(pos) then
|
||||
return 0
|
||||
end
|
||||
return count
|
||||
end
|
||||
|
||||
minetest.register_node("techage:ta5_hl_chest", {
|
||||
description = S("TA5 Hyperloop Chest"),
|
||||
tiles = {
|
||||
-- up, down, right, left, back, front
|
||||
"techage_filling_ta4.png^techage_frame_ta5_top.png",
|
||||
"techage_filling_ta4.png^techage_frame_ta5.png",
|
||||
"techage_filling_ta4.png^techage_frame_ta5.png^techage_appl_chest_back_ta4.png",
|
||||
"techage_filling_ta4.png^techage_frame_ta5.png^techage_appl_chest_back_ta4.png",
|
||||
"techage_filling_ta4.png^techage_frame_ta5.png^techage_appl_chest_back_ta4.png",
|
||||
"techage_filling_ta4.png^techage_frame_ta5.png^techage_appl_chest_front_ta4.png",
|
||||
},
|
||||
after_place_node = function(pos, placer)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
inv:set_size('main', 32)
|
||||
local number = techage.add_node(pos, "techage:ta5_hl_chest")
|
||||
meta:set_string("node_number", number)
|
||||
meta:set_string("owner", placer:get_player_name())
|
||||
meta:set_string("formspec", formspec(pos))
|
||||
meta:set_string("infotext", S("TA5 Hyperloop Chest").." "..number)
|
||||
hyperloop.after_place_node(pos, placer, "chest")
|
||||
end,
|
||||
on_receive_fields = function(pos, formname, fields, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return
|
||||
end
|
||||
if techage.get_expoints(player) >= EX_POINTS then
|
||||
if techage.menu.eval_input(pos, hyperloop.SUBMENU, fields) then
|
||||
hyperloop.after_formspec(pos, fields)
|
||||
shared_inv.on_rightclick(pos, player, "main")
|
||||
M(pos):set_string("formspec", formspec(pos))
|
||||
end
|
||||
end
|
||||
end,
|
||||
on_timer = shared_inv.node_timer,
|
||||
on_rightclick = function(pos, node, clicker)
|
||||
shared_inv.on_rightclick(pos, clicker, "main")
|
||||
M(pos):set_string("formspec", formspec(pos))
|
||||
end,
|
||||
can_dig = function(pos, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return false
|
||||
end
|
||||
shared_inv.before_inv_access(pos, "main")
|
||||
local inv = minetest.get_meta(pos):get_inventory()
|
||||
return inv:is_empty("main")
|
||||
end,
|
||||
after_dig_node = function(pos, oldnode, oldmetadata, digger)
|
||||
techage.remove_node(pos, oldnode, oldmetadata)
|
||||
hyperloop.after_dig_node(pos, oldnode, oldmetadata, digger)
|
||||
techage.del_mem(pos)
|
||||
end,
|
||||
allow_metadata_inventory_put = allow_metadata_inventory_put,
|
||||
allow_metadata_inventory_take = allow_metadata_inventory_take,
|
||||
allow_metadata_inventory_move = allow_metadata_inventory_move,
|
||||
on_metadata_inventory_put = shared_inv.after_inv_access,
|
||||
on_metadata_inventory_take = shared_inv.after_inv_access,
|
||||
paramtype2 = "facedir",
|
||||
groups = {choppy=2, cracky=2, crumbly=2},
|
||||
is_ground_content = false,
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
})
|
||||
|
||||
|
||||
techage.register_node({"techage:ta5_hl_chest"}, {
|
||||
on_inv_request = function(pos, in_dir, access_type)
|
||||
pos = remote_pos(pos)
|
||||
if pos then
|
||||
local meta = minetest.get_meta(pos)
|
||||
if meta then
|
||||
return meta:get_inventory(), "main"
|
||||
end
|
||||
end
|
||||
end,
|
||||
on_pull_item = function(pos, in_dir, num, item_name)
|
||||
pos = remote_pos(pos)
|
||||
if pos then
|
||||
local meta = minetest.get_meta(pos)
|
||||
if meta then
|
||||
local inv = meta:get_inventory()
|
||||
if inv then
|
||||
return techage.get_items(pos, inv, "main", num)
|
||||
end
|
||||
end
|
||||
end
|
||||
return false
|
||||
end,
|
||||
on_push_item = function(pos, in_dir, stack)
|
||||
if techage.hyperloop.is_paired(pos) then
|
||||
pos = remote_pos(pos)
|
||||
if pos then
|
||||
local meta = minetest.get_meta(pos)
|
||||
if meta then
|
||||
local inv = meta:get_inventory()
|
||||
if inv then
|
||||
return techage.put_items(inv, "main", stack)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return false
|
||||
end,
|
||||
on_unpull_item = function(pos, in_dir, stack)
|
||||
pos = remote_pos(pos)
|
||||
if pos then
|
||||
local meta = minetest.get_meta(pos)
|
||||
if meta then
|
||||
local inv = meta:get_inventory()
|
||||
if inv then
|
||||
return techage.put_items(inv, "main", stack)
|
||||
end
|
||||
end
|
||||
end
|
||||
return false
|
||||
end,
|
||||
on_recv_message = function(pos, src, topic, payload)
|
||||
if topic == "state" then
|
||||
local meta = minetest.get_meta(pos)
|
||||
if meta then
|
||||
local inv = meta:get_inventory()
|
||||
if inv then
|
||||
return techage.get_inv_state(inv, "main")
|
||||
end
|
||||
end
|
||||
return "error"
|
||||
else
|
||||
return "unsupported"
|
||||
end
|
||||
end,
|
||||
on_beduino_request_data = function(pos, src, topic, payload)
|
||||
if topic == 131 then -- Chest State
|
||||
local meta = minetest.get_meta(pos)
|
||||
if meta then
|
||||
local inv = meta:get_inventory()
|
||||
if inv then
|
||||
return 0, {techage.get_inv_state_num(inv, "main")}
|
||||
end
|
||||
end
|
||||
else
|
||||
return 2, ""
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
|
||||
minetest.register_craft({
|
||||
type = "shapeless",
|
||||
output = "techage:ta5_hl_chest",
|
||||
recipe = {"techage:chest_ta4", "techage:ta5_aichip"}
|
||||
})
|
||||
|
||||
minetest.register_on_mods_loaded(function()
|
||||
if not minetest.global_exists("hyperloop") then
|
||||
minetest.clear_craft({output = "techage:ta5_hl_chest"})
|
||||
end
|
||||
end)
|
242
techage/basis/assemble.lua
Normal file
242
techage/basis/assemble.lua
Normal file
@ -0,0 +1,242 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019-2020 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
Assemble routines
|
||||
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local P = minetest.string_to_pos
|
||||
local M = minetest.get_meta
|
||||
local S = techage.S
|
||||
|
||||
techage.assemble = {}
|
||||
|
||||
local Face2Dir = {[0]=
|
||||
{x=0, y=0, z=1},
|
||||
{x=1, y=0, z=0},
|
||||
{x=0, y=0, z=-1},
|
||||
{x=-1, y=0, z=0},
|
||||
{x=0, y=-1, z=0},
|
||||
{x=0, y=1, z=0}
|
||||
}
|
||||
|
||||
-- Determine the destination position based on the base position,
|
||||
-- param2, and a route table like : {0,3}
|
||||
-- 0 = forward, 1 = right, 2 = backward, 3 = left
|
||||
local function dest_pos(pos, param2, route, y_offs)
|
||||
local p2 = param2
|
||||
local pos1 = {x=pos.x, y=pos.y+y_offs, z=pos.z}
|
||||
for _,dir in ipairs(route) do
|
||||
p2 = (param2 + dir) % 4
|
||||
pos1 = vector.add(pos1, Face2Dir[p2])
|
||||
end
|
||||
return pos1, p2
|
||||
end
|
||||
|
||||
|
||||
-- timer based function
|
||||
local function build(pos, param2, AssemblyPlan, idx)
|
||||
local item = AssemblyPlan[idx]
|
||||
if item ~= nil then
|
||||
local y, path, fd_offs, node_name = item[1], item[2], item[3], item[4]
|
||||
local pos1 = dest_pos(pos, param2, path, y)
|
||||
minetest.add_node(pos1, {name=node_name, param2=(param2 + fd_offs) % 4})
|
||||
minetest.after(0.5, build, pos, param2, AssemblyPlan, idx+1)
|
||||
else
|
||||
local nvm = techage.get_nvm(pos)
|
||||
nvm.assemble_locked = false
|
||||
end
|
||||
end
|
||||
|
||||
-- timer based function
|
||||
local function remove(pos, param2, AssemblyPlan, idx)
|
||||
local item = AssemblyPlan[idx]
|
||||
if item ~= nil then
|
||||
local y, path = item[1], item[2]
|
||||
local pos1 = dest_pos(pos, param2, path, y)
|
||||
minetest.remove_node(pos1)
|
||||
minetest.after(0.5, remove, pos, param2, AssemblyPlan, idx-1)
|
||||
else
|
||||
local nvm = techage.get_nvm(pos)
|
||||
nvm.assemble_locked = false
|
||||
end
|
||||
end
|
||||
|
||||
local function check_space(pos, param2, AssemblyPlan, player_name)
|
||||
for _,item in ipairs(AssemblyPlan) do
|
||||
local y, path, node_name = item[1], item[2], item[4]
|
||||
local pos1 = dest_pos(pos, param2, path, y)
|
||||
if minetest.is_protected(pos1, player_name) then
|
||||
minetest.chat_send_player(player_name, S("[TA] Area is protected!"))
|
||||
return false
|
||||
end
|
||||
|
||||
local node = techage.get_node_lvm(pos1)
|
||||
local ndef = minetest.registered_nodes[node.name]
|
||||
if not ndef or not ndef.buildable_to and node.name ~= node_name then
|
||||
minetest.chat_send_player(player_name, S("[TA] Not enough space!"))
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
-- Two important flags:
|
||||
-- 1) nvm.assemble_locked is true while the object is being assembled/disassembled
|
||||
-- 2) nvm.assemble_build is true if the object is assembled
|
||||
function techage.assemble.build(pos, AssemblyPlan, player_name)
|
||||
-- check protection
|
||||
if minetest.is_protected(pos, player_name) then
|
||||
return
|
||||
end
|
||||
local nvm = techage.get_nvm(pos)
|
||||
if nvm.assemble_locked then
|
||||
return
|
||||
end
|
||||
local node = minetest.get_node(pos)
|
||||
if check_space(pos, node.param2, AssemblyPlan, player_name) then
|
||||
nvm.assemble_locked = true
|
||||
build(pos, node.param2, AssemblyPlan, 1)
|
||||
nvm.assemble_build = true
|
||||
end
|
||||
end
|
||||
|
||||
function techage.assemble.remove(pos, AssemblyPlan, player_name)
|
||||
-- check protection
|
||||
if minetest.is_protected(pos, player_name) then
|
||||
return
|
||||
end
|
||||
local nvm = techage.get_nvm(pos)
|
||||
if nvm.assemble_locked then
|
||||
return
|
||||
end
|
||||
local node = minetest.get_node(pos)
|
||||
nvm.assemble_locked = true
|
||||
remove(pos, node.param2, AssemblyPlan, #AssemblyPlan)
|
||||
nvm.assemble_build = false
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Assembly functions based on nodes from node inventory
|
||||
--------------------------------------------------------------------------------
|
||||
local function play_sound(pos, sound)
|
||||
minetest.sound_play(sound, {
|
||||
pos = pos,
|
||||
gain = 1,
|
||||
max_hear_distance = 10,
|
||||
})
|
||||
end
|
||||
|
||||
local function build_inv(pos, inv, param2, AssemblyPlan, player_name, idx)
|
||||
local item = AssemblyPlan[idx]
|
||||
if item ~= nil then
|
||||
local y, path, fd_offs, node_name = item[1], item[2], item[3], item[4]
|
||||
local pos1 = dest_pos(pos, param2, path, y)
|
||||
if not minetest.is_protected(pos1, player_name) then
|
||||
local node = minetest.get_node(pos1)
|
||||
if techage.is_air_like(node.name) then
|
||||
local stack = inv:remove_item("src", ItemStack(node_name))
|
||||
if stack:get_count() == 1 then
|
||||
minetest.add_node(pos1, {name=node_name, param2=(param2 + fd_offs) % 4})
|
||||
play_sound(pos, "default_place_node_hard")
|
||||
local ndef = minetest.registered_nodes[node_name]
|
||||
if ndef and ndef.after_place_node then
|
||||
local placer = minetest.get_player_by_name(player_name)
|
||||
ndef.after_place_node(pos1, placer, ItemStack(node_name))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
minetest.after(0.5, build_inv, pos, inv, param2, AssemblyPlan, player_name, idx + 1)
|
||||
else
|
||||
local nvm = techage.get_nvm(pos)
|
||||
nvm.assemble_locked = false
|
||||
end
|
||||
end
|
||||
|
||||
local function remove_inv(pos, inv, param2, AssemblyPlan, player_name, idx)
|
||||
local item = AssemblyPlan[idx]
|
||||
if item ~= nil then
|
||||
local y, path, fd_offs, node_name = item[1], item[2], item[3], item[4]
|
||||
local pos1 = dest_pos(pos, param2, path, y)
|
||||
if not minetest.is_protected(pos1, player_name) then
|
||||
local stack = ItemStack(node_name)
|
||||
if inv:room_for_item("src", stack) then
|
||||
local node = minetest.get_node(pos1)
|
||||
if node.name == node_name then
|
||||
local meta = M(pos1):to_table()
|
||||
minetest.remove_node(pos1)
|
||||
inv:add_item("src", stack)
|
||||
play_sound(pos, "default_dig_cracky")
|
||||
local ndef = minetest.registered_nodes[node_name]
|
||||
if ndef and ndef.after_dig_node then
|
||||
local digger = minetest.get_player_by_name(player_name)
|
||||
ndef.after_dig_node(pos1, node, meta, digger)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
minetest.after(0.5, remove_inv, pos, inv, param2, AssemblyPlan, player_name, idx - 1)
|
||||
else
|
||||
local nvm = techage.get_nvm(pos)
|
||||
nvm.assemble_locked = false
|
||||
end
|
||||
end
|
||||
|
||||
function techage.assemble.build_inv(pos, inv, AssemblyPlan, player_name)
|
||||
-- check protection
|
||||
if minetest.is_protected(pos, player_name) then
|
||||
return
|
||||
end
|
||||
local nvm = techage.get_nvm(pos)
|
||||
if nvm.assemble_locked then
|
||||
return
|
||||
end
|
||||
local node = minetest.get_node(pos)
|
||||
nvm.assemble_locked = true
|
||||
build_inv(pos, inv, node.param2, AssemblyPlan, player_name, 1)
|
||||
end
|
||||
|
||||
function techage.assemble.remove_inv(pos, inv, AssemblyPlan, player_name)
|
||||
-- check protection
|
||||
if minetest.is_protected(pos, player_name) then
|
||||
return
|
||||
end
|
||||
local nvm = techage.get_nvm(pos)
|
||||
if nvm.assemble_locked then
|
||||
return
|
||||
end
|
||||
local node = minetest.get_node(pos)
|
||||
nvm.assemble_locked = true
|
||||
remove_inv(pos, inv, node.param2, AssemblyPlan, player_name, #AssemblyPlan)
|
||||
end
|
||||
|
||||
function techage.assemble.count_items(AssemblyPlan)
|
||||
local t = {}
|
||||
for _, item in ipairs(AssemblyPlan) do
|
||||
local node_name = item[4]
|
||||
local ndef = minetest.registered_nodes[node_name]
|
||||
local name = ndef.description
|
||||
if not t[name] then
|
||||
t[name] = 1
|
||||
else
|
||||
t[name] = t[name] + 1
|
||||
end
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
-- Determine the destination position based on the given route
|
||||
-- param2, and a route table like : {0,3}
|
||||
-- 0 = forward, 1 = right, 2 = backward, 3 = left
|
||||
-- techage.assemble.get_pos(pos, param2, route, y_offs)
|
||||
techage.assemble.get_pos = dest_pos
|
150
techage/basis/boiler_lib.lua
Normal file
150
techage/basis/boiler_lib.lua
Normal file
@ -0,0 +1,150 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019-2023 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
Boiler common functions
|
||||
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local P = minetest.string_to_pos
|
||||
local M = minetest.get_meta
|
||||
local S = techage.S
|
||||
|
||||
local HEAT_STEP = 10
|
||||
local MAX_WATER = 10
|
||||
local BLOCKING_TIME = 0.3 -- 300ms
|
||||
|
||||
techage.boiler = {}
|
||||
|
||||
local IsWater = {
|
||||
["bucket:bucket_river_water"] = "bucket:bucket_empty",
|
||||
}
|
||||
|
||||
local IsBucket = {}
|
||||
|
||||
local function node_description(name)
|
||||
name = string.split(name, " ")[1]
|
||||
local ndef = minetest.registered_nodes[name] or minetest.registered_items[name] or minetest.registered_craftitems[name]
|
||||
if ndef and ndef.description then
|
||||
return minetest.formspec_escape(ndef.description)
|
||||
end
|
||||
return ""
|
||||
end
|
||||
|
||||
local function item_image(x, y, itemname)
|
||||
return "box["..x..","..y..";0.85,0.9;#808080]"..
|
||||
"item_image["..x..","..y..";1,1;"..itemname.."]"
|
||||
end
|
||||
|
||||
function techage.boiler.formspec(pos, nvm)
|
||||
local title = S("Water Boiler")
|
||||
local temp = nvm.temperature or 20
|
||||
local ratio = nvm.power_ratio or 0
|
||||
local tooltip = S("To add water punch\nthe boiler\nwith a water bucket")
|
||||
return "size[5,3]"..
|
||||
default.gui_bg..
|
||||
default.gui_bg_img..
|
||||
default.gui_slots..
|
||||
"box[0,-0.1;4.8,0.5;#c6e8ff]"..
|
||||
"label[1.5,-0.1;"..minetest.colorize("#000000", title).."]"..
|
||||
item_image(1, 1.5, "default:water_source "..(nvm.num_water or 0))..
|
||||
"tooltip[1,1.5;1,1;"..tooltip..";#0C3D32;#FFFFFF]"..
|
||||
"image[3,1.0;1,2;techage_form_temp_bg.png^[lowpart:"..
|
||||
temp..":techage_form_temp_fg.png]"..
|
||||
"tooltip[3,1;1,2;"..S("water temperature")..";#0C3D32;#FFFFFF]"
|
||||
end
|
||||
|
||||
function techage.boiler.water_temperature(pos, nvm)
|
||||
nvm.temperature = nvm.temperature or 20
|
||||
nvm.num_water = nvm.num_water or 0
|
||||
nvm.water_level = nvm.water_level or 0
|
||||
if nvm.fire_trigger then
|
||||
nvm.temperature = math.min(nvm.temperature + HEAT_STEP, 100)
|
||||
else
|
||||
nvm.temperature = math.max(nvm.temperature - HEAT_STEP, 20)
|
||||
end
|
||||
nvm.fire_trigger = false
|
||||
|
||||
if nvm.water_level == 0 then
|
||||
if nvm.num_water > 0 then
|
||||
nvm.num_water = nvm.num_water - 1
|
||||
nvm.water_level = 100
|
||||
else
|
||||
nvm.temperature = 20
|
||||
end
|
||||
end
|
||||
return nvm.temperature
|
||||
end
|
||||
|
||||
function techage.boiler.on_rightclick(pos, node, clicker)
|
||||
techage.set_activeformspec(pos, clicker)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
M(pos):set_string("formspec", techage.boiler.formspec(pos, nvm))
|
||||
end
|
||||
|
||||
function techage.boiler.can_dig(pos, player)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
nvm.num_water = nvm.num_water or 0
|
||||
return nvm.num_water == 0
|
||||
end
|
||||
|
||||
local function space_in_inventory(wielded_item, item_count, puncher)
|
||||
-- check if holding more than 1 empty container
|
||||
if item_count > 1 then
|
||||
local inv = puncher:get_inventory()
|
||||
local item = ItemStack({name=wielded_item, count = item_count - 1})
|
||||
if inv:room_for_item("main", item) then
|
||||
inv:add_item("main", item)
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function techage.boiler.on_punch(pos, node, puncher, pointed_thing)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
local mem = techage.get_mem(pos)
|
||||
mem.blocking_time = mem.blocking_time or 0
|
||||
if mem.blocking_time > techage.SystemTime then
|
||||
return
|
||||
end
|
||||
|
||||
nvm.num_water = nvm.num_water or 0
|
||||
local wielded_item = puncher:get_wielded_item():get_name()
|
||||
local item_count = puncher:get_wielded_item():get_count()
|
||||
if IsWater[wielded_item] and nvm.num_water < MAX_WATER then
|
||||
mem.blocking_time = techage.SystemTime + BLOCKING_TIME
|
||||
nvm.num_water = nvm.num_water + 1
|
||||
puncher:set_wielded_item(ItemStack(IsWater[wielded_item]))
|
||||
M(pos):set_string("formspec", techage.boiler.formspec(pos, nvm))
|
||||
elseif IsBucket[wielded_item] and nvm.num_water > 0 then
|
||||
if item_count > 1 then
|
||||
local inv = puncher:get_inventory()
|
||||
local item = ItemStack(IsBucket[wielded_item])
|
||||
if inv:room_for_item("main", item) then
|
||||
inv:add_item("main", item)
|
||||
puncher:set_wielded_item({name=wielded_item, count = item_count - 1})
|
||||
mem.blocking_time = techage.SystemTime + BLOCKING_TIME
|
||||
nvm.num_water = nvm.num_water - 1
|
||||
end
|
||||
else
|
||||
mem.blocking_time = techage.SystemTime + BLOCKING_TIME
|
||||
nvm.num_water = nvm.num_water - 1
|
||||
puncher:set_wielded_item(ItemStack(IsBucket[wielded_item]))
|
||||
end
|
||||
M(pos):set_string("formspec", techage.boiler.formspec(pos, nvm))
|
||||
end
|
||||
end
|
||||
|
||||
function techage.register_water_bucket(empty_bucket, full_bucket)
|
||||
IsWater[full_bucket] = empty_bucket
|
||||
IsBucket[empty_bucket] = full_bucket
|
||||
end
|
689
techage/basis/command.lua
Normal file
689
techage/basis/command.lua
Normal file
@ -0,0 +1,689 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019-2020 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
Basis functions for inter-node communication
|
||||
|
||||
]]--
|
||||
|
||||
--- for lazy programmers
|
||||
local S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
--local P = minetest.string_to_pos
|
||||
--local M = minetest.get_meta
|
||||
local has_mesecons = minetest.global_exists("mesecon")
|
||||
|
||||
local NodeInfoCache = {}
|
||||
local NumbersToBeRecycled = {}
|
||||
local MP = minetest.get_modpath("techage")
|
||||
local techage_use_sqlite = minetest.settings:get_bool('techage_use_sqlite', false)
|
||||
|
||||
-- Localize functions to avoid table lookups (better performance)
|
||||
local string_split = string.split
|
||||
local NodeDef = techage.NodeDef
|
||||
local Tube = techage.Tube
|
||||
local is_cart_available = minecart.is_nodecart_available
|
||||
local techage_counting_hit = techage.counting_hit
|
||||
local tubelib2_side_to_dir = tubelib2.side_to_dir
|
||||
|
||||
-------------------------------------------------------------------
|
||||
-- Database
|
||||
-------------------------------------------------------------------
|
||||
local backend
|
||||
if techage_use_sqlite then
|
||||
backend = dofile(MP .. "/basis/numbers_sqlite.lua")
|
||||
else
|
||||
backend = dofile(MP .. "/basis/numbers_storage.lua")
|
||||
end
|
||||
|
||||
local function update_nodeinfo(number)
|
||||
local pos = backend.get_nodepos(number)
|
||||
if pos then
|
||||
NodeInfoCache[number] = {pos = pos, name = techage.get_node_lvm(pos).name}
|
||||
return NodeInfoCache[number]
|
||||
end
|
||||
end
|
||||
|
||||
local function delete_nodeinfo_entry(number)
|
||||
if number and NodeInfoCache[number] then
|
||||
number = next(NodeInfoCache, number)
|
||||
if number then
|
||||
NodeInfoCache[number] = nil
|
||||
end
|
||||
else
|
||||
number = next(NodeInfoCache, nil)
|
||||
end
|
||||
return number
|
||||
end
|
||||
|
||||
-- Keep the cache size small by deleting entries randomly
|
||||
local function keep_small(number)
|
||||
number = delete_nodeinfo_entry(number)
|
||||
minetest.after(10, keep_small, number)
|
||||
end
|
||||
|
||||
keep_small()
|
||||
|
||||
minetest.after(2, backend.delete_invalid_entries, NodeDef)
|
||||
|
||||
-------------------------------------------------------------------
|
||||
-- Local helper functions
|
||||
-------------------------------------------------------------------
|
||||
local function in_list(list, x)
|
||||
for _, v in ipairs(list) do
|
||||
if v == x then return true end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- Determine position related node number for addressing purposes
|
||||
local function get_number(pos, new)
|
||||
local meta = minetest.get_meta(pos)
|
||||
if meta:contains("node_number") then
|
||||
return meta:get_string("node_number")
|
||||
end
|
||||
-- generate new number
|
||||
if new then
|
||||
local num = backend.add_nodepos(pos)
|
||||
meta:set_string("node_number", num)
|
||||
return num
|
||||
end
|
||||
end
|
||||
|
||||
local function not_protected(pos, placer_name, clicker_name)
|
||||
local meta = minetest.get_meta(pos)
|
||||
if meta then
|
||||
if placer_name and not minetest.is_protected(pos, placer_name) then
|
||||
if clicker_name == nil or placer_name == clicker_name then
|
||||
return true
|
||||
end
|
||||
if not minetest.is_protected(pos, clicker_name) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function register_lbm(name, nodenames)
|
||||
minetest.register_lbm({
|
||||
label = "[TechAge] Node update",
|
||||
name = name.."update",
|
||||
nodenames = nodenames,
|
||||
run_at_every_load = true,
|
||||
action = function(pos, node)
|
||||
if NodeDef[node.name] and NodeDef[node.name].on_node_load then
|
||||
NodeDef[node.name].on_node_load(pos, node)
|
||||
end
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
local SideToDir = {B=1, R=2, F=3, L=4, D=5, U=6}
|
||||
|
||||
local function side_to_dir(side, param2)
|
||||
return tubelib2_side_to_dir(side, param2)
|
||||
end
|
||||
|
||||
techage.side_to_outdir = side_to_dir
|
||||
|
||||
function techage.side_to_indir(side, param2)
|
||||
return tubelib2.Turn180Deg[side_to_dir(side, param2)]
|
||||
end
|
||||
|
||||
local function get_next_node(pos, out_dir)
|
||||
local res, npos, node = Tube:compatible_node(pos, out_dir)
|
||||
local in_dir = tubelib2.Turn180Deg[out_dir]
|
||||
return res, npos, in_dir, node.name
|
||||
end
|
||||
|
||||
local function get_dest_node(pos, out_dir)
|
||||
local spos, in_dir = Tube:get_connected_node_pos(pos, out_dir)
|
||||
local _,node = Tube:get_node(spos)
|
||||
return spos, in_dir, node.name
|
||||
end
|
||||
|
||||
local function item_handling_node(name)
|
||||
local node_def = name and NodeDef[name]
|
||||
if node_def then
|
||||
return node_def.on_pull_item or node_def.on_push_item or node_def.is_pusher
|
||||
end
|
||||
end
|
||||
|
||||
local function is_air_like(name)
|
||||
local ndef = minetest.registered_nodes[name]
|
||||
if ndef and ndef.buildable_to then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
techage.SystemTime = 0
|
||||
minetest.register_globalstep(function(dtime)
|
||||
techage.SystemTime = techage.SystemTime + dtime
|
||||
end)
|
||||
|
||||
-- used by TA1 hammer: dug_node[player_name] = pos
|
||||
techage.dug_node = {}
|
||||
minetest.register_on_dignode(function(pos, oldnode, digger)
|
||||
if not digger then return end
|
||||
-- store the position of the dug block for tools like the TA1 hammer
|
||||
techage.dug_node[digger:get_player_name()] = pos
|
||||
end)
|
||||
|
||||
-------------------------------------------------------------------
|
||||
-- API helper functions
|
||||
-------------------------------------------------------------------
|
||||
|
||||
-- Check if both strings are the same or one string starts with the other string.
|
||||
function techage.string_compare(s1, s2)
|
||||
if s1 and s2 then
|
||||
local minLength = math.min(#s1, #s2)
|
||||
return string.sub(s1, 1, minLength) == string.sub(s2, 1, minLength)
|
||||
end
|
||||
end
|
||||
|
||||
-- Function returns { pos, name } for the node referenced by number
|
||||
function techage.get_node_info(dest_num)
|
||||
return NodeInfoCache[dest_num] or update_nodeinfo(dest_num)
|
||||
end
|
||||
|
||||
-- Function returns the node number from the given position or
|
||||
-- nil, if no node number for this position is assigned.
|
||||
function techage.get_node_number(pos)
|
||||
return get_number(pos)
|
||||
end
|
||||
|
||||
function techage.get_pos(pos, side)
|
||||
local node = techage.get_node_lvm(pos)
|
||||
local dir = nil
|
||||
if node.name ~= "air" and node.name ~= "ignore" then
|
||||
dir = side_to_dir(side, node.param2)
|
||||
end
|
||||
return tubelib2.get_pos(pos, dir)
|
||||
end
|
||||
|
||||
-- Function is used for available nodes with lost numbers, only.
|
||||
function techage.get_new_number(pos, name)
|
||||
-- store position
|
||||
return get_number(pos, true)
|
||||
end
|
||||
|
||||
-- extract ident and value from strings like "ident=value"
|
||||
function techage.ident_value(s)
|
||||
local ident, value = unpack(string.split(s, "=", true, 1))
|
||||
return (ident or ""):trim(), (value or ""):trim()
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------
|
||||
-- Node construction/destruction functions
|
||||
-------------------------------------------------------------------
|
||||
|
||||
-- Add node to the techage lists.
|
||||
-- Function determines and returns the node position number,
|
||||
-- needed for message communication.
|
||||
-- If TA2 node, return '-' instead of a real number, because
|
||||
-- TA2 nodes should not support number based commands.
|
||||
function techage.add_node(pos, name, is_ta2)
|
||||
if item_handling_node(name) then
|
||||
Tube:after_place_node(pos)
|
||||
end
|
||||
if is_ta2 then
|
||||
return "-"
|
||||
end
|
||||
local key = minetest.hash_node_position(pos)
|
||||
local num = NumbersToBeRecycled[key]
|
||||
if num then
|
||||
NodeInfoCache[num] = nil
|
||||
backend.set_nodepos(num, pos)
|
||||
NumbersToBeRecycled[key] = nil
|
||||
return num
|
||||
end
|
||||
return get_number(pos, true)
|
||||
end
|
||||
|
||||
-- Function removes the node from the techage lists.
|
||||
function techage.remove_node(pos, oldnode, oldmetadata)
|
||||
local number = oldmetadata and oldmetadata.fields and (oldmetadata.fields.node_number or oldmetadata.fields.number)
|
||||
number = number or get_number(pos)
|
||||
if number and tonumber(number) then
|
||||
local key = minetest.hash_node_position(pos)
|
||||
NumbersToBeRecycled[key] = number
|
||||
NodeInfoCache[number] = nil
|
||||
end
|
||||
if oldnode and item_handling_node(oldnode.name) then
|
||||
Tube:after_dig_node(pos)
|
||||
end
|
||||
end
|
||||
|
||||
-- Repairs the node number after it was erased by `backend.delete_invalid_entries`
|
||||
function techage.repair_number(pos)
|
||||
local number = techage.get_node_number(pos)
|
||||
if number then
|
||||
backend.set_nodepos(number, pos)
|
||||
end
|
||||
end
|
||||
|
||||
-- Like techage.add_node, but use the old number again
|
||||
function techage.unpack_node(pos, name, number)
|
||||
if item_handling_node(name) then
|
||||
Tube:after_place_node(pos)
|
||||
end
|
||||
local key = minetest.hash_node_position(pos)
|
||||
NumbersToBeRecycled[key] = nil
|
||||
if number then
|
||||
backend.set_nodepos(number, pos)
|
||||
end
|
||||
end
|
||||
|
||||
-- Like techage.remove_node but don't store the number for this position
|
||||
function techage.pack_node(pos, oldnode, number)
|
||||
if number then
|
||||
NodeInfoCache[number] = nil
|
||||
end
|
||||
if oldnode and item_handling_node(oldnode.name) then
|
||||
Tube:after_dig_node(pos)
|
||||
end
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------
|
||||
-- Used by the assembly tool
|
||||
-------------------------------------------------------------------
|
||||
function techage.pre_add_node(pos, number)
|
||||
local key = minetest.hash_node_position(pos)
|
||||
NumbersToBeRecycled[key] = number
|
||||
end
|
||||
|
||||
function techage.post_remove_node(pos)
|
||||
local key = minetest.hash_node_position(pos)
|
||||
NumbersToBeRecycled[key] = nil
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------
|
||||
-- Node register function
|
||||
-------------------------------------------------------------------
|
||||
|
||||
-- Register node for techage communication
|
||||
-- Call this function only at load time!
|
||||
-- Param names: List of node names like {"techage:pusher_off", "techage:pusher_on"}
|
||||
-- Param node_definition: A table according to:
|
||||
-- {
|
||||
-- on_inv_request = func(pos, in_dir, access_type)
|
||||
-- on_pull_item = func(pos, in_dir, num, (opt.) item_name),
|
||||
-- on_push_item = func(pos, in_dir, item),
|
||||
-- on_unpull_item = func(pos, in_dir, item),
|
||||
-- on_recv_message = func(pos, src, topic, payload),
|
||||
-- on_node_load = func(pos), -- LBM function
|
||||
-- on_transfer = func(pos, in_dir, topic, payload),
|
||||
-- }
|
||||
function techage.register_node(names, node_definition)
|
||||
-- store facedir table for all known node names
|
||||
for _,n in ipairs(names) do
|
||||
NodeDef[n] = node_definition
|
||||
end
|
||||
if node_definition.on_pull_item or node_definition.on_push_item or
|
||||
node_definition.is_pusher then
|
||||
Tube:add_secondary_node_names(names)
|
||||
|
||||
for _,n in ipairs(names) do
|
||||
techage.KnownNodes[n] = true
|
||||
end
|
||||
end
|
||||
-- register LBM
|
||||
if node_definition.on_node_load then
|
||||
register_lbm(names[1], names)
|
||||
end
|
||||
|
||||
-- register mvps stopper
|
||||
if has_mesecons then
|
||||
for _, name in ipairs(names) do
|
||||
mesecon.register_mvps_stopper(name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------
|
||||
-- Send message functions
|
||||
-------------------------------------------------------------------
|
||||
|
||||
function techage.not_protected(number, placer_name, clicker_name)
|
||||
local ninfo = NodeInfoCache[number] or update_nodeinfo(number)
|
||||
if ninfo and ninfo.pos then
|
||||
return not_protected(ninfo.pos, placer_name, clicker_name)
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- Check the given number value.
|
||||
-- Returns true if the number is valid, point to real node and
|
||||
-- and the node is not protected for the given player_name.
|
||||
function techage.check_number(number, placer_name)
|
||||
if number then
|
||||
if not techage.not_protected(number, placer_name, nil) then
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- Check the given list of numbers.
|
||||
-- Returns true if number(s) is/are valid, point to real nodes and
|
||||
-- and the nodes are not protected for the given player_name.
|
||||
function techage.check_numbers(numbers, placer_name)
|
||||
if numbers then
|
||||
for _,num in ipairs(string_split(numbers, " ")) do
|
||||
if not techage.not_protected(num, placer_name, nil) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function techage.send_multi(src, numbers, topic, payload)
|
||||
--print("send_multi", src, numbers, topic)
|
||||
for _,num in ipairs(string_split(numbers, " ")) do
|
||||
local ninfo = NodeInfoCache[num] or update_nodeinfo(num)
|
||||
if ninfo and ninfo.name and ninfo.pos then
|
||||
local ndef = NodeDef[ninfo.name]
|
||||
if ndef and ndef.on_recv_message then
|
||||
techage_counting_hit()
|
||||
ndef.on_recv_message(ninfo.pos, src, topic, payload)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function techage.send_single(src, number, topic, payload)
|
||||
--print("send_single", src, number, topic)
|
||||
local ninfo = NodeInfoCache[number] or update_nodeinfo(number)
|
||||
if ninfo and ninfo.name and ninfo.pos then
|
||||
local ndef = NodeDef[ninfo.name]
|
||||
if ndef and ndef.on_recv_message then
|
||||
techage_counting_hit()
|
||||
return ndef.on_recv_message(ninfo.pos, src, topic, payload)
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- The destination node location is either:
|
||||
-- A) a destination position, specified by pos
|
||||
-- B) a neighbor position, specified by caller pos/outdir, or pos/side
|
||||
-- C) a tubelib2 network connection, specified by caller pos/outdir, or pos/side
|
||||
-- outdir is one of: 1..6
|
||||
-- side is one of: "B", "R", "F", "L", "D", "U"
|
||||
-- network is a tuebelib2 network instance
|
||||
-- opt: nodenames is a table of valid the callee node names
|
||||
function techage.transfer(pos, outdir, topic, payload, network, nodenames)
|
||||
-- determine out-dir
|
||||
if outdir and type(outdir) == "string" then
|
||||
local param2 = techage.get_node_lvm(pos).param2
|
||||
outdir = side_to_dir(outdir, param2)
|
||||
end
|
||||
-- determine destination pos
|
||||
local dpos, indir
|
||||
if network then
|
||||
dpos, indir = network:get_connected_node_pos(pos, outdir)
|
||||
else
|
||||
dpos, indir = tubelib2.get_pos(pos, outdir), outdir
|
||||
end
|
||||
-- check node name
|
||||
local name = techage.get_node_lvm(dpos).name
|
||||
if nodenames and not in_list(nodenames, name) then
|
||||
return false
|
||||
end
|
||||
-- call "on_transfer"
|
||||
local ndef = NodeDef[name]
|
||||
if ndef and ndef.on_transfer then
|
||||
return ndef.on_transfer(dpos, indir, topic, payload)
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------
|
||||
-- Beduino functions (see "bep-005_ta_cmnd.md")
|
||||
-------------------------------------------------------------------
|
||||
function techage.beduino_send_cmnd(src, number, topic, payload)
|
||||
--print("beduino_send_cmnd", src, number, topic)
|
||||
local ninfo = NodeInfoCache[number] or update_nodeinfo(number)
|
||||
if ninfo and ninfo.name and ninfo.pos then
|
||||
local ndef = NodeDef[ninfo.name]
|
||||
if ndef and ndef.on_beduino_receive_cmnd then
|
||||
return ndef.on_beduino_receive_cmnd(ninfo.pos, src, topic, payload or {})
|
||||
end
|
||||
end
|
||||
return 1, ""
|
||||
end
|
||||
|
||||
function techage.beduino_request_data(src, number, topic, payload)
|
||||
--print("beduino_request_data", src, number, topic)
|
||||
local ninfo = NodeInfoCache[number] or update_nodeinfo(number)
|
||||
if ninfo and ninfo.name and ninfo.pos then
|
||||
local ndef = NodeDef[ninfo.name]
|
||||
if ndef and ndef.on_beduino_request_data then
|
||||
return ndef.on_beduino_request_data(ninfo.pos, src, topic, payload or {})
|
||||
end
|
||||
end
|
||||
return 1, ""
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------
|
||||
-- Client side Push/Pull item functions
|
||||
-------------------------------------------------------------------
|
||||
|
||||
function techage.get_inv_access(pos, out_dir, access_type)
|
||||
local npos, in_dir, name = get_dest_node(pos, out_dir)
|
||||
if npos and NodeDef[name] and NodeDef[name].on_inv_request then
|
||||
return NodeDef[name].on_inv_request(npos, in_dir, access_type)
|
||||
end
|
||||
end
|
||||
|
||||
function techage.pull_items(pos, out_dir, num, item_name)
|
||||
local npos, in_dir, name = get_dest_node(pos, out_dir)
|
||||
if npos and NodeDef[name] and NodeDef[name].on_pull_item then
|
||||
return NodeDef[name].on_pull_item(npos, in_dir, num, item_name)
|
||||
end
|
||||
end
|
||||
|
||||
function techage.push_items(pos, out_dir, stack, idx)
|
||||
local npos, in_dir, name = get_dest_node(pos, out_dir)
|
||||
if npos and NodeDef[name] and NodeDef[name].on_push_item then
|
||||
return NodeDef[name].on_push_item(npos, in_dir, stack, idx)
|
||||
elseif is_air_like(name) or is_cart_available(npos) then
|
||||
minetest.add_item(npos, stack)
|
||||
return true
|
||||
end
|
||||
return stack
|
||||
end
|
||||
|
||||
-- Check for recursion and too long distances
|
||||
local start_pos
|
||||
function techage.safe_push_items(pos, out_dir, stack, idx)
|
||||
local mem = techage.get_mem(pos)
|
||||
if not mem.pushing then
|
||||
if not start_pos then
|
||||
start_pos = pos
|
||||
mem.pushing = true
|
||||
local res = techage.push_items(pos, out_dir, stack, idx)
|
||||
mem.pushing = nil
|
||||
start_pos = nil
|
||||
return res
|
||||
else
|
||||
local npos, in_dir, name = get_dest_node(pos, out_dir)
|
||||
if vector.distance(start_pos, npos) < (Tube.max_tube_length or 100) then
|
||||
mem.pushing = true
|
||||
local res = techage.push_items(pos, out_dir, stack, idx)
|
||||
mem.pushing = nil
|
||||
return res
|
||||
end
|
||||
end
|
||||
end
|
||||
return stack
|
||||
end
|
||||
|
||||
function techage.unpull_items(pos, out_dir, stack)
|
||||
local npos, in_dir, name = get_dest_node(pos, out_dir)
|
||||
if npos and NodeDef[name] and NodeDef[name].on_unpull_item then
|
||||
return NodeDef[name].on_unpull_item(npos, in_dir, stack)
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------
|
||||
-- Server side helper functions
|
||||
-------------------------------------------------------------------
|
||||
|
||||
-- Get the given number of items from the inv. The position within the list
|
||||
-- is incremented each time so that different item stacks will be considered.
|
||||
-- Returns nil if ItemList is empty.
|
||||
function techage.get_items(pos, inv, listname, num)
|
||||
if inv:is_empty(listname) then
|
||||
return nil
|
||||
end
|
||||
local size = inv:get_size(listname)
|
||||
local mem = techage.get_mem(pos)
|
||||
mem.ta_startpos = mem.ta_startpos or 0
|
||||
for idx = mem.ta_startpos, mem.ta_startpos+size do
|
||||
idx = (idx % size) + 1
|
||||
local items = inv:get_stack(listname, idx)
|
||||
if items:get_count() > 0 then
|
||||
local taken = items:take_item(num)
|
||||
inv:set_stack(listname, idx, items)
|
||||
mem.ta_startpos = idx
|
||||
return taken
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Put the given stack into the given ItemList/inventory.
|
||||
-- Function returns:
|
||||
-- - true, if all items are moved
|
||||
-- - false, if no item is moved
|
||||
-- - leftover, if less than all items are moved
|
||||
-- (true/false is the legacy mode and can't be removed)
|
||||
function techage.put_items(inv, listname, item, idx)
|
||||
local leftover
|
||||
if idx and inv and idx <= inv:get_size(listname) then
|
||||
local stack = inv:get_stack(listname, idx)
|
||||
leftover = stack:add_item(item)
|
||||
inv:set_stack(listname, idx, stack)
|
||||
elseif inv then
|
||||
leftover = inv:add_item(listname, item)
|
||||
else
|
||||
return false
|
||||
end
|
||||
|
||||
local cnt = leftover:get_count()
|
||||
if cnt == item:get_count() then
|
||||
return false
|
||||
elseif cnt == 0 then
|
||||
return true
|
||||
else
|
||||
return leftover
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- Return "full", "loaded", or "empty" depending
|
||||
-- on the inventory load.
|
||||
-- Full is returned, when no empty stack is available.
|
||||
function techage.get_inv_state(inv, listname)
|
||||
local state
|
||||
if inv:is_empty(listname) then
|
||||
state = "empty"
|
||||
else
|
||||
local list = inv:get_list(listname)
|
||||
state = "full"
|
||||
for _, item in ipairs(list) do
|
||||
if item:is_empty() then
|
||||
return "loaded"
|
||||
end
|
||||
end
|
||||
end
|
||||
return state
|
||||
end
|
||||
|
||||
-- Beduino variant
|
||||
function techage.get_inv_state_num(inv, listname)
|
||||
local state
|
||||
if inv:is_empty(listname) then
|
||||
state = 0
|
||||
else
|
||||
local list = inv:get_list(listname)
|
||||
state = 2
|
||||
for _, item in ipairs(list) do
|
||||
if item:is_empty() then
|
||||
return 1
|
||||
end
|
||||
end
|
||||
end
|
||||
return state
|
||||
end
|
||||
|
||||
minetest.register_chatcommand("ta_send", {
|
||||
description = minetest.formspec_escape(
|
||||
"Send a techage command to the block with the number given: /ta_send <number> <command> [<data>]"),
|
||||
func = function(name, param)
|
||||
local num, cmnd, payload = param:match('^([0-9]+)%s+(%w+)%s*(.*)$')
|
||||
|
||||
if num and cmnd then
|
||||
if techage.not_protected(num, name) then
|
||||
local resp = techage.send_single("0", num, cmnd, payload)
|
||||
if type(resp) == "string" then
|
||||
return true, resp
|
||||
else
|
||||
return true, dump(resp)
|
||||
end
|
||||
else
|
||||
return false, "Destination block is protected"
|
||||
end
|
||||
end
|
||||
return false, "Syntax: /ta_send <number> <command> [<data>]"
|
||||
end
|
||||
})
|
||||
|
||||
minetest.register_chatcommand("expoints", {
|
||||
privs = {
|
||||
server = true
|
||||
},
|
||||
func = function(name, param)
|
||||
local player_name, points = param:match("^(%S+)%s*(%d*)$")
|
||||
if player_name then
|
||||
local player = minetest.get_player_by_name(player_name)
|
||||
if player then
|
||||
if points and points ~= "" then
|
||||
if techage.set_expoints(player, tonumber(points)) then
|
||||
return true, "The player "..player_name.." now has "..points.." experience points."
|
||||
end
|
||||
else
|
||||
points = techage.get_expoints(player)
|
||||
return true, "The player "..player_name.." has "..points.." experience points."
|
||||
end
|
||||
else
|
||||
return false, "Unknown player "..player_name
|
||||
end
|
||||
end
|
||||
return false, "Syntax error! Syntax: /expoints <name> [<points>]"
|
||||
end
|
||||
})
|
||||
|
||||
minetest.register_chatcommand("my_expoints", {
|
||||
func = function(name, param)
|
||||
local player = minetest.get_player_by_name(name)
|
||||
if player then
|
||||
local points = techage.get_expoints(player)
|
||||
if points then
|
||||
return true, "You have "..points.." experience points."
|
||||
end
|
||||
end
|
||||
end
|
||||
})
|
115
techage/basis/conf_inv.lua
Normal file
115
techage/basis/conf_inv.lua
Normal file
@ -0,0 +1,115 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2020 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
Configured inventory lib
|
||||
Assuming the inventory has the name "conf"
|
||||
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local M = minetest.get_meta
|
||||
|
||||
local inv_lib = {}
|
||||
|
||||
function inv_lib.preassigned_stacks(pos, xsize, ysize)
|
||||
local inv = M(pos):get_inventory()
|
||||
local tbl = {}
|
||||
for idx = 1, xsize * ysize do
|
||||
local item_name = inv:get_stack("conf", idx):get_name()
|
||||
if item_name ~= "" then
|
||||
local x = (idx - 1) % xsize
|
||||
local y = math.floor((idx - 1) / xsize)
|
||||
tbl[#tbl+1] = "item_image["..x..","..y..";1,1;"..item_name.."]"
|
||||
end
|
||||
end
|
||||
return table.concat(tbl, "")
|
||||
end
|
||||
|
||||
function inv_lib.item_filter(pos, size)
|
||||
local inv = M(pos):get_inventory()
|
||||
local filter = {}
|
||||
for idx = 1, size do
|
||||
local item_name = inv:get_stack("conf", idx):get_name()
|
||||
if item_name == "" then item_name = "unconfigured" end
|
||||
if not filter[item_name] then
|
||||
filter[item_name] = {}
|
||||
end
|
||||
table.insert(filter[item_name], idx)
|
||||
end
|
||||
return filter
|
||||
end
|
||||
|
||||
function inv_lib.allow_conf_inv_put(pos, listname, index, stack, player)
|
||||
local inv = M(pos):get_inventory()
|
||||
local list = inv:get_list(listname)
|
||||
|
||||
if list[index]:get_count() == 0 then
|
||||
stack:set_count(1)
|
||||
inv:set_stack(listname, index, stack)
|
||||
return 0
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
function inv_lib.allow_conf_inv_take(pos, listname, index, stack, player)
|
||||
local inv = M(pos):get_inventory()
|
||||
inv:set_stack(listname, index, nil)
|
||||
return 0
|
||||
end
|
||||
|
||||
function inv_lib.allow_conf_inv_move(pos, from_list, from_index, to_list, to_index, count, player)
|
||||
local inv = minetest.get_meta(pos):get_inventory()
|
||||
local stack = inv:get_stack(to_list, to_index)
|
||||
|
||||
if stack:get_count() == 0 then
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
end
|
||||
end
|
||||
|
||||
function inv_lib.put_items(pos, inv, listname, item, stacks, idx)
|
||||
local name = item:get_name()
|
||||
local count = item:get_count()
|
||||
for _, i in ipairs(stacks or {}) do
|
||||
if not idx or idx == i then
|
||||
local stack = inv:get_stack(listname, i)
|
||||
local leftover = stack:add_item({name = name, count = count})
|
||||
count = leftover:get_count()
|
||||
inv:set_stack(listname, i, stack)
|
||||
if count == 0 then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
if count > 0 then
|
||||
return ItemStack({name = name, count = count})
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function inv_lib.take_item(pos, inv, listname, num, stacks)
|
||||
local mem = techage.get_mem(pos)
|
||||
mem.ta_startpos = mem.ta_startpos or 1
|
||||
local size = #(stacks or {})
|
||||
for i = 1, size do
|
||||
local idx = stacks[((i + mem.ta_startpos) % size) + 1]
|
||||
local stack = inv:get_stack(listname, idx)
|
||||
local taken = stack:take_item(num)
|
||||
if taken:get_count() > 0 then
|
||||
inv:set_stack(listname, idx, stack)
|
||||
mem.ta_startpos = mem.ta_startpos + i
|
||||
return taken
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return inv_lib
|
71
techage/basis/counting.lua
Normal file
71
techage/basis/counting.lua
Normal file
@ -0,0 +1,71 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019-2021 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
Count techage commands player related
|
||||
|
||||
]]--
|
||||
|
||||
local PlayerName
|
||||
local PlayerPoints = {}
|
||||
local LastPlayerPoints = {}
|
||||
local S = techage.S
|
||||
|
||||
local MAX_POINTS = tonumber(minetest.settings:get("techage_command_limit")) or 1200
|
||||
|
||||
function techage.counting_start(player_name)
|
||||
PlayerName = player_name
|
||||
PlayerPoints[PlayerName] = PlayerPoints[PlayerName] or 0
|
||||
end
|
||||
|
||||
function techage.counting_stop()
|
||||
PlayerName = nil
|
||||
end
|
||||
|
||||
function techage.counting_hit()
|
||||
if PlayerName then
|
||||
PlayerPoints[PlayerName] = PlayerPoints[PlayerName] + 1
|
||||
end
|
||||
end
|
||||
|
||||
function techage.counting_add(player_name, points)
|
||||
PlayerPoints[player_name] = (PlayerPoints[player_name] or 0) + points
|
||||
end
|
||||
|
||||
local function output()
|
||||
for name, val in pairs(PlayerPoints) do
|
||||
if val > MAX_POINTS then
|
||||
local obj = minetest.get_player_by_name(name)
|
||||
if obj then
|
||||
minetest.chat_send_player(name,
|
||||
S("[techage] The limit for 'number of commands per minute' has been exceeded.") ..
|
||||
" " .. string.format(MAX_POINTS .. " " .. S("is allowed. Current value is") .. " " .. val));
|
||||
minetest.log("action", "[techage] " .. name ..
|
||||
" exceeds the limit for commands per minute. value = " .. val)
|
||||
local factor = 100 / (obj:get_armor_groups().fleshy or 100)
|
||||
obj:punch(obj, 1.0, {full_punch_interval=1.0, damage_groups = {fleshy=factor * 5}})
|
||||
end
|
||||
end
|
||||
end
|
||||
LastPlayerPoints = table.copy(PlayerPoints)
|
||||
PlayerPoints = {}
|
||||
minetest.after(60, output)
|
||||
end
|
||||
|
||||
minetest.after(60, output)
|
||||
|
||||
|
||||
minetest.register_chatcommand("ta_limit", {
|
||||
description = "Get your current techage command limit value",
|
||||
func = function(name)
|
||||
local num = LastPlayerPoints[name] or 0
|
||||
return true, S("Your current value is") .. " " .. num .. " " .. S("per minute") .. ". " ..
|
||||
MAX_POINTS .. " " .. S("is allowed")
|
||||
end
|
||||
})
|
117
techage/basis/fake_player.lua
Normal file
117
techage/basis/fake_player.lua
Normal file
@ -0,0 +1,117 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019-2020 Joachim Stolberg
|
||||
Copyright (C) 2020 Thomas S.
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
Fake Player
|
||||
|
||||
]]--
|
||||
|
||||
-- Map method names to their return values
|
||||
local methods = {
|
||||
get_pos = { x = 0, y = 0, z = 0 },
|
||||
set_pos = nil,
|
||||
moveto = nil,
|
||||
punch = nil,
|
||||
right_click = nil,
|
||||
get_hp = 20,
|
||||
set_hp = nil,
|
||||
get_inventory = nil,
|
||||
get_wield_list = "",
|
||||
get_wield_index = 0,
|
||||
get_wielded_item = ItemStack(),
|
||||
set_wielded_item = true,
|
||||
set_armor_groups = nil,
|
||||
get_armor_groups = {},
|
||||
set_animation = nil,
|
||||
get_animation = {},
|
||||
set_animation_frame_speed = nil,
|
||||
set_attach = nil,
|
||||
get_attach = nil,
|
||||
set_detach = nil,
|
||||
get_bone_position = {},
|
||||
set_properties = nil,
|
||||
get_properties = {},
|
||||
is_player = false,
|
||||
get_nametag_attributes = {},
|
||||
set_nametag_attributes = nil,
|
||||
get_player_name = "",
|
||||
get_player_velocity = nil,
|
||||
add_player_velocity = nil,
|
||||
get_look_dir = vector.new(0, 0, 1),
|
||||
get_look_vertical = 0,
|
||||
get_look_horizontal = 0,
|
||||
set_look_vertical = nil,
|
||||
set_look_horizontal = nil,
|
||||
get_look_pitch = 0,
|
||||
get_look_yaw = 0,
|
||||
set_look_pitch = nil,
|
||||
set_look_yaw = nil,
|
||||
get_breath = 10,
|
||||
set_breath = nil,
|
||||
set_fov = nil,
|
||||
get_fov = 0,
|
||||
set_attribute = nil,
|
||||
get_attribute = nil,
|
||||
get_meta = nil,
|
||||
set_inventory_formspec = nil,
|
||||
get_inventory_formspec = "",
|
||||
set_formspec_prepend = nil,
|
||||
get_formspec_prepend = "",
|
||||
get_player_control = {},
|
||||
get_player_control_bits = 0,
|
||||
set_physics_override = nil,
|
||||
get_physics_override = {},
|
||||
hud_add = 0,
|
||||
hud_remove = nil,
|
||||
hud_change = nil,
|
||||
hud_get = {},
|
||||
hud_set_flags = nil,
|
||||
hud_get_flags = {},
|
||||
hud_set_hotbar_itemcount = nil,
|
||||
hud_get_hotbar_itemcount = 8,
|
||||
hud_set_hotbar_image = nil,
|
||||
hud_get_hotbar_image = "",
|
||||
hud_set_hotbar_selected_image = nil,
|
||||
hud_get_hotbar_selected_image = "",
|
||||
set_sky = nil,
|
||||
get_sky = {},
|
||||
get_sky_color = {},
|
||||
set_sun = nil,
|
||||
get_sun = {},
|
||||
set_moon = nil,
|
||||
get_moon = {},
|
||||
set_stars = nil,
|
||||
get_stars = {},
|
||||
set_clouds = nil,
|
||||
get_clouds = {},
|
||||
override_day_night_ratio = nil,
|
||||
get_day_night_ratio = nil,
|
||||
set_local_animation = nil,
|
||||
get_local_animation = {},
|
||||
set_eye_offset = nil,
|
||||
get_eye_offset = {},
|
||||
send_mapblock = nil,
|
||||
}
|
||||
|
||||
techage.Fake_player = {}
|
||||
techage.Fake_player.__index = techage.Fake_player
|
||||
|
||||
function techage.Fake_player:new()
|
||||
local fake_player = {}
|
||||
setmetatable(fake_player, techage.Fake_player)
|
||||
return fake_player
|
||||
end
|
||||
|
||||
|
||||
for method_name, return_value in pairs(methods) do
|
||||
techage.Fake_player[method_name] = function(self, ...)
|
||||
return return_value
|
||||
end
|
||||
end
|
150
techage/basis/firebox_lib.lua
Normal file
150
techage/basis/firebox_lib.lua
Normal file
@ -0,0 +1,150 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019-2021 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
Firebox basic functions
|
||||
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local P = minetest.string_to_pos
|
||||
local M = minetest.get_meta
|
||||
local S = techage.S
|
||||
|
||||
techage.firebox = {}
|
||||
|
||||
techage.firebox.Burntime = {
|
||||
["techage:charcoal"] = 1, -- will be replaced by burntime
|
||||
["default:coal_lump"] = 1,
|
||||
["default:coalblock"] = 1,
|
||||
["techage:oil_source"] = 1,
|
||||
["techage:gas"] = 1,
|
||||
["techage:gasoline"] = 1,
|
||||
["techage:naphtha"] = 1,
|
||||
["techage:fueloil"] = 1,
|
||||
}
|
||||
|
||||
techage.firebox.ValidOilFuels = {
|
||||
["techage:gasoline"] = 1, -- category
|
||||
["techage:naphtha"] = 2,
|
||||
["techage:fueloil"] = 3,
|
||||
["techage:oil_source"] = 4,
|
||||
}
|
||||
|
||||
|
||||
local function determine_burntimes()
|
||||
for k,_ in pairs(techage.firebox.Burntime)do
|
||||
local fuel,_ = minetest.get_craft_result({method = "fuel", width = 1, items = {k}})
|
||||
techage.firebox.Burntime[k] = fuel.time
|
||||
end
|
||||
end
|
||||
minetest.register_on_mods_loaded(determine_burntimes)
|
||||
|
||||
function techage.firebox.formspec(nvm)
|
||||
local fuel_percent = 0
|
||||
if nvm.running then
|
||||
fuel_percent = ((nvm.burn_cycles or 1) * 100) / (nvm.burn_cycles_total or 1)
|
||||
end
|
||||
return "size[8,6]"..
|
||||
default.gui_bg..
|
||||
default.gui_bg_img..
|
||||
default.gui_slots..
|
||||
"box[0,-0.1;7.8,0.5;#c6e8ff]"..
|
||||
"label[3,-0.1;"..minetest.colorize( "#000000", S("Firebox")).."]"..
|
||||
"list[current_name;fuel;3,1;1,1;]"..
|
||||
"image[4,1;1,1;default_furnace_fire_bg.png^[lowpart:"..
|
||||
fuel_percent..":default_furnace_fire_fg.png]"..
|
||||
"list[current_player;main;0,2.3;8,4;]"..
|
||||
"listring[current_name;fuel]"..
|
||||
"listring[current_player;main]"..
|
||||
default.get_hotbar_bg(0, 2.3)
|
||||
end
|
||||
|
||||
function techage.firebox.can_dig(pos, player)
|
||||
local inv = M(pos):get_inventory()
|
||||
return inv:is_empty("fuel")
|
||||
end
|
||||
|
||||
function techage.firebox.allow_metadata_inventory_put(pos, listname, index, stack, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return 0
|
||||
end
|
||||
if techage.firebox.Burntime[stack:get_name()] then
|
||||
return stack:get_count()
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
function techage.firebox.allow_metadata_inventory_take(pos, listname, index, stack, player)
|
||||
if minetest.is_protected(pos, player:get_player_name()) then
|
||||
return 0
|
||||
end
|
||||
return stack:get_count()
|
||||
end
|
||||
|
||||
function techage.firebox.on_rightclick(pos, node, clicker)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
techage.set_activeformspec(pos, clicker)
|
||||
M(pos):set_string("formspec", techage.firebox.formspec(nvm))
|
||||
end
|
||||
|
||||
function techage.firebox.swap_node(pos, name)
|
||||
local node = techage.get_node_lvm(pos)
|
||||
if node.name == name then
|
||||
return
|
||||
end
|
||||
node.name = name
|
||||
minetest.swap_node(pos, node)
|
||||
end
|
||||
|
||||
function techage.firebox.get_fuel(pos)
|
||||
local inv = M(pos):get_inventory()
|
||||
local items = inv:get_stack("fuel", 1)
|
||||
if items:get_count() > 0 then
|
||||
local taken = items:take_item(1)
|
||||
inv:set_stack("fuel", 1, items)
|
||||
return taken
|
||||
end
|
||||
end
|
||||
|
||||
function techage.firebox.has_fuel(pos)
|
||||
local inv = M(pos):get_inventory()
|
||||
local items = inv:get_stack("fuel", 1)
|
||||
return items:get_count() > 0
|
||||
end
|
||||
|
||||
function techage.firebox.is_free_position(pos, player_name)
|
||||
local pos2 = techage.get_pos(pos, 'F')
|
||||
if minetest.is_protected(pos2, player_name) then
|
||||
minetest.chat_send_player(player_name, S("[TA] Area is protected!"))
|
||||
return false
|
||||
end
|
||||
local node = techage.get_node_lvm(pos2)
|
||||
local ndef = minetest.registered_nodes[node.name]
|
||||
if not ndef or not ndef.buildable_to then
|
||||
minetest.chat_send_player(player_name, S("[TA] Not enough space!"))
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function techage.firebox.set_firehole(pos, on)
|
||||
local param2 = techage.get_node_lvm(pos).param2
|
||||
local pos2 = techage.get_pos(pos, 'F')
|
||||
if on == true then
|
||||
minetest.swap_node(pos2, {name="techage:coalfirehole_on", param2 = param2})
|
||||
elseif on == false then
|
||||
minetest.swap_node(pos2, {name="techage:coalfirehole", param2 = param2})
|
||||
else
|
||||
local node = techage.get_node_lvm(pos2)
|
||||
if node.name == "techage:coalfirehole" or node.name == "techage:coalfirehole_on" then
|
||||
minetest.swap_node(pos2, {name="air"})
|
||||
end
|
||||
end
|
||||
end
|
955
techage/basis/fly_lib.lua
Normal file
955
techage/basis/fly_lib.lua
Normal file
@ -0,0 +1,955 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2020-2023 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
Block fly/move library
|
||||
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local M = minetest.get_meta
|
||||
local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local S2P = minetest.string_to_pos
|
||||
local S = techage.S
|
||||
|
||||
local flylib = {}
|
||||
|
||||
local function lvect_add_vec(lvect1, offs)
|
||||
if not lvect1 or not offs then return end
|
||||
|
||||
local lvect2 = {}
|
||||
for _, v in ipairs(lvect1) do
|
||||
lvect2[#lvect2 + 1] = vector.add(v, offs)
|
||||
end
|
||||
return lvect2
|
||||
end
|
||||
|
||||
-- yaw in radiant
|
||||
local function rotate(v, yaw)
|
||||
local sinyaw = math.sin(yaw)
|
||||
local cosyaw = math.cos(yaw)
|
||||
return {x = v.x * cosyaw - v.z * sinyaw, y = v.y, z = v.x * sinyaw + v.z * cosyaw}
|
||||
end
|
||||
|
||||
-- playername is needed for carts, to attach the player to the cart entity
|
||||
local function set_node(item, playername)
|
||||
local dest_pos = item.dest_pos
|
||||
local name = item.name or "air"
|
||||
local param2 = item.param2 or 0
|
||||
local nvm = techage.get_nvm(item.base_pos)
|
||||
local node = techage.get_node_lvm(dest_pos)
|
||||
local ndef1 = minetest.registered_nodes[name]
|
||||
local ndef2 = minetest.registered_nodes[node.name]
|
||||
|
||||
nvm.running = false
|
||||
M(item.base_pos):set_string("status", S("Stopped"))
|
||||
if ndef1 and ndef2 then
|
||||
if minecart.is_cart(name) and (minecart.is_rail(dest_pos, node.name) or minecart.is_cart(name)) then
|
||||
local player = playername and minetest.get_player_by_name(playername)
|
||||
minecart.place_and_start_cart(dest_pos, {name = name, param2 = param2}, item.cartdef, player)
|
||||
return
|
||||
elseif ndef2.buildable_to then
|
||||
local meta = M(dest_pos)
|
||||
if name ~= "techage:moveblock" then
|
||||
minetest.set_node(dest_pos, {name=name, param2=param2})
|
||||
meta:from_table(item.metadata or {})
|
||||
meta:set_string("ta_move_block", "")
|
||||
meta:set_int("ta_door_locked", 1)
|
||||
end
|
||||
return
|
||||
end
|
||||
local meta = M(dest_pos)
|
||||
if not meta:contains("ta_move_block") then
|
||||
meta:set_string("ta_move_block", minetest.serialize({name=name, param2=param2}))
|
||||
return
|
||||
end
|
||||
elseif ndef1 then
|
||||
if name ~= "techage:moveblock" then
|
||||
minetest.add_item(dest_pos, ItemStack(name))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Entity monitoring
|
||||
-------------------------------------------------------------------------------
|
||||
local queue = {}
|
||||
local first = 0
|
||||
local last = -1
|
||||
|
||||
local function push(item)
|
||||
last = last + 1
|
||||
queue[last] = item
|
||||
end
|
||||
|
||||
local function pop()
|
||||
if first > last then return end
|
||||
local item = queue[first]
|
||||
queue[first] = nil -- to allow garbage collection
|
||||
first = first + 1
|
||||
return item
|
||||
end
|
||||
|
||||
local function monitoring()
|
||||
local num = last - first + 1
|
||||
for _ = 1, num do
|
||||
local item = pop()
|
||||
if item.ttl >= techage.SystemTime then
|
||||
-- still valud
|
||||
push(item)
|
||||
elseif item.ttl ~= 0 then
|
||||
set_node(item)
|
||||
end
|
||||
end
|
||||
minetest.after(1, monitoring)
|
||||
end
|
||||
minetest.after(1, monitoring)
|
||||
|
||||
minetest.register_on_shutdown(function()
|
||||
local num = last - first + 1
|
||||
for _ = 1, num do
|
||||
local item = pop()
|
||||
if item.ttl ~= 0 then
|
||||
set_node(item)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
local function monitoring_add_entity(item)
|
||||
item.ttl = techage.SystemTime + 1
|
||||
push(item)
|
||||
end
|
||||
|
||||
local function monitoring_del_entity(item)
|
||||
-- Mark as timed out
|
||||
item.ttl = 0
|
||||
end
|
||||
|
||||
local function monitoring_trigger_entity(item)
|
||||
item.ttl = techage.SystemTime + 1
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- to_path function for the fly/move path
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
local function strsplit(text)
|
||||
text = text:gsub("\r\n", "\n")
|
||||
text = text:gsub("\r", "\n")
|
||||
return string.split(text, "\n", true)
|
||||
end
|
||||
|
||||
local function trim(s)
|
||||
return (s:gsub("^%s*(.-)%s*$", "%1"))
|
||||
end
|
||||
|
||||
function flylib.distance(v)
|
||||
return math.abs(v.x) + math.abs(v.y) + math.abs(v.z)
|
||||
end
|
||||
|
||||
function flylib.to_vector(s, max_dist)
|
||||
local x,y,z = unpack(string.split(s, ","))
|
||||
x = tonumber(x) or 0
|
||||
y = tonumber(y) or 0
|
||||
z = tonumber(z) or 0
|
||||
if x and y and z then
|
||||
if not max_dist or (math.abs(x) + math.abs(y) + math.abs(z)) <= max_dist then
|
||||
return {x = x, y = y, z = z}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function flylib.to_path(s, max_dist)
|
||||
local tPath
|
||||
local dist = 0
|
||||
|
||||
for _, line in ipairs(strsplit(s or "")) do
|
||||
line = trim(line)
|
||||
line = string.split(line, "--", true, 1)[1] or ""
|
||||
if line ~= "" then
|
||||
local v = flylib.to_vector(line)
|
||||
if v then
|
||||
dist = dist + flylib.distance(v)
|
||||
if not max_dist or dist <= max_dist then
|
||||
tPath = tPath or {}
|
||||
tPath[#tPath + 1] = v
|
||||
else
|
||||
return tPath, S("Error: Max. length of the flight route exceeded by @1 blocks !!", dist - max_dist)
|
||||
end
|
||||
else
|
||||
return tPath, S("Error: Invalid path !!")
|
||||
end
|
||||
end
|
||||
end
|
||||
return tPath
|
||||
end
|
||||
|
||||
local function next_path_pos(pos, lpath, idx)
|
||||
local offs = lpath[idx]
|
||||
if offs then
|
||||
return vector.add(pos, offs)
|
||||
end
|
||||
end
|
||||
|
||||
local function reverse_path(lpath)
|
||||
local lres = {}
|
||||
for i = #lpath, 1, -1 do
|
||||
lres[#lres + 1] = vector.multiply(lpath[i], -1)
|
||||
end
|
||||
return lres
|
||||
end
|
||||
|
||||
local function dest_offset(lpath)
|
||||
local offs = {x=0, y=0, z=0}
|
||||
for i = 1,#lpath do
|
||||
offs = vector.add(offs, lpath[i])
|
||||
end
|
||||
return offs
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Protect the doors from being opened by hand
|
||||
-------------------------------------------------------------------------------
|
||||
local function new_on_rightclick(old_on_rightclick)
|
||||
return function(pos, node, clicker, itemstack, pointed_thing)
|
||||
if M(pos):contains("ta_door_locked") then
|
||||
return itemstack
|
||||
end
|
||||
if old_on_rightclick then
|
||||
return old_on_rightclick(pos, node, clicker, itemstack, pointed_thing)
|
||||
else
|
||||
return itemstack
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function flylib.protect_door_from_being_opened(name)
|
||||
-- Change on_rightclick function.
|
||||
local ndef = minetest.registered_nodes[name]
|
||||
if ndef then
|
||||
local old_on_rightclick = ndef.on_rightclick
|
||||
minetest.override_item(ndef.name, {
|
||||
on_rightclick = new_on_rightclick(old_on_rightclick)
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Entity / Move / Attach / Detach
|
||||
-------------------------------------------------------------------------------
|
||||
local MIN_SPEED = 0.4
|
||||
local MAX_SPEED = 8
|
||||
local CORNER_SPEED = 4
|
||||
|
||||
local function calc_speed(v)
|
||||
return math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z)
|
||||
end
|
||||
|
||||
-- Only the ID ist stored, not the object
|
||||
local function get_object_id(object)
|
||||
for id, entity in pairs(minetest.luaentities) do
|
||||
if entity.object == object then
|
||||
return id
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- determine exact position of attached entities
|
||||
local function obj_pos(obj)
|
||||
local _, _, pos = obj:get_attach()
|
||||
if pos then
|
||||
pos = vector.divide(pos, 29)
|
||||
return vector.add(obj:get_pos(), pos)
|
||||
end
|
||||
end
|
||||
|
||||
-- Check access conflicts with other mods
|
||||
local function lock_player(player)
|
||||
local meta = player:get_meta()
|
||||
if meta:get_int("player_physics_locked") == 0 then
|
||||
meta:set_int("player_physics_locked", 1)
|
||||
meta:set_string("player_physics_locked_by", "ta_flylib")
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function unlock_player(player)
|
||||
local meta = player:get_meta()
|
||||
if meta:get_int("player_physics_locked") == 1 then
|
||||
if meta:get_string("player_physics_locked_by") == "ta_flylib" then
|
||||
meta:set_int("player_physics_locked", 0)
|
||||
meta:set_string("player_physics_locked_by", "")
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function detach_player(player)
|
||||
local pos = obj_pos(player)
|
||||
if pos then
|
||||
player:set_detach()
|
||||
player:set_properties({visual_size = {x=1, y=1}})
|
||||
player:set_pos(pos)
|
||||
end
|
||||
-- TODO: move to save position
|
||||
end
|
||||
|
||||
-- Attach player/mob to given parent object (block)
|
||||
local function attach_single_object(parent, obj, distance)
|
||||
local self = parent:get_luaentity()
|
||||
local res = obj:get_attach()
|
||||
if not res then -- not already attached
|
||||
local yaw
|
||||
if obj:is_player() then
|
||||
yaw = obj:get_look_horizontal()
|
||||
else
|
||||
yaw = obj:get_rotation().y
|
||||
end
|
||||
-- store for later use
|
||||
local offs = table.copy(distance)
|
||||
-- Calc entity rotation, which is relative to the parent's rotation
|
||||
local rot = parent:get_rotation()
|
||||
if self.param2 >= 20 then
|
||||
distance = rotate(distance, 2 * math.pi - rot.y)
|
||||
distance.y = -distance.y
|
||||
distance.x = -distance.x
|
||||
rot.y = rot.y - yaw
|
||||
elseif self.param2 < 4 then
|
||||
distance = rotate(distance, 2 * math.pi - rot.y)
|
||||
rot.y = rot.y - yaw
|
||||
end
|
||||
distance = vector.multiply(distance, 29)
|
||||
obj:set_attach(parent, "", distance, vector.multiply(rot, 180 / math.pi))
|
||||
obj:set_properties({visual_size = {x=2.9, y=2.9}})
|
||||
if obj:is_player() then
|
||||
if lock_player(obj) then
|
||||
table.insert(self.players, {name = obj:get_player_name(), offs = offs})
|
||||
end
|
||||
else
|
||||
table.insert(self.entities, {objID = get_object_id(obj), offs = offs})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Attach all objects around to the parent object
|
||||
-- offs is the search/attach position offset
|
||||
-- distance (optional) is the attach distance to the center of the entity
|
||||
local function attach_objects(pos, offs, parent, yoffs, distance)
|
||||
local pos1 = vector.add(pos, offs)
|
||||
for _, obj in pairs(minetest.get_objects_inside_radius(pos1, 0.9)) do
|
||||
-- keep relative object position
|
||||
distance = distance or vector.subtract(obj:get_pos(), pos)
|
||||
local entity = obj:get_luaentity()
|
||||
if entity then
|
||||
local mod = entity.name:gmatch("(.-):")()
|
||||
if techage.RegisteredMobsMods[mod] then
|
||||
distance.y = distance.y + yoffs
|
||||
attach_single_object(parent, obj, distance)
|
||||
end
|
||||
elseif obj:is_player() then
|
||||
attach_single_object(parent, obj, distance)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Detach all attached objects from the parent object
|
||||
local function detach_objects(pos, self)
|
||||
for _, item in ipairs(self.entities or {}) do
|
||||
local entity = minetest.luaentities[item.objID]
|
||||
if entity then
|
||||
local obj = entity.object
|
||||
obj:set_detach()
|
||||
obj:set_properties({visual_size = {x=1, y=1}})
|
||||
local pos1 = vector.add(pos, item.offs)
|
||||
pos1.y = pos1.y - (self.yoffs or 0)
|
||||
obj:set_pos(pos1)
|
||||
end
|
||||
end
|
||||
for _, item in ipairs(self.players or {}) do
|
||||
local obj = minetest.get_player_by_name(item.name)
|
||||
if obj then
|
||||
obj:set_detach()
|
||||
obj:set_properties({visual_size = {x=1, y=1}})
|
||||
local pos1 = vector.add(pos, item.offs)
|
||||
pos1.y = pos1.y + 0.1
|
||||
obj:set_pos(pos1)
|
||||
unlock_player(obj)
|
||||
end
|
||||
end
|
||||
self.entities = {}
|
||||
self.players = {}
|
||||
end
|
||||
|
||||
local function entity_to_node(pos, obj)
|
||||
local self = obj:get_luaentity()
|
||||
if self and self.item then
|
||||
local playername = self.players and self.players[1] and self.players[1].name
|
||||
detach_objects(pos, self)
|
||||
monitoring_del_entity(self.item)
|
||||
minetest.after(0.1, obj.remove, obj)
|
||||
set_node(self.item, playername)
|
||||
end
|
||||
end
|
||||
|
||||
-- Create a node entitiy.
|
||||
-- * base_pos is controller block related
|
||||
-- * start_pos and dest_pos are entity positions
|
||||
local function node_to_entity(base_pos, start_pos, dest_pos)
|
||||
local meta = M(start_pos)
|
||||
local node, metadata, cartdef
|
||||
|
||||
node = techage.get_node_lvm(start_pos)
|
||||
if minecart.is_cart(node.name) then
|
||||
cartdef = minecart.remove_cart(start_pos)
|
||||
elseif meta:contains("ta_move_block") then
|
||||
-- Move-block stored as metadata
|
||||
node = minetest.deserialize(meta:get_string("ta_move_block"))
|
||||
metadata = {}
|
||||
meta:set_string("ta_move_block", "")
|
||||
meta:set_string("ta_block_locked", "true")
|
||||
elseif not meta:contains("ta_block_locked") then
|
||||
-- Block with other metadata
|
||||
node = techage.get_node_lvm(start_pos)
|
||||
metadata = meta:to_table()
|
||||
minetest.after(0.1, minetest.remove_node, start_pos)
|
||||
else
|
||||
return
|
||||
end
|
||||
local obj = minetest.add_entity(start_pos, "techage:move_item")
|
||||
if obj then
|
||||
local self = obj:get_luaentity()
|
||||
local rot = techage.facedir_to_rotation(node.param2)
|
||||
obj:set_rotation(rot)
|
||||
obj:set_properties({wield_item=node.name})
|
||||
obj:set_armor_groups({immortal=1})
|
||||
|
||||
-- To be able to revert to node
|
||||
self.param2 = node.param2
|
||||
self.item = {
|
||||
name = node.name,
|
||||
param2 = node.param2,
|
||||
metadata = metadata or {},
|
||||
dest_pos = dest_pos,
|
||||
base_pos = base_pos,
|
||||
cartdef = cartdef,
|
||||
}
|
||||
monitoring_add_entity(self.item)
|
||||
|
||||
-- Prepare for attachments
|
||||
self.players = {}
|
||||
self.entities = {}
|
||||
-- Prepare for path walk
|
||||
self.path_idx = 1
|
||||
return obj, self.item.cartdef ~= nil
|
||||
end
|
||||
end
|
||||
|
||||
-- move block direction
|
||||
local function determine_dir(pos1, pos2)
|
||||
local vdist = vector.subtract(pos2, pos1)
|
||||
local ndist = vector.length(vdist)
|
||||
if ndist > 0 then
|
||||
return vector.divide(vdist, ndist)
|
||||
end
|
||||
return {x=0, y=0, z=0}
|
||||
end
|
||||
|
||||
local function move_entity(obj, next_pos, dir, is_corner)
|
||||
local self = obj:get_luaentity()
|
||||
self.next_pos = next_pos
|
||||
self.dir = dir
|
||||
if is_corner then
|
||||
local vel = vector.multiply(dir, math.min(CORNER_SPEED, self.max_speed))
|
||||
obj:set_velocity(vel)
|
||||
end
|
||||
local acc = vector.multiply(dir, self.max_speed / 2)
|
||||
obj:set_acceleration(acc)
|
||||
end
|
||||
|
||||
local function moveon_entity(obj, self, pos1)
|
||||
local pos2 = next_path_pos(pos1, self.lmove, self.path_idx)
|
||||
if pos2 then
|
||||
self.path_idx = self.path_idx + 1
|
||||
local dir = determine_dir(pos1, pos2)
|
||||
move_entity(obj, pos2, dir, true)
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
minetest.register_entity("techage:move_item", {
|
||||
initial_properties = {
|
||||
pointable = true,
|
||||
makes_footstep_sound = true,
|
||||
static_save = false,
|
||||
collide_with_objects = false,
|
||||
physical = false,
|
||||
visual = "wielditem",
|
||||
wield_item = "default:dirt",
|
||||
visual_size = {x=0.67, y=0.67, z=0.67},
|
||||
selectionbox = {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5},
|
||||
},
|
||||
|
||||
on_step = function(self, dtime, moveresult)
|
||||
local stop_obj = function(obj, self)
|
||||
local next_pos = self.next_pos
|
||||
obj:move_to(self.next_pos, true)
|
||||
obj:set_acceleration({x=0, y=0, z=0})
|
||||
obj:set_velocity({x=0, y=0, z=0})
|
||||
self.next_pos = nil
|
||||
self.old_dist = nil
|
||||
return next_pos
|
||||
end
|
||||
|
||||
if self.next_pos then
|
||||
local obj = self.object
|
||||
local pos = obj:get_pos()
|
||||
local dist = vector.distance(pos, self.next_pos)
|
||||
local speed = calc_speed(obj:get_velocity())
|
||||
self.old_dist = self.old_dist or dist
|
||||
|
||||
if self.lmove and self.lmove[self.path_idx] then
|
||||
local min_dist = math.min(1, self.max_speed / 8)
|
||||
if dist < min_dist or dist > self.old_dist then
|
||||
-- change of direction
|
||||
local next_pos = stop_obj(obj, self)
|
||||
if not moveon_entity(obj, self, next_pos) then
|
||||
minetest.after(0.5, entity_to_node, next_pos, obj)
|
||||
end
|
||||
return
|
||||
end
|
||||
elseif dist < 0.05 or dist > self.old_dist then
|
||||
-- Landing
|
||||
local next_pos = stop_obj(obj, self)
|
||||
local dest_pos = self.item.dest_pos or next_pos
|
||||
minetest.after(0.5, entity_to_node, dest_pos, obj)
|
||||
return
|
||||
end
|
||||
|
||||
self.old_dist = dist
|
||||
|
||||
-- Braking or limit max speed
|
||||
if speed > (dist * 2) or speed > self.max_speed then
|
||||
speed = math.min(speed, math.max(dist * 2, MIN_SPEED))
|
||||
local vel = vector.multiply(self.dir,speed)
|
||||
obj:set_velocity(vel)
|
||||
obj:set_acceleration({x=0, y=0, z=0})
|
||||
end
|
||||
|
||||
monitoring_trigger_entity(self.item)
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
local function is_valid_dest(pos)
|
||||
local node = techage.get_node_lvm(pos)
|
||||
if techage.is_air_like(node.name) then
|
||||
return true
|
||||
end
|
||||
if minecart.is_rail(pos, node.name) or minecart.is_cart(node.name) then
|
||||
return true
|
||||
end
|
||||
if not M(pos):contains("ta_move_block") then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function is_simple_node(pos)
|
||||
local node = techage.get_node_lvm(pos)
|
||||
if not minecart.is_rail(pos, node.name) then
|
||||
local ndef = minetest.registered_nodes[node.name]
|
||||
return node.name ~= "air" and techage.can_dig_node(node.name, ndef) or minecart.is_cart(node.name)
|
||||
end
|
||||
end
|
||||
|
||||
-- Move node from 'pos1' to the destination, calculated by means of 'lmove'
|
||||
-- * pos and meta are controller block related
|
||||
-- * lmove is the movement as a list of `moves`
|
||||
-- * height is move block height as value between 0 and 1 and used to calculate the offset
|
||||
-- for the attached object (player).
|
||||
local function move_node(pos, meta, pos1, lmove, max_speed, height)
|
||||
local pos2 = next_path_pos(pos1, lmove, 1)
|
||||
local offs = dest_offset(lmove)
|
||||
local dest_pos = vector.add(pos1, offs)
|
||||
-- optional for non-player objects
|
||||
local yoffs = meta:get_float("offset")
|
||||
|
||||
if pos2 then
|
||||
local dir = determine_dir(pos1, pos2)
|
||||
local obj, is_cart = node_to_entity(pos, pos1, dest_pos)
|
||||
|
||||
if obj then
|
||||
if is_cart then
|
||||
attach_objects(pos1, 0, obj, yoffs, {x = 0, y = -0.4, z = 0})
|
||||
else
|
||||
local offs = {x=0, y=height or 1, z=0}
|
||||
attach_objects(pos1, offs, obj, yoffs)
|
||||
if dir.y == 0 then
|
||||
if (dir.x ~= 0 and dir.z == 0) or (dir.x == 0 and dir.z ~= 0) then
|
||||
attach_objects(pos1, dir, obj, yoffs)
|
||||
end
|
||||
end
|
||||
end
|
||||
local self = obj:get_luaentity()
|
||||
self.path_idx = 2
|
||||
self.lmove = lmove
|
||||
self.max_speed = max_speed
|
||||
self.yoffs = yoffs
|
||||
move_entity(obj, pos2, dir)
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
-- Default Move Mode
|
||||
--
|
||||
|
||||
-- Move the nodes from nvm.lpos1 to nvm.lpos2
|
||||
-- * nvm.lpos1 is a list of nodes
|
||||
-- * lmove is the movement as a list of `moves`
|
||||
-- * pos, meta, and nvm are controller block related
|
||||
--- height is move block height as value between 0 and 1 and used to calculate the offset
|
||||
-- for the attached object (player).
|
||||
local function multi_move_nodes(pos, meta, nvm, lmove, max_speed, height, move2to1)
|
||||
local owner = meta:get_string("owner")
|
||||
techage.counting_add(owner, #lmove, #nvm.lpos1 * #lmove)
|
||||
|
||||
for idx = 1, #nvm.lpos1 do
|
||||
local pos1 = nvm.lpos1[idx]
|
||||
local pos2 = nvm.lpos2[idx]
|
||||
--print("multi_move_nodes", idx, P2S(pos1), P2S(pos2))
|
||||
|
||||
if move2to1 then
|
||||
pos1, pos2 = pos2, pos1
|
||||
end
|
||||
|
||||
if not minetest.is_protected(pos1, owner) and not minetest.is_protected(pos2, owner) then
|
||||
if is_simple_node(pos1) and is_valid_dest(pos2) then
|
||||
if move_node(pos, meta, pos1, lmove, max_speed, height) == false then
|
||||
meta:set_string("status", S("No valid node at the start position"))
|
||||
return false
|
||||
end
|
||||
else
|
||||
if not is_simple_node(pos1) then
|
||||
meta:set_string("status", S("No valid node at the start position"))
|
||||
minetest.chat_send_player(owner, " [techage] " .. S("No valid node at the start position") .. " at " .. P2S(pos1))
|
||||
else
|
||||
meta:set_string("status", S("No valid destination position"))
|
||||
minetest.chat_send_player(owner, " [techage] " .. S("No valid destination position") .. " at " .. P2S(pos2))
|
||||
end
|
||||
return false
|
||||
end
|
||||
else
|
||||
if minetest.is_protected(pos1, owner) then
|
||||
meta:set_string("status", S("Start position is protected"))
|
||||
minetest.chat_send_player(owner, " [techage] " .. S("Start position is protected") .. " at " .. P2S(pos1))
|
||||
else
|
||||
meta:set_string("status", S("Destination position is protected"))
|
||||
minetest.chat_send_player(owner, " [techage] " .. S("Destination position is protected") .. " at " .. P2S(pos2))
|
||||
end
|
||||
return false
|
||||
end
|
||||
end
|
||||
meta:set_string("status", S("Running"))
|
||||
return true
|
||||
end
|
||||
|
||||
-- Move the nodes from lpos1 to lpos2.
|
||||
-- * lpos1 is a list of nodes
|
||||
-- * lpos2 = lpos1 + move
|
||||
-- * pos and meta are controller block related
|
||||
-- * height is move block height as value between 0 and 1 and used to calculate the offset
|
||||
-- for the attached object (player).
|
||||
local function move_nodes(pos, meta, lpos1, move, max_speed, height)
|
||||
local owner = meta:get_string("owner")
|
||||
lpos1 = lpos1 or {}
|
||||
techage.counting_add(owner, #lpos1)
|
||||
|
||||
local lpos2 = {}
|
||||
for idx = 1, #lpos1 do
|
||||
|
||||
local pos1 = lpos1[idx]
|
||||
local pos2 = vector.add(lpos1[idx], move)
|
||||
lpos2[idx] = pos2
|
||||
|
||||
if not minetest.is_protected(pos1, owner) and not minetest.is_protected(pos2, owner) then
|
||||
if is_simple_node(pos1) and is_valid_dest(pos2) then
|
||||
move_node(pos, meta, pos1, {move}, max_speed, height)
|
||||
else
|
||||
if not is_simple_node(pos1) then
|
||||
meta:set_string("status", S("No valid node at the start position"))
|
||||
minetest.chat_send_player(owner, " [techage] " .. S("No valid node at the start position") .. " at " .. P2S(pos1))
|
||||
else
|
||||
meta:set_string("status", S("No valid destination position"))
|
||||
minetest.chat_send_player(owner, " [techage] " .. S("No valid destination position") .. " at " .. P2S(pos2))
|
||||
end
|
||||
return false, lpos1
|
||||
end
|
||||
else
|
||||
if minetest.is_protected(pos1, owner) then
|
||||
meta:set_string("status", S("Start position is protected"))
|
||||
minetest.chat_send_player(owner, " [techage] " .. S("Start position is protected") .. " at " .. P2S(pos1))
|
||||
else
|
||||
meta:set_string("status", S("Destination position is protected"))
|
||||
minetest.chat_send_player(owner, " [techage] " .. S("Destination position is protected") .. " at " .. P2S(pos2))
|
||||
end
|
||||
return false, lpos1
|
||||
end
|
||||
end
|
||||
|
||||
meta:set_string("status", S("Running"))
|
||||
return true, lpos2
|
||||
end
|
||||
|
||||
--
|
||||
-- Teleport Mode
|
||||
--
|
||||
local function is_player_available(lpos1)
|
||||
if #lpos1 == 1 then
|
||||
for _, obj in pairs(minetest.get_objects_inside_radius(lpos1[1], 0.9)) do
|
||||
if obj:is_player() then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function teleport(base_pos, pos1, pos2, meta, owner, lmove, max_speed)
|
||||
if not minetest.is_protected(pos1, owner) and not minetest.is_protected(pos2, owner) then
|
||||
local node1 = techage.get_node_lvm(pos1)
|
||||
local node2 = techage.get_node_lvm(pos2)
|
||||
if techage.is_air_like(node1.name) and techage.is_air_like(node2.name) then
|
||||
minetest.swap_node(pos1, {name = "techage:moveblock", param2 = 0})
|
||||
if move_node(base_pos, meta, pos1, lmove, max_speed, 0) == false then
|
||||
meta:set_string("status", S("No valid start position"))
|
||||
return false
|
||||
end
|
||||
else
|
||||
if not techage.is_air_like(node1.name) then
|
||||
meta:set_string("status", S("No valid start position"))
|
||||
minetest.chat_send_player(owner, " [techage] " .. S("No valid start position") .. " at " .. P2S(pos1))
|
||||
else
|
||||
meta:set_string("status", S("No valid destination position"))
|
||||
minetest.chat_send_player(owner, " [techage] " .. S("No valid destination position") .. " at " .. P2S(pos2))
|
||||
end
|
||||
return false
|
||||
end
|
||||
else
|
||||
if minetest.is_protected(pos1, owner) then
|
||||
meta:set_string("status", S("Start position is protected"))
|
||||
minetest.chat_send_player(owner, " [techage] " .. S("Start position is protected") .. " at " .. P2S(pos1))
|
||||
else
|
||||
meta:set_string("status", S("Destination position is protected"))
|
||||
minetest.chat_send_player(owner, " [techage] " .. S("Destination position is protected") .. " at " .. P2S(pos2))
|
||||
end
|
||||
return false
|
||||
end
|
||||
meta:set_string("status", S("Running"))
|
||||
return true
|
||||
end
|
||||
|
||||
-- Move the player from nvm.lpos1 to nvm.lpos2
|
||||
-- * nvm.lpos1 is a list of length one(!) with the not to be moved block below the player
|
||||
-- * lmove is the movement as a list of `moves`
|
||||
-- * pos, meta, and nvm are controller block related
|
||||
local function multi_teleport_player(base_pos, meta, nvm, lmove, max_speed, move2to1)
|
||||
local owner = meta:get_string("owner")
|
||||
techage.counting_add(owner, #lmove, #nvm.lpos1 * #lmove)
|
||||
|
||||
local pos1 = vector.add(nvm.lpos1[1], {x=0, y=1, z=0})
|
||||
local pos2 = vector.add(nvm.lpos2[1], {x=0, y=1, z=0})
|
||||
|
||||
if move2to1 then
|
||||
pos1, pos2 = pos2, pos1
|
||||
end
|
||||
|
||||
return teleport(base_pos, pos1, pos2, meta, owner, lmove, max_speed)
|
||||
end
|
||||
|
||||
-- Move the player from lpos1 to lpos2.
|
||||
-- * lpos1 is a list of length one(!) with the not to be moved block below the player
|
||||
-- * lpos2 = lpos1 + move
|
||||
-- * pos and meta are controller block related
|
||||
local function teleport_player(base_pos, meta, lpos1, move, max_speed)
|
||||
local owner = meta:get_string("owner")
|
||||
lpos1 = lpos1 or {}
|
||||
techage.counting_add(owner, #lpos1)
|
||||
|
||||
local pos1 = vector.add(lpos1[1], {x=0, y=1, z=0})
|
||||
local pos2 = vector.add(pos1, move)
|
||||
|
||||
return teleport(base_pos, pos1, pos2, meta, owner, {move}, max_speed), nil
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------------
|
||||
-- API
|
||||
--------------------------------------------------------------------------------------
|
||||
|
||||
-- move2to1 is the direction and is true for 'from pos2 to pos1'
|
||||
-- Move path and other data is stored as meta data of pos
|
||||
function flylib.move_to_other_pos(pos, move2to1)
|
||||
local meta = M(pos)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
local lmove, err = flylib.to_path(meta:get_string("path")) or {}
|
||||
local max_speed = meta:contains("max_speed") and meta:get_int("max_speed") or MAX_SPEED
|
||||
local height = meta:contains("height") and meta:get_float("height") or 1
|
||||
local teleport_mode = meta:get_string("teleport_mode") == "enable"
|
||||
|
||||
if err or nvm.running then return false end
|
||||
|
||||
height = techage.in_range(height, 0, 1)
|
||||
max_speed = techage.in_range(max_speed, MIN_SPEED, MAX_SPEED)
|
||||
nvm.lpos1 = nvm.lpos1 or {}
|
||||
|
||||
local offs = dest_offset(lmove)
|
||||
if move2to1 then
|
||||
lmove = reverse_path(lmove)
|
||||
end
|
||||
-- calc destination positions
|
||||
nvm.lpos2 = lvect_add_vec(nvm.lpos1, offs)
|
||||
local lpos = move2to1 and nvm.lpos2 or nvm.lpos1
|
||||
|
||||
if teleport_mode and is_player_available(lpos) then
|
||||
nvm.running = multi_teleport_player(pos, meta, nvm, lmove, max_speed, move2to1)
|
||||
elseif not teleport_mode then
|
||||
nvm.running = multi_move_nodes(pos, meta, nvm, lmove, max_speed, height, move2to1)
|
||||
end
|
||||
nvm.moveBA = nvm.running and not move2to1
|
||||
return nvm.running
|
||||
end
|
||||
|
||||
-- `move` the movement as a vector
|
||||
function flylib.move_to(pos, move)
|
||||
local meta = M(pos)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
local height = techage.in_range(meta:contains("height") and meta:get_float("height") or 1, 0, 1)
|
||||
local max_speed = meta:contains("max_speed") and meta:get_int("max_speed") or MAX_SPEED
|
||||
local teleport_mode = meta:get_string("teleport_mode") == "enable"
|
||||
|
||||
if nvm.running then return false end
|
||||
|
||||
-- TODO: Not working so far. There is no known 'nvm.lastpos' as start pos.
|
||||
--if teleport_mode and is_player_available(nvm.lpos1) then
|
||||
-- nvm.running, nvm.lastpos = teleport_player(pos, meta, nvm.lastpos or nvm.lpos1, move, max_speed)
|
||||
--elseif not teleport_mode then
|
||||
nvm.running, nvm.lastpos = move_nodes(pos, meta, nvm.lastpos or nvm.lpos1, move, max_speed, height)
|
||||
--end
|
||||
return nvm.running
|
||||
end
|
||||
|
||||
function flylib.reset_move(pos)
|
||||
local meta = M(pos)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
local height = techage.in_range(meta:contains("height") and meta:get_float("height") or 1, 0, 1)
|
||||
local max_speed = meta:contains("max_speed") and meta:get_int("max_speed") or MAX_SPEED
|
||||
|
||||
if nvm.running then return false end
|
||||
if meta:get_string("teleport_mode") == "enable" then return false end
|
||||
|
||||
if nvm.lpos1 and nvm.lpos1[1] then
|
||||
local move = vector.subtract(nvm.lpos1[1], (nvm.lastpos or nvm.lpos1)[1])
|
||||
|
||||
nvm.running, nvm.lastpos = move_nodes(pos, meta, nvm.lastpos or nvm.lpos1, move, max_speed, height)
|
||||
return nvm.running
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- pos is the controller block pos
|
||||
-- lpos is a list of node positions to be moved
|
||||
-- rot is one of "l", "r", "2l", "2r"
|
||||
function flylib.rotate_nodes(pos, lpos, rot)
|
||||
local meta = M(pos)
|
||||
local owner = meta:get_string("owner")
|
||||
-- cpos is the center pos
|
||||
local cpos = meta:contains("center") and flylib.to_vector(meta:get_string("center"))
|
||||
local lpos2 = techage.rotate_around_center(lpos, rot, cpos)
|
||||
local param2
|
||||
local nodes2 = {}
|
||||
|
||||
techage.counting_add(owner, #lpos * 2)
|
||||
|
||||
for i, pos1 in ipairs(lpos) do
|
||||
local node = techage.get_node_lvm(pos1)
|
||||
if rot == "l" then
|
||||
param2 = techage.param2_turn_right(node.param2)
|
||||
elseif rot == "r" then
|
||||
param2 = techage.param2_turn_left(node.param2)
|
||||
else
|
||||
param2 = techage.param2_turn_right(techage.param2_turn_right(node.param2))
|
||||
end
|
||||
if not minetest.is_protected(pos1, owner) and is_simple_node(pos1) then
|
||||
minetest.remove_node(pos1)
|
||||
nodes2[#nodes2 + 1] = {pos = lpos2[i], name = node.name, param2 = param2}
|
||||
end
|
||||
end
|
||||
for _,item in ipairs(nodes2) do
|
||||
if not minetest.is_protected(item.pos, owner) and is_valid_dest(item.pos) then
|
||||
minetest.add_node(item.pos, {name = item.name, param2 = item.param2})
|
||||
end
|
||||
end
|
||||
return lpos2
|
||||
end
|
||||
|
||||
function flylib.exchange_node(pos, name, param2)
|
||||
local meta = M(pos)
|
||||
local move_block
|
||||
|
||||
-- consider stored "objects"
|
||||
if meta:contains("ta_move_block") then
|
||||
move_block = meta:get_string("ta_move_block")
|
||||
end
|
||||
|
||||
minetest.swap_node(pos, {name = name, param2 = param2})
|
||||
|
||||
if move_block then
|
||||
meta:set_string("ta_move_block", move_block)
|
||||
end
|
||||
end
|
||||
|
||||
function flylib.remove_node(pos)
|
||||
local meta = M(pos)
|
||||
local move_block
|
||||
|
||||
-- consider stored "objects"
|
||||
if meta:contains("ta_move_block") then
|
||||
move_block = meta:get_string("ta_move_block")
|
||||
end
|
||||
|
||||
minetest.remove_node(pos)
|
||||
|
||||
if move_block then
|
||||
local node = minetest.deserialize(move_block)
|
||||
minetest.add_node(pos, node)
|
||||
meta:set_string("ta_move_block", "")
|
||||
end
|
||||
end
|
||||
|
||||
minetest.register_on_joinplayer(function(player)
|
||||
unlock_player(player)
|
||||
end)
|
||||
|
||||
minetest.register_on_leaveplayer(function(player)
|
||||
if unlock_player(player) then
|
||||
detach_player(player)
|
||||
end
|
||||
end)
|
||||
|
||||
minetest.register_on_dieplayer(function(player)
|
||||
if unlock_player(player) then
|
||||
detach_player(player)
|
||||
end
|
||||
end)
|
||||
|
||||
techage.flylib = flylib
|
52
techage/basis/formspec_update.lua
Normal file
52
techage/basis/formspec_update.lua
Normal file
@ -0,0 +1,52 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
Keep only one formspec active per player
|
||||
|
||||
]]--
|
||||
|
||||
local P2S = minetest.pos_to_string
|
||||
|
||||
local ActiveFormspecs = {}
|
||||
local ActivePlayer = {}
|
||||
|
||||
|
||||
function techage.is_activeformspec(pos)
|
||||
return ActiveFormspecs[P2S(pos)]
|
||||
end
|
||||
|
||||
function techage.set_activeformspec(pos, player)
|
||||
local name = player and player:get_player_name()
|
||||
if name then
|
||||
if ActivePlayer[name] then
|
||||
ActiveFormspecs[ActivePlayer[name]] = nil
|
||||
end
|
||||
ActivePlayer[name] = P2S(pos)
|
||||
ActiveFormspecs[P2S(pos)] = true
|
||||
end
|
||||
end
|
||||
|
||||
function techage.reset_activeformspec(pos, player)
|
||||
local name = player and player:get_player_name()
|
||||
if name then
|
||||
if ActivePlayer[name] then
|
||||
ActiveFormspecs[ActivePlayer[name]] = nil
|
||||
ActivePlayer[name] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
minetest.register_on_leaveplayer(function(player)
|
||||
local name = player:get_player_name()
|
||||
if ActivePlayer[name] then
|
||||
ActiveFormspecs[ActivePlayer[name]] = nil
|
||||
ActivePlayer[name] = nil
|
||||
end
|
||||
end)
|
197
techage/basis/fuel_lib.lua
Normal file
197
techage/basis/fuel_lib.lua
Normal file
@ -0,0 +1,197 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019-2021 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
Oil fuel burning lib
|
||||
|
||||
]]--
|
||||
|
||||
local S2P = minetest.string_to_pos
|
||||
local P2S = minetest.pos_to_string
|
||||
local M = minetest.get_meta
|
||||
local S = techage.S
|
||||
local Pipe = techage.LiquidPipe
|
||||
local liquid = networks.liquid
|
||||
local ValidOilFuels = techage.firebox.ValidOilFuels
|
||||
local Burntime = techage.firebox.Burntime
|
||||
|
||||
techage.fuel = {}
|
||||
|
||||
local CAPACITY = 50
|
||||
local BLOCKING_TIME = 0.3 -- 300ms
|
||||
|
||||
techage.fuel.CAPACITY = CAPACITY
|
||||
|
||||
-- fuel burning categories (equal or better than...)
|
||||
techage.fuel.BT_BITUMEN = 5
|
||||
techage.fuel.BT_OIL = 4
|
||||
techage.fuel.BT_FUELOIL = 3
|
||||
techage.fuel.BT_NAPHTHA = 2
|
||||
techage.fuel.BT_GASOLINE = 1
|
||||
|
||||
|
||||
function techage.fuel.fuel_container(x, y, nvm)
|
||||
local itemname = ""
|
||||
if nvm.liquid and nvm.liquid.name and nvm.liquid.amount and nvm.liquid.amount > 0 then
|
||||
itemname = nvm.liquid.name.." "..nvm.liquid.amount
|
||||
end
|
||||
local fuel_percent = 0
|
||||
if nvm.running or techage.is_running(nvm) then
|
||||
fuel_percent = ((nvm.burn_cycles or 1) * 100) / (nvm.burn_cycles_total or 1)
|
||||
end
|
||||
return "container["..x..","..y.."]"..
|
||||
"box[0,0;1.05,2.1;#000000]"..
|
||||
"image[0.1,0.1;1,1;default_furnace_fire_bg.png^[lowpart:"..
|
||||
fuel_percent..":default_furnace_fire_fg.png]"..
|
||||
techage.item_image(0.1, 1.1, itemname)..
|
||||
"container_end[]"
|
||||
end
|
||||
|
||||
local function help(x, y)
|
||||
local tooltip = S("To add fuel punch\nthis block\nwith a fuel container")
|
||||
return "label["..x..","..y..";"..minetest.colorize("#000000", minetest.formspec_escape("[?]")).."]"..
|
||||
"tooltip["..x..","..y..";0.5,0.5;"..tooltip..";#0C3D32;#FFFFFF]"
|
||||
end
|
||||
|
||||
function techage.fuel.formspec(nvm)
|
||||
local title = S("Fuel Menu")
|
||||
return "size[4,3]"..
|
||||
default.gui_bg..
|
||||
default.gui_bg_img..
|
||||
default.gui_slots..
|
||||
"box[0,-0.1;3.8,0.5;#c6e8ff]"..
|
||||
"label[1,-0.1;"..minetest.colorize("#000000", title).."]"..
|
||||
help(3.4, -0.1)..
|
||||
techage.fuel.fuel_container(1.5, 1, nvm)
|
||||
end
|
||||
|
||||
function techage.fuel.can_dig(pos, player)
|
||||
if not player or minetest.is_protected(pos, player:get_player_name()) then
|
||||
return false
|
||||
end
|
||||
local nvm = techage.get_nvm(pos)
|
||||
nvm.liquid = nvm.liquid or {}
|
||||
nvm.liquid.amount = nvm.liquid.amount or 0
|
||||
local inv = M(pos):get_inventory()
|
||||
return not inv or inv:is_empty("fuel") and nvm.liquid.amount == 0
|
||||
end
|
||||
|
||||
function techage.fuel.on_rightclick(pos, node, clicker)
|
||||
techage.set_activeformspec(pos, clicker)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
M(pos):set_string("formspec", techage.fuel.formspec(nvm))
|
||||
end
|
||||
|
||||
-- name is the fuel item name
|
||||
function techage.fuel.burntime(name)
|
||||
if ValidOilFuels[name] then
|
||||
return Burntime[name] or 0.01 -- not zero !
|
||||
end
|
||||
return 0.01 -- not zero !
|
||||
end
|
||||
|
||||
-- equal or better than the given category (see 'techage.fuel.BT_BITUMEN,...')
|
||||
function techage.fuel.valid_fuel(name, category)
|
||||
return ValidOilFuels[name] and ValidOilFuels[name] <= category
|
||||
end
|
||||
|
||||
function techage.fuel.on_punch(pos, node, puncher, pointed_thing)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
local mem = techage.get_mem(pos)
|
||||
mem.blocking_time = mem.blocking_time or 0
|
||||
if mem.blocking_time > techage.SystemTime then
|
||||
return
|
||||
end
|
||||
|
||||
local wielded_item = puncher:get_wielded_item():get_name()
|
||||
local item_count = puncher:get_wielded_item():get_count()
|
||||
local new_item = techage.liquid.fill_on_punch(nvm, wielded_item, item_count, puncher)
|
||||
if new_item then
|
||||
puncher:set_wielded_item(new_item)
|
||||
M(pos):set_string("formspec", techage.fuel.formspec(pos, nvm))
|
||||
mem.blocking_time = techage.SystemTime + BLOCKING_TIME
|
||||
return
|
||||
end
|
||||
|
||||
local ldef = techage.liquid.get_liquid_def(wielded_item)
|
||||
if ldef and ValidOilFuels[ldef.inv_item] then
|
||||
local lqd = (minetest.registered_nodes[node.name] or {}).liquid
|
||||
if not lqd.fuel_cat or ValidOilFuels[ldef.inv_item] <= lqd.fuel_cat then
|
||||
local new_item = techage.liquid.empty_on_punch(pos, nvm, wielded_item, item_count)
|
||||
if new_item then
|
||||
puncher:set_wielded_item(new_item)
|
||||
M(pos):set_string("formspec", techage.fuel.formspec(pos, nvm))
|
||||
mem.blocking_time = techage.SystemTime + BLOCKING_TIME
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function techage.fuel.get_fuel(nvm)
|
||||
if nvm.liquid and nvm.liquid.name and nvm.liquid.amount then
|
||||
if nvm.liquid.amount > 0 then
|
||||
nvm.liquid.amount = nvm.liquid.amount - 1
|
||||
return nvm.liquid.name
|
||||
end
|
||||
nvm.liquid.name = nil
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function techage.fuel.has_fuel(nvm)
|
||||
nvm.liquid = nvm.liquid or {}
|
||||
nvm.liquid.amount = nvm.liquid.amount or 0
|
||||
return nvm.liquid.amount > 0
|
||||
end
|
||||
|
||||
function techage.fuel.get_fuel_amount(nvm)
|
||||
if nvm.liquid and nvm.liquid.amount then
|
||||
return nvm.liquid.amount
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
function techage.fuel.get_liquid_table(valid_fuel, capacity, start_firebox)
|
||||
return {
|
||||
capa = capacity,
|
||||
fuel_cat = valid_fuel,
|
||||
peek = function(pos)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
return liquid.srv_peek(nvm)
|
||||
end,
|
||||
put = function(pos, indir, name, amount)
|
||||
if techage.fuel.valid_fuel(name, valid_fuel) then
|
||||
local nvm = techage.get_nvm(pos)
|
||||
local res = liquid.srv_put(nvm, name, amount, capacity)
|
||||
start_firebox(pos, nvm)
|
||||
if techage.is_activeformspec(pos) then
|
||||
M(pos):set_string("formspec", techage.fuel.formspec(nvm))
|
||||
end
|
||||
return res
|
||||
end
|
||||
return amount
|
||||
end,
|
||||
take = function(pos, indir, name, amount)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
amount, name = liquid.srv_take(nvm, name, amount)
|
||||
if techage.is_activeformspec(pos) then
|
||||
M(pos):set_string("formspec", techage.fuel.formspec(nvm))
|
||||
end
|
||||
return amount, name
|
||||
end,
|
||||
untake = function(pos, indir, name, amount)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
local leftover = liquid.srv_put(nvm, name, amount, capacity)
|
||||
if techage.is_activeformspec(pos) then
|
||||
M(pos):set_string("formspec", techage.fuel.formspec(nvm))
|
||||
end
|
||||
return leftover
|
||||
end
|
||||
}
|
||||
end
|
102
techage/basis/gravel_lib.lua
Normal file
102
techage/basis/gravel_lib.lua
Normal file
@ -0,0 +1,102 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
Gravel Sieve basis functions
|
||||
|
||||
]]--
|
||||
|
||||
-- Increase the probability over the natural occurrence
|
||||
local PROBABILITY_FACTOR = 2
|
||||
|
||||
-- Ore probability table (1/n)
|
||||
local ore_probability = {
|
||||
}
|
||||
|
||||
local ProbabilityCorrections = {
|
||||
["default:tin_lump"] = 0.3, -- extensively used
|
||||
["default:coal_lump"] = 0.2, -- extensively used
|
||||
["default:iron_lump"] = 0.5, -- extensively used
|
||||
["techage:baborium_lump"] = 99999, -- mining required
|
||||
}
|
||||
|
||||
-- collect all registered ores and calculate the probability
|
||||
local function add_ores()
|
||||
for _,item in pairs(minetest.registered_ores) do
|
||||
if not ore_probability[item.ore] and minetest.registered_nodes[item.ore] then
|
||||
local drop = minetest.registered_nodes[item.ore].drop
|
||||
if type(drop) == "string"
|
||||
and drop ~= item.ore
|
||||
and drop ~= ""
|
||||
and item.ore_type == "scatter"
|
||||
and item.wherein == "default:stone"
|
||||
and item.clust_scarcity ~= nil and item.clust_scarcity > 0
|
||||
and item.clust_num_ores ~= nil and item.clust_num_ores > 0
|
||||
and item.y_max ~= nil and item.y_min ~= nil then
|
||||
local factor = 0.5
|
||||
if item.y_max < -250 then
|
||||
factor = -250 / item.y_max
|
||||
end
|
||||
local probability = (techage.ore_rarity / PROBABILITY_FACTOR) * item.clust_scarcity /
|
||||
(item.clust_num_ores * factor)
|
||||
-- lower value means higher probability
|
||||
ore_probability[drop] = math.min(ore_probability[drop] or 100000, probability)
|
||||
end
|
||||
end
|
||||
end
|
||||
-- some corrections
|
||||
for key, correction in pairs(ProbabilityCorrections) do
|
||||
if ore_probability[key] then
|
||||
ore_probability[key] = ore_probability[key] * correction
|
||||
-- consider upper and lower level
|
||||
ore_probability[key] = techage.in_range(ore_probability[key], 10, 100000)
|
||||
end
|
||||
end
|
||||
local overall_probability = 0.0
|
||||
for name,probability in pairs(ore_probability) do
|
||||
minetest.log("info", string.format("[techage] %-32s %u", name, probability))
|
||||
overall_probability = overall_probability + 1.0/probability
|
||||
end
|
||||
minetest.log("info", string.format("[techage] Overall probability %g", overall_probability))
|
||||
end
|
||||
|
||||
minetest.register_on_mods_loaded(add_ores)
|
||||
|
||||
--
|
||||
-- Change the probability of ores or register new ores for sieving
|
||||
--
|
||||
function techage.register_ore_for_gravelsieve(ore_name, probability)
|
||||
ore_probability[ore_name] = probability
|
||||
end
|
||||
|
||||
-- determine ore based on the calculated probability
|
||||
function techage.gravelsieve_get_random_gravel_ore()
|
||||
for ore, probability in pairs(ore_probability) do
|
||||
if math.random(probability) == 1 then
|
||||
return ItemStack(ore)
|
||||
end
|
||||
end
|
||||
if math.random(2) == 1 then
|
||||
return ItemStack("default:gravel")
|
||||
else
|
||||
return ItemStack("techage:sieved_gravel")
|
||||
end
|
||||
end
|
||||
|
||||
function techage.gravelsieve_get_random_basalt_ore()
|
||||
if math.random(40) == 1 then
|
||||
return ItemStack("default:coal_lump")
|
||||
elseif math.random(40) == 1 then
|
||||
return ItemStack("default:iron_lump")
|
||||
elseif math.random(2) == 1 then
|
||||
return ItemStack("techage:basalt_gravel")
|
||||
else
|
||||
return ItemStack("techage:sieved_basalt_gravel")
|
||||
end
|
||||
end
|
240
techage/basis/hyperloop.lua
Normal file
240
techage/basis/hyperloop.lua
Normal file
@ -0,0 +1,240 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019-2022 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
For chests and tanks with hyperloop support
|
||||
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local S2P = minetest.string_to_pos
|
||||
local P2S = minetest.pos_to_string
|
||||
local M = minetest.get_meta
|
||||
local N = techage.get_node_lvm
|
||||
local S = techage.S
|
||||
|
||||
-- Will be initialized when mods are loaded
|
||||
local Stations = nil
|
||||
local Tube = nil
|
||||
local HYPERLOOP = nil
|
||||
|
||||
techage.hyperloop = {}
|
||||
|
||||
--[[
|
||||
|
||||
tStations["(x,y,z)"] = {
|
||||
conn = {dir = "(200,0,20)", ...},
|
||||
name = <node_type>, -- chest/tank
|
||||
owner = "singleplayer",
|
||||
conn_name = <own name>,
|
||||
single = true/nil,
|
||||
}
|
||||
|
||||
]]--
|
||||
|
||||
minetest.register_on_mods_loaded(function()
|
||||
if minetest.global_exists("hyperloop") then
|
||||
Stations = hyperloop.Stations
|
||||
Tube = hyperloop.Tube
|
||||
HYPERLOOP = true
|
||||
Tube:add_secondary_node_names({"techage:ta5_hl_chest", "techage:ta5_hl_tank"})
|
||||
end
|
||||
end)
|
||||
|
||||
local function get_remote_pos(pos, rmt_name)
|
||||
local owner = M(pos):get_string("owner")
|
||||
for key,item in pairs(Stations:get_node_table(pos)) do
|
||||
if item.owner == owner and item.conn_name == rmt_name then
|
||||
return S2P(key)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function get_free_server_list(pos, owner)
|
||||
if Stations and Stations.get_node_table then
|
||||
local tbl = {M(pos):get_string("remote_name")}
|
||||
for key,item in pairs(Stations:get_node_table(pos) or {}) do
|
||||
if item.single and item.owner == owner then
|
||||
if M(pos):get_string("node_type") == M(S2P(key)):get_string("node_type") then
|
||||
tbl[#tbl+1] = item.conn_name
|
||||
end
|
||||
end
|
||||
end
|
||||
tbl[#tbl+1] = ""
|
||||
return tbl
|
||||
end
|
||||
return {}
|
||||
end
|
||||
|
||||
local function on_lose_connection(pos, node_type)
|
||||
local name = techage.get_node_lvm(pos).name
|
||||
local ndef = minetest.registered_nodes[name]
|
||||
if ndef and ndef.on_lose_connection then
|
||||
ndef.on_lose_connection(pos, node_type)
|
||||
end
|
||||
end
|
||||
|
||||
local function on_dropdown(pos)
|
||||
if pos then
|
||||
local owner = M(pos):get_string("owner")
|
||||
return table.concat(get_free_server_list(pos, owner), ",") or ""
|
||||
end
|
||||
return ""
|
||||
end
|
||||
|
||||
local function update_node_data(pos, state, conn_name, remote_name, rmt_pos)
|
||||
local meta = M(pos)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
|
||||
if state == "server_connected" then
|
||||
Stations:update(pos, {conn_name=conn_name, single="nil"})
|
||||
meta:set_string("status", "server")
|
||||
meta:set_string("conn_name", conn_name)
|
||||
meta:set_string("remote_name", "")
|
||||
meta:set_string("conn_status", S("connected to") .. " " .. P2S(rmt_pos))
|
||||
nvm.rmt_pos = rmt_pos
|
||||
elseif state == "client_connected" then
|
||||
Stations:update(pos, {conn_name="nil", single="nil"})
|
||||
meta:set_string("status", "client")
|
||||
meta:set_string("conn_name", "")
|
||||
meta:set_string("remote_name", remote_name)
|
||||
meta:set_string("conn_status", S("connected to") .. " " .. P2S(rmt_pos))
|
||||
nvm.rmt_pos = rmt_pos
|
||||
elseif state == "server_not_connected" then
|
||||
Stations:update(pos, {conn_name=conn_name, single=true})
|
||||
meta:set_string("status", "server")
|
||||
meta:set_string("conn_name", conn_name)
|
||||
meta:set_string("remote_name", "")
|
||||
meta:set_string("conn_status", S("not connected"))
|
||||
nvm.rmt_pos = nil
|
||||
on_lose_connection(pos, "server")
|
||||
elseif state == "client_not_connected" then
|
||||
Stations:update(pos, {conn_name="nil", single=nil})
|
||||
meta:set_string("status", "not connected")
|
||||
meta:set_string("conn_name", "")
|
||||
meta:set_string("remote_name", "")
|
||||
meta:set_string("conn_status", S("not connected"))
|
||||
nvm.rmt_pos = nil
|
||||
on_lose_connection(pos, "client")
|
||||
end
|
||||
end
|
||||
|
||||
techage.hyperloop.SUBMENU = {
|
||||
{
|
||||
type = "label",
|
||||
label = S("Enter a block name or select an existing one"),
|
||||
tooltip = "",
|
||||
name = "l1",
|
||||
},
|
||||
{
|
||||
type = "ascii",
|
||||
name = "conn_name",
|
||||
label = S("Block name"),
|
||||
tooltip = S("Connection name for this block"),
|
||||
default = "",
|
||||
},
|
||||
{
|
||||
type = "dropdown",
|
||||
choices = "",
|
||||
on_dropdown = on_dropdown,
|
||||
name = "remote_name",
|
||||
label = S("Remote name"),
|
||||
tooltip = S("Connection name of the remote block"),
|
||||
},
|
||||
}
|
||||
|
||||
function techage.hyperloop.is_client(pos)
|
||||
if HYPERLOOP then
|
||||
local nvm = techage.get_nvm(pos)
|
||||
if Stations:get(nvm.rmt_pos) then
|
||||
if M(pos):get_string("status") == "client" then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function techage.hyperloop.is_server(pos)
|
||||
if HYPERLOOP then
|
||||
if M(pos):get_string("status") == "server" then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function techage.hyperloop.is_paired(pos)
|
||||
if HYPERLOOP then
|
||||
local nvm = techage.get_nvm(pos)
|
||||
if Stations:get(nvm.rmt_pos) then
|
||||
if M(pos):get_string("status") ~= "not connected" then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function techage.hyperloop.remote_pos(pos)
|
||||
if HYPERLOOP then
|
||||
local nvm = techage.get_nvm(pos)
|
||||
if Stations:get(nvm.rmt_pos) then
|
||||
if M(pos):contains("remote_name") then
|
||||
return nvm.rmt_pos or pos
|
||||
end
|
||||
end
|
||||
end
|
||||
return pos
|
||||
end
|
||||
|
||||
function techage.hyperloop.after_place_node(pos, placer, node_type)
|
||||
if HYPERLOOP then
|
||||
Stations:set(pos, node_type, {owner=placer:get_player_name()})
|
||||
M(pos):set_string("node_type", node_type)
|
||||
Tube:after_place_node(pos)
|
||||
end
|
||||
end
|
||||
|
||||
function techage.hyperloop.after_dig_node(pos, oldnode, oldmetadata, digger)
|
||||
if HYPERLOOP then
|
||||
local conn_name = oldmetadata.fields.conn_name
|
||||
local remote_name = oldmetadata.fields.remote_name
|
||||
local loc_pos, rmt_pos = pos, techage.get_nvm(pos).rmt_pos
|
||||
|
||||
-- Close connections
|
||||
if remote_name and rmt_pos then -- Connected client
|
||||
update_node_data(rmt_pos, "server_not_connected", remote_name, "")
|
||||
elseif conn_name and rmt_pos then -- Connected server
|
||||
update_node_data(rmt_pos, "client_not_connected", "", conn_name)
|
||||
end
|
||||
|
||||
Tube:after_dig_node(pos)
|
||||
Stations:delete(pos)
|
||||
end
|
||||
end
|
||||
|
||||
function techage.hyperloop.after_formspec(pos, fields)
|
||||
if HYPERLOOP and fields.save or fields.key_enter_field then
|
||||
local meta = M(pos)
|
||||
local conn_name = meta:get_string("conn_name")
|
||||
local remote_name = meta:get_string("remote_name")
|
||||
local status = meta:contains("status") and meta:get_string("status") or "not connected"
|
||||
local loc_pos, rmt_pos = pos, techage.get_nvm(pos).rmt_pos
|
||||
|
||||
if status == "not connected" then
|
||||
if fields.remote_name ~= "" then -- Client
|
||||
local rmt_pos = get_remote_pos(pos, fields.remote_name)
|
||||
if rmt_pos then
|
||||
update_node_data(loc_pos, "client_connected", "", fields.remote_name, rmt_pos)
|
||||
update_node_data(rmt_pos, "server_connected", fields.remote_name, "", loc_pos)
|
||||
end
|
||||
elseif fields.conn_name ~= "" then -- Server
|
||||
update_node_data(loc_pos, "server_not_connected", fields.conn_name, "")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
146
techage/basis/laser_lib.lua
Normal file
146
techage/basis/laser_lib.lua
Normal file
@ -0,0 +1,146 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019-2021 Joachim Stolberg
|
||||
|
||||
GPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
Laser basis functions
|
||||
|
||||
]]--
|
||||
|
||||
local Entities = {}
|
||||
local SIZES = {1, 2, 3, 6, 12, 24, 48} -- for laser entities
|
||||
local GAP_MIN = 1 -- between 2 blocks
|
||||
local GAP_MAX = 2 * 48 -- between 2 blocks
|
||||
|
||||
-- Return the area (pos1,pos2) for a destination node
|
||||
local function get_pos_range(pos, dir)
|
||||
local pos1 = vector.add(pos, vector.multiply(dir, GAP_MIN + 1)) -- min
|
||||
local pos2 = vector.add(pos, vector.multiply(dir, GAP_MAX + 1)) -- max
|
||||
return pos1, pos2
|
||||
end
|
||||
|
||||
-- Return first pos after start pos and the destination pos
|
||||
local function get_positions(pos, mem, dir)
|
||||
local pos1 = vector.add(pos, dir) -- start pos
|
||||
local _, pos2 = get_pos_range(pos, dir) -- last pos
|
||||
local _, pos3 = minetest.line_of_sight(pos1, pos2)
|
||||
pos3 = pos3 or pos2 -- destination node pos
|
||||
if not mem.peer_node_pos or not vector.equals(pos3, mem.peer_node_pos) then
|
||||
mem.peer_node_pos = pos3
|
||||
local dist = vector.distance(pos1, pos3)
|
||||
if dist > GAP_MIN and dist <= GAP_MAX then
|
||||
return true, pos1, pos3 -- new values
|
||||
else
|
||||
return false -- invalid values
|
||||
end
|
||||
end
|
||||
return true -- no new values
|
||||
end
|
||||
|
||||
-- return for both laser entities the pos and length
|
||||
local function get_laser_length_and_pos(pos1, pos2, dir)
|
||||
local dist = vector.distance(pos1, pos2)
|
||||
|
||||
for _, size in ipairs(SIZES) do
|
||||
if dist <= (size * 2) then
|
||||
pos1 = vector.add (pos1, vector.multiply(dir, (size / 2) - 0.5))
|
||||
pos2 = vector.subtract(pos2, vector.multiply(dir, (size / 2) + 0.5))
|
||||
return size, pos1, pos2
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function del_laser(pos)
|
||||
local key = minetest.hash_node_position(pos)
|
||||
local items = Entities[key]
|
||||
if items then
|
||||
local laser1, laser2 = items[1], items[2]
|
||||
laser1:remove()
|
||||
laser2:remove()
|
||||
Entities[key] = nil
|
||||
end
|
||||
return key
|
||||
end
|
||||
|
||||
local function add_laser(pos, pos1, pos2, size, param2)
|
||||
local key = del_laser(pos)
|
||||
|
||||
local laser1 = minetest.add_entity(pos1, "techage:laser" .. size)
|
||||
if laser1 then
|
||||
local yaw = math.pi / 2 * (param2 + 1)
|
||||
laser1:set_rotation({x = 0, y = yaw, z = 0})
|
||||
end
|
||||
|
||||
local laser2 = minetest.add_entity(pos2, "techage:laser" .. size)
|
||||
if laser2 then
|
||||
param2 = (param2 + 2) % 4 -- flip dir
|
||||
local yaw = math.pi / 2 * (param2 + 1)
|
||||
laser2:set_rotation({x = 0, y = yaw, z = 0})
|
||||
end
|
||||
|
||||
Entities[key] = {laser1, laser2}
|
||||
end
|
||||
|
||||
for _, size in ipairs(SIZES) do
|
||||
minetest.register_entity("techage:laser" .. size, {
|
||||
initial_properties = {
|
||||
visual = "cube",
|
||||
textures = {
|
||||
"techage_laser.png",
|
||||
"techage_laser.png",
|
||||
"techage_laser.png",
|
||||
"techage_laser.png",
|
||||
"techage_laser.png",
|
||||
"techage_laser.png",
|
||||
},
|
||||
use_texture_alpha = true,
|
||||
physical = false,
|
||||
collide_with_objects = false,
|
||||
pointable = false,
|
||||
static_save = false,
|
||||
visual_size = {x = size, y = 0.05, z = 0.05},
|
||||
glow = 14,
|
||||
shaded = true,
|
||||
},
|
||||
})
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- API functions
|
||||
-------------------------------------------------------------------------------
|
||||
-- if force is not true, do not redraw the laser if nothing has changed
|
||||
function techage.renew_laser(pos, force)
|
||||
local mem = techage.get_mem(pos)
|
||||
if force then
|
||||
mem.peer_node_pos = nil
|
||||
mem.param2 = nil
|
||||
end
|
||||
mem.param2 = mem.param2 or minetest.get_node(pos).param2
|
||||
local dir = minetest.facedir_to_dir(mem.param2)
|
||||
local res, pos1, pos2 = get_positions(pos, mem, dir)
|
||||
if pos1 then
|
||||
local size, pos3, pos4 = get_laser_length_and_pos(pos1, pos2, dir)
|
||||
if size then
|
||||
add_laser(pos, pos3, pos4, size, mem.param2)
|
||||
return res, pos1, pos2
|
||||
end
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
function techage.add_laser(pos, pos1, pos2)
|
||||
local dir = vector.direction(pos1, pos2)
|
||||
local param2 = minetest.dir_to_facedir(dir)
|
||||
local size, pos3, pos4 = get_laser_length_and_pos(pos1, pos2, dir)
|
||||
if size then
|
||||
add_laser(pos, pos3, pos4, size, param2)
|
||||
end
|
||||
end
|
||||
|
||||
-- techage.del_laser(pos)
|
||||
techage.del_laser = del_laser
|
25
techage/basis/legacy.lua
Normal file
25
techage/basis/legacy.lua
Normal file
@ -0,0 +1,25 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019-2021 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
For the transition from v0.26 to v1.0
|
||||
|
||||
]]--
|
||||
|
||||
function techage.register_node_for_v1_transition(nodenames, on_node_load)
|
||||
minetest.register_lbm({
|
||||
label = "[TechAge] V1 transition",
|
||||
name = nodenames[1].."transition",
|
||||
nodenames = nodenames,
|
||||
run_at_every_load = false,
|
||||
action = function(pos, node)
|
||||
on_node_load(pos, node)
|
||||
end
|
||||
})
|
||||
end
|
633
techage/basis/lib.lua
Normal file
633
techage/basis/lib.lua
Normal file
@ -0,0 +1,633 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019-2022 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
Helper functions
|
||||
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local P = minetest.string_to_pos
|
||||
local M = minetest.get_meta
|
||||
local S = techage.S
|
||||
|
||||
-- Input data to generate the Param2ToDir table
|
||||
local Input = {
|
||||
8,9,10,11, -- 1
|
||||
16,17,18,19, -- 2
|
||||
4,5,6,7, -- 3
|
||||
12,13,14,15, -- 4
|
||||
0,1,2,3, -- 5
|
||||
20,21,22,23, -- 6
|
||||
}
|
||||
|
||||
-- Input data to turn a "facedir" block to the right/left
|
||||
local ROTATION = {
|
||||
{5,14,11,16}, -- x+
|
||||
{7,12,9,18}, -- x-
|
||||
{0,1,2,3}, -- y+
|
||||
{22,21,20,23}, -- y-
|
||||
{6,15,8,17}, -- z+
|
||||
{4,13,10,19}, -- z-
|
||||
}
|
||||
|
||||
local FACEDIR_TO_ROT = {[0] =
|
||||
{x=0.000000, y=0.000000, z=0.000000},
|
||||
{x=0.000000, y=4.712389, z=0.000000},
|
||||
{x=0.000000, y=3.141593, z=0.000000},
|
||||
{x=0.000000, y=1.570796, z=0.000000},
|
||||
{x=4.712389, y=0.000000, z=0.000000},
|
||||
{x=3.141593, y=1.570796, z=1.570796},
|
||||
{x=1.570796, y=4.712389, z=4.712389},
|
||||
{x=3.141593, y=4.712389, z=4.712389},
|
||||
{x=1.570796, y=0.000000, z=0.000000},
|
||||
{x=0.000000, y=4.712389, z=1.570796},
|
||||
{x=4.712389, y=1.570796, z=4.712389},
|
||||
{x=0.000000, y=1.570796, z=4.712389},
|
||||
{x=0.000000, y=0.000000, z=1.570796},
|
||||
{x=4.712389, y=0.000000, z=1.570796},
|
||||
{x=0.000000, y=3.141593, z=4.712389},
|
||||
{x=1.570796, y=3.141593, z=4.712389},
|
||||
{x=0.000000, y=0.000000, z=4.712389},
|
||||
{x=1.570796, y=0.000000, z=4.712389},
|
||||
{x=0.000000, y=3.141593, z=1.570796},
|
||||
{x=4.712389, y=0.000000, z=4.712389},
|
||||
{x=0.000000, y=0.000000, z=3.141593},
|
||||
{x=0.000000, y=1.570796, z=3.141593},
|
||||
{x=0.000000, y=3.141593, z=3.141593},
|
||||
{x=0.000000, y=4.712389, z=3.141593},
|
||||
}
|
||||
|
||||
local RotationViaYAxis = {}
|
||||
|
||||
for _,row in ipairs(ROTATION) do
|
||||
for i = 1,4 do
|
||||
local val = row[i]
|
||||
local left = row[i == 1 and 4 or i - 1]
|
||||
local right = row[i == 4 and 1 or i + 1]
|
||||
RotationViaYAxis[val] = {left, right}
|
||||
end
|
||||
end
|
||||
|
||||
function techage.facedir_to_rotation(facedir)
|
||||
return FACEDIR_TO_ROT[facedir] or FACEDIR_TO_ROT[0]
|
||||
end
|
||||
|
||||
function techage.param2_turn_left(param2)
|
||||
return (RotationViaYAxis[param2] or RotationViaYAxis[0])[2]
|
||||
end
|
||||
|
||||
function techage.param2_turn_right(param2)
|
||||
return (RotationViaYAxis[param2] or RotationViaYAxis[0])[1]
|
||||
end
|
||||
|
||||
-- Roll a block in north direction (south is vice versa)
|
||||
local RollNorth = {
|
||||
{0,4,22,8},
|
||||
{1,5,23,9},
|
||||
{2,6,20,10},
|
||||
{3,7,21,11},
|
||||
{12,13,14,15},
|
||||
{16,19,18,17},
|
||||
}
|
||||
-- Roll a block in east direction (west is vice versa)
|
||||
local RollEast = {
|
||||
{0,12,20,16},
|
||||
{1,13,21,17},
|
||||
{2,14,22,18},
|
||||
{3,15,23,19},
|
||||
{4,7,6,5},
|
||||
{8,9,10,11},
|
||||
}
|
||||
|
||||
-- Generate a table for all facedir and param2 values:
|
||||
-- TurnUp[facedir][param2] = new_param2
|
||||
local TurnUp = {[0] = {}, {}, {}, {}}
|
||||
|
||||
for i = 1,6 do
|
||||
for j = 1,4 do
|
||||
local idx = RollNorth[i][j]
|
||||
TurnUp[0][idx] = RollNorth[i][j == 4 and 1 or j + 1] -- north
|
||||
TurnUp[2][idx] = RollNorth[i][j == 1 and 4 or j - 1] -- south
|
||||
|
||||
idx = RollEast[i][j]
|
||||
TurnUp[1][idx] = RollEast[i][j == 4 and 1 or j + 1] -- east
|
||||
TurnUp[3][idx] = RollEast[i][j == 1 and 4 or j - 1] -- west
|
||||
end
|
||||
end
|
||||
|
||||
-- facedir is from the players (0..3)
|
||||
-- param2 is from the node (0..23)
|
||||
function techage.param2_turn_up(facedir, param2)
|
||||
return TurnUp[facedir % 4][param2 % 24]
|
||||
end
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Rotate nodes around the center
|
||||
-------------------------------------------------------------------------------
|
||||
function techage.positions_center(lpos)
|
||||
local c = {x=0, y=0, z=0}
|
||||
for _,v in ipairs(lpos) do
|
||||
c = vector.add(c, v)
|
||||
end
|
||||
c = vector.divide(c, #lpos)
|
||||
c = vector.round(c)
|
||||
c.y = 0
|
||||
return c
|
||||
end
|
||||
|
||||
function techage.rotate_around_axis(v, c, turn)
|
||||
local dx, dz = v.x - c.x, v.z - c.z
|
||||
if turn == "l" then
|
||||
return {
|
||||
x = c.x - dz,
|
||||
y = v.y,
|
||||
z = c.z + dx,
|
||||
}
|
||||
elseif turn == "r" then
|
||||
return {
|
||||
x = c.x + dz,
|
||||
y = v.y,
|
||||
z = c.z - dx,
|
||||
}
|
||||
elseif turn == "" then
|
||||
return v
|
||||
else -- turn 180 degree
|
||||
return {
|
||||
x = c.x - dx,
|
||||
y = v.y,
|
||||
z = c.z - dz,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
-- Function returns a list ẃith the new node positions
|
||||
-- turn is one of "l", "r", "2l", "2r"
|
||||
-- cpos is the center pos (optional)
|
||||
function techage.rotate_around_center(nodes1, turn, cpos)
|
||||
cpos = cpos or techage.positions_center(nodes1)
|
||||
local nodes2 = {}
|
||||
for _,pos in ipairs(nodes1) do
|
||||
nodes2[#nodes2 + 1] = techage.rotate_around_axis(pos, cpos, turn)
|
||||
end
|
||||
return nodes2
|
||||
end
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Helper functions
|
||||
-------------------------------------------------------------------------------
|
||||
-- allowed for digging
|
||||
local SimpleNodes = {}
|
||||
|
||||
-- translation from param2 to dir (out of the node upwards)
|
||||
local Param2Dir = {}
|
||||
for idx,val in ipairs(Input) do
|
||||
Param2Dir[val] = math.floor((idx - 1) / 4) + 1
|
||||
end
|
||||
|
||||
-- used by lamps and power switches
|
||||
function techage.determine_node_bottom_as_dir(node)
|
||||
return tubelib2.Turn180Deg[Param2Dir[node.param2] or 1]
|
||||
end
|
||||
|
||||
function techage.determine_node_top_as_dir(node)
|
||||
return Param2Dir[node.param2] or 1
|
||||
end
|
||||
|
||||
-- rotation rules (screwdriver) for wallmounted "facedir" nodes
|
||||
function techage.rotate_wallmounted(param2)
|
||||
local offs = math.floor(param2 / 4) * 4
|
||||
local rot = ((param2 % 4) + 1) % 4
|
||||
return offs + rot
|
||||
end
|
||||
|
||||
function techage.in_range(val, min, max)
|
||||
val = tonumber(val)
|
||||
if val < min then return min end
|
||||
if val > max then return max end
|
||||
return val
|
||||
end
|
||||
|
||||
function techage.one_of(val, selection)
|
||||
for _,v in ipairs(selection) do
|
||||
if val == v then return val end
|
||||
end
|
||||
return selection[1]
|
||||
end
|
||||
|
||||
function techage.index(list, x)
|
||||
for idx, v in pairs(list) do
|
||||
if v == x then return idx end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function techage.in_list(list, x)
|
||||
for idx, v in pairs(list) do
|
||||
if v == x then return true end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function techage.add_to_set(set, x)
|
||||
if not techage.index(set, x) then
|
||||
table.insert(set, x)
|
||||
end
|
||||
end
|
||||
|
||||
-- techage.tbl_filter({"a", "b", "c", "d"}, function(v, k, t) return v >= "c" end) --> {"c","d"}
|
||||
techage.tbl_filter = function(t, filterIter)
|
||||
local out = {}
|
||||
|
||||
for k, v in pairs(t) do
|
||||
if filterIter(v, k, t) then out[k] = v end
|
||||
end
|
||||
|
||||
return out
|
||||
end
|
||||
|
||||
function techage.get_node_lvm(pos)
|
||||
local node = minetest.get_node_or_nil(pos)
|
||||
if node then
|
||||
return node
|
||||
end
|
||||
local vm = minetest.get_voxel_manip()
|
||||
local MinEdge, MaxEdge = vm:read_from_map(pos, pos)
|
||||
local data = vm:get_data()
|
||||
local param2_data = vm:get_param2_data()
|
||||
local area = VoxelArea:new({MinEdge = MinEdge, MaxEdge = MaxEdge})
|
||||
local idx = area:indexp(pos)
|
||||
if data[idx] and param2_data[idx] then
|
||||
return {
|
||||
name = minetest.get_name_from_content_id(data[idx]),
|
||||
param2 = param2_data[idx]
|
||||
}
|
||||
end
|
||||
return {name="ignore", param2=0}
|
||||
end
|
||||
|
||||
function techage.is_air_like(name)
|
||||
local ndef = minetest.registered_nodes[name]
|
||||
if ndef and ndef.buildable_to then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- returns true, if node can be dug, otherwise false
|
||||
function techage.can_dig_node(name, ndef)
|
||||
if not ndef then return false end
|
||||
if SimpleNodes[name] ~= nil then
|
||||
return SimpleNodes[name]
|
||||
end
|
||||
|
||||
if ndef.groups and ndef.groups.techage_door == 1 then
|
||||
SimpleNodes[name] = true
|
||||
return true
|
||||
end
|
||||
if name == "ignore" then
|
||||
SimpleNodes[name] = false
|
||||
return false
|
||||
end
|
||||
if name == "air" then
|
||||
SimpleNodes[name] = true
|
||||
return true
|
||||
end
|
||||
if ndef.buildable_to == true then
|
||||
SimpleNodes[name] = true
|
||||
return true
|
||||
end
|
||||
-- don't remove nodes with some intelligence or undiggable nodes
|
||||
if ndef.drop == "" then
|
||||
SimpleNodes[name] = false
|
||||
return false
|
||||
end
|
||||
if ndef.diggable == false then
|
||||
SimpleNodes[name] = false
|
||||
return false
|
||||
end
|
||||
if ndef.after_dig_node then
|
||||
SimpleNodes[name] = false
|
||||
return false
|
||||
end
|
||||
-- add it to the white list
|
||||
SimpleNodes[name] = true
|
||||
return true
|
||||
end
|
||||
|
||||
-- Simple nodes
|
||||
function techage.register_simple_nodes(node_names, is_valid)
|
||||
if is_valid == nil then is_valid = true end
|
||||
for _,name in ipairs(node_names or {}) do
|
||||
SimpleNodes[name] = is_valid
|
||||
end
|
||||
end
|
||||
|
||||
techage.dig_states = {
|
||||
NOT_DIGGABLE = 1,
|
||||
INV_FULL = 2,
|
||||
DUG = 3
|
||||
}
|
||||
|
||||
-- Digs a node like a player would by utilizing a fake player object.
|
||||
-- add_to_inv(itemstacks) is a method that should try to add the dropped stacks to an appropriate inventory.
|
||||
-- The node will only be dug, if add_to_inv(itemstacks) returns true.
|
||||
function techage.dig_like_player(pos, fake_player, add_to_inv)
|
||||
local node = techage.get_node_lvm(pos)
|
||||
local ndef = minetest.registered_nodes[node.name]
|
||||
if not ndef or ndef.diggable == false or (ndef.can_dig and not ndef.can_dig(pos, fake_player)) then
|
||||
return techage.dig_states.NOT_DIGGABLE
|
||||
end
|
||||
local drop_as_strings = minetest.get_node_drops(node)
|
||||
local drop_as_stacks = {}
|
||||
for _,itemstring in ipairs(drop_as_strings) do
|
||||
drop_as_stacks[#drop_as_stacks+1] = ItemStack(itemstring)
|
||||
end
|
||||
local meta = M(pos)
|
||||
if ndef.preserve_metadata then
|
||||
ndef.preserve_metadata(pos, node, meta, drop_as_stacks)
|
||||
end
|
||||
|
||||
if add_to_inv(drop_as_stacks) then
|
||||
local oldmeta = meta:to_table()
|
||||
minetest.remove_node(pos)
|
||||
|
||||
if ndef.after_dig_node then
|
||||
ndef.after_dig_node(pos, node, oldmeta, fake_player)
|
||||
end
|
||||
return techage.dig_states.DUG
|
||||
end
|
||||
return techage.dig_states.INV_FULL
|
||||
end
|
||||
|
||||
local function handle_drop(drop)
|
||||
-- To keep it simple, return only the item with the lowest rarity
|
||||
if drop.items then
|
||||
local rarity = 9999
|
||||
local name
|
||||
for idx,item in ipairs(drop.items) do
|
||||
if item.rarity and item.rarity < rarity then
|
||||
rarity = item.rarity
|
||||
name = item.items[1] -- take always the first item
|
||||
else
|
||||
return item.items[1] -- take always the first item
|
||||
end
|
||||
end
|
||||
return name
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- returns the node name, if node can be dropped, otherwise nil
|
||||
function techage.dropped_node(node, ndef)
|
||||
if node.name == "air" then return end
|
||||
--if ndef.buildable_to == true then return end
|
||||
if not ndef.diggable then return end
|
||||
if ndef.drop == "" then return end
|
||||
if type(ndef.drop) == "table" then
|
||||
return handle_drop(ndef.drop)
|
||||
end
|
||||
return ndef.drop or node.name
|
||||
end
|
||||
|
||||
-- needed for windmill plants
|
||||
local function determine_ocean_ids()
|
||||
techage.OceanIdTbl = {}
|
||||
for name, _ in pairs(minetest.registered_biomes) do
|
||||
if string.find(name, "ocean") then
|
||||
local id = minetest.get_biome_id(name)
|
||||
--print(id, name)
|
||||
techage.OceanIdTbl[id] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
determine_ocean_ids()
|
||||
|
||||
-- check if natural water is on given position (water placed by player has param2 = 1)
|
||||
function techage.is_ocean(pos)
|
||||
if pos.y > 1 then return false end
|
||||
local node = techage.get_node_lvm(pos)
|
||||
if node.name ~= "default:water_source" then return false end
|
||||
if node.param2 == 1 then return false end
|
||||
return true
|
||||
end
|
||||
|
||||
function techage.item_image(x, y, itemname, count)
|
||||
local name, size = unpack(string.split(itemname, " "))
|
||||
size = count and count or size
|
||||
size = tonumber(size) or 1
|
||||
local label = ""
|
||||
local text = minetest.formspec_escape(ItemStack(itemname):get_description())
|
||||
local tooltip = "tooltip["..x..","..y..";1,1;"..text..";#0C3D32;#FFFFFF]"
|
||||
|
||||
if minetest.registered_tools[name] and size > 1 then
|
||||
local offs = 0
|
||||
if size < 10 then
|
||||
offs = 0.65
|
||||
elseif size < 100 then
|
||||
offs = 0.5
|
||||
elseif size < 1000 then
|
||||
offs = 0.35
|
||||
else
|
||||
offs = 0.2
|
||||
end
|
||||
label = "label["..(x + offs)..","..(y + 0.45)..";"..tostring(size).."]"
|
||||
end
|
||||
|
||||
return "box["..x..","..y..";0.85,0.9;#808080]"..
|
||||
"item_image["..x..","..y..";1,1;"..itemname.."]"..
|
||||
tooltip..
|
||||
label
|
||||
end
|
||||
|
||||
function techage.item_image_small(x, y, itemname, tooltip_prefix)
|
||||
local name = unpack(string.split(itemname, " "))
|
||||
local tooltip = ""
|
||||
local ndef = minetest.registered_nodes[name] or minetest.registered_items[name] or minetest.registered_craftitems[name]
|
||||
|
||||
if ndef and ndef.description then
|
||||
local text = minetest.formspec_escape(ndef.description)
|
||||
tooltip = "tooltip["..x..","..y..";0.8,0.8;"..tooltip_prefix..": "..text..";#0C3D32;#FFFFFF]"
|
||||
end
|
||||
|
||||
return "box["..x..","..y..";0.65,0.7;#808080]"..
|
||||
"item_image["..x..","..y..";0.8,0.8;"..name.."]"..
|
||||
tooltip
|
||||
end
|
||||
|
||||
function techage.vector_dump(posses)
|
||||
local t = {}
|
||||
for _,pos in ipairs(posses) do
|
||||
t[#t + 1] = minetest.pos_to_string(pos)
|
||||
end
|
||||
return table.concat(t, " ")
|
||||
end
|
||||
|
||||
-- title bar help (width is the fornmspec width)
|
||||
function techage.question_mark_help(width, tooltip)
|
||||
local x = width- 0.6
|
||||
return "label["..x..",-0.1;"..minetest.colorize("#000000", minetest.formspec_escape("[?]")).."]"..
|
||||
"tooltip["..x..",-0.1;0.5,0.5;"..tooltip..";#0C3D32;#FFFFFF]"
|
||||
end
|
||||
|
||||
function techage.wrench_tooltip(x, y)
|
||||
local tooltip = S("Block has an\nadditional wrench menu")
|
||||
return "image["..x.."," .. y .. ";0.5,0.5;techage_inv_wrench.png]" ..
|
||||
"tooltip["..x..","..y..";0.5,0.5;"..tooltip..";#0C3D32;#FFFFFF]"
|
||||
end
|
||||
|
||||
techage.RegisteredMobsMods = {}
|
||||
|
||||
-- Register mobs mods for the move/fly controllers
|
||||
function techage.register_mobs_mods(mod)
|
||||
techage.RegisteredMobsMods[mod] = true
|
||||
end
|
||||
|
||||
function techage.beduino_signed_var(val)
|
||||
val = val or 0
|
||||
return val >= 32768 and val - 0x10000 or val
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Terminal history buffer
|
||||
-------------------------------------------------------------------------------
|
||||
local BUFFER_DEPTH = 10
|
||||
|
||||
function techage.historybuffer_add(pos, s)
|
||||
local mem = techage.get_mem(pos)
|
||||
mem.hisbuf = mem.hisbuf or {}
|
||||
|
||||
if #s > 2 then
|
||||
table.insert(mem.hisbuf, s)
|
||||
if #mem.hisbuf > BUFFER_DEPTH then
|
||||
table.remove(mem.hisbuf, 1)
|
||||
end
|
||||
mem.hisbuf_idx = #mem.hisbuf + 1
|
||||
end
|
||||
end
|
||||
|
||||
function techage.historybuffer_priv(pos)
|
||||
local mem = techage.get_mem(pos)
|
||||
mem.hisbuf = mem.hisbuf or {}
|
||||
mem.hisbuf_idx = mem.hisbuf_idx or 1
|
||||
|
||||
mem.hisbuf_idx = math.max(1, mem.hisbuf_idx - 1)
|
||||
return mem.hisbuf[mem.hisbuf_idx]
|
||||
end
|
||||
|
||||
function techage.historybuffer_next(pos)
|
||||
local mem = techage.get_mem(pos)
|
||||
mem.hisbuf = mem.hisbuf or {}
|
||||
mem.hisbuf_idx = mem.hisbuf_idx or 1
|
||||
|
||||
mem.hisbuf_idx = math.min(#mem.hisbuf, mem.hisbuf_idx + 1)
|
||||
return mem.hisbuf[mem.hisbuf_idx]
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Player TA5 Experience Points
|
||||
-------------------------------------------------------------------------------
|
||||
function techage.get_expoints(player)
|
||||
if player and player.get_meta then
|
||||
local meta = player:get_meta()
|
||||
if meta then
|
||||
return meta:get_int("techage_ex_points")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Can only be used from one collider
|
||||
function techage.add_expoint(player, number)
|
||||
if player and player.get_meta then
|
||||
local meta = player:get_meta()
|
||||
if meta then
|
||||
if not meta:contains("techage_collider_number") then
|
||||
meta:set_string("techage_collider_number", number)
|
||||
end
|
||||
if meta:get_string("techage_collider_number") == number then
|
||||
meta:set_int("techage_ex_points", meta:get_int("techage_ex_points") + 1)
|
||||
return true
|
||||
else
|
||||
minetest.chat_send_player(player:get_player_name(), "[techage] More than one collider is not allowed!")
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Delete number with: `//lua minetest.get_player_by_name("<name>"):get_meta():set_string("techage_collider_number", "")`
|
||||
|
||||
function techage.on_remove_collider(player)
|
||||
if player and player.get_meta then
|
||||
local meta = player:get_meta()
|
||||
if meta then
|
||||
meta:set_string("techage_collider_number", "")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function techage.set_expoints(player, ex_points)
|
||||
if player and player.get_meta then
|
||||
local meta = player:get_meta()
|
||||
if meta then
|
||||
meta:set_int("techage_ex_points", ex_points)
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Scheduler for a table-based, cyclic call of functions
|
||||
-------------------------------------------------------------------------------
|
||||
local TABLE_SIZE = 256
|
||||
techage.scheduler = {}
|
||||
|
||||
local function add_to_table(tbl, i, func)
|
||||
while i < TABLE_SIZE do
|
||||
if not tbl[i] then
|
||||
tbl[i] = func
|
||||
return i + 1
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
return i
|
||||
end
|
||||
|
||||
function techage.scheduler.init(pos)
|
||||
local mem = techage.get_mem(pos)
|
||||
mem.sched_idx = 0
|
||||
end
|
||||
|
||||
-- tFunc : (empty) table of functions
|
||||
-- call_rate : (2,4,8,16,32,64 or 128)
|
||||
-- offset : 0-128
|
||||
-- func : function to be called
|
||||
function techage.scheduler.register(tFunc, call_rate, offset, func)
|
||||
local i= 0
|
||||
while i < TABLE_SIZE do
|
||||
if (i % call_rate) == offset then
|
||||
i = add_to_table(tFunc, i, func)
|
||||
else
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
return tFunc
|
||||
end
|
||||
|
||||
-- tFunc : table of functions
|
||||
-- default : default function (optional)
|
||||
-- Returns a function to be called be the callee
|
||||
function techage.scheduler.get(pos, tFunc, default)
|
||||
local mem = techage.get_mem(pos)
|
||||
mem.sched_idx = ((mem.sched_idx or 0) + 1) % TABLE_SIZE
|
||||
return tFunc[mem.sched_idx] or default or function() end
|
||||
end
|
278
techage/basis/liquid_lib.lua
Normal file
278
techage/basis/liquid_lib.lua
Normal file
@ -0,0 +1,278 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019-2020 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
Liquid lib
|
||||
|
||||
]]--
|
||||
|
||||
local M = minetest.get_meta
|
||||
local S = techage.S
|
||||
local P2S = minetest.pos_to_string
|
||||
local LQD = function(pos) return (minetest.registered_nodes[techage.get_node_lvm(pos).name] or {}).liquid end
|
||||
|
||||
local BLOCKING_TIME = 0.3 -- 300ms
|
||||
|
||||
techage.liquid = {}
|
||||
local LiquidDef = {}
|
||||
local IsLiquid = {}
|
||||
local ContainerDef = {}
|
||||
|
||||
local function help(x, y)
|
||||
local tooltip = S("To add liquids punch\nthe tank\nwith a liquid container")
|
||||
return "label["..x..","..y..";"..minetest.colorize("#000000", minetest.formspec_escape("[?]")).."]"..
|
||||
"tooltip["..x..","..y..";0.5,0.5;"..tooltip..";#0C3D32;#FFFFFF]"
|
||||
end
|
||||
|
||||
function techage.liquid.formspec(pos, nvm, title)
|
||||
title = title or S("Liquid Tank")
|
||||
local itemname = "techage:liquid"
|
||||
if nvm.liquid and nvm.liquid.amount and nvm.liquid.amount > 0 and nvm.liquid.name then
|
||||
itemname = nvm.liquid.name.." "..nvm.liquid.amount
|
||||
end
|
||||
local name = minetest.get_node(pos).name
|
||||
if name == "techage:ta4_tank" then
|
||||
local meta = M(pos)
|
||||
local public = dump((meta:get_int("public") or 0) == 1)
|
||||
local keep_assignment = dump((meta:get_int("keep_assignment") or 0) == 1)
|
||||
return "size[8,3.5]"..
|
||||
"box[0,-0.1;7.8,0.5;#c6e8ff]"..
|
||||
"label[0.2,-0.1;"..minetest.colorize("#000000", title).."]"..
|
||||
help(7.4, -0.1)..
|
||||
techage.item_image(3.5, 1, itemname)..
|
||||
"checkbox[0.1,2.5;public;"..S("Allow public access to the tank")..";"..public.."]"..
|
||||
"checkbox[0.1,3;keep_assignment;"..S("keep assignment")..";"..keep_assignment.."]"
|
||||
else
|
||||
return "size[8,2]"..
|
||||
"box[0,-0.1;7.8,0.5;#c6e8ff]"..
|
||||
"label[0.2,-0.1;"..minetest.colorize("#000000", title).."]"..
|
||||
help(7.4, -0.1)..
|
||||
techage.item_image(3.5, 1, itemname)
|
||||
end
|
||||
end
|
||||
|
||||
function techage.liquid.is_empty(pos)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
return not nvm.liquid or (nvm.liquid.amount or 0) <= 0
|
||||
end
|
||||
|
||||
techage.liquid.recv_message = {
|
||||
on_recv_message = function(pos, src, topic, payload)
|
||||
if topic == "load" then
|
||||
local nvm = techage.get_nvm(pos)
|
||||
nvm.liquid = nvm.liquid or {}
|
||||
nvm.liquid.amount = nvm.liquid.amount or 0
|
||||
return techage.power.percent(LQD(pos).capa, nvm.liquid.amount), nvm.liquid.amount
|
||||
elseif topic == "size" then
|
||||
return LQD(pos).capa
|
||||
else
|
||||
return "unsupported"
|
||||
end
|
||||
end,
|
||||
on_beduino_request_data = function(pos, src, topic, payload)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
if topic == 128 then
|
||||
return 0, techage.get_node_lvm(pos).name
|
||||
elseif topic == 134 then
|
||||
local nvm = techage.get_nvm(pos)
|
||||
nvm.liquid = nvm.liquid or {}
|
||||
nvm.liquid.amount = nvm.liquid.amount or 0
|
||||
if payload[1] == 1 then
|
||||
local value = techage.power.percent(LQD(pos).capa, nvm.liquid.amount)
|
||||
return 0, {math.floor(value + 0.5)}
|
||||
else
|
||||
return 0, {nvm.liquid.amount}
|
||||
end
|
||||
else
|
||||
return 2, ""
|
||||
end
|
||||
end,
|
||||
}
|
||||
|
||||
-- like: register_liquid("techage:ta3_barrel_oil", "techage:ta3_barrel_empty", 10, "techage:oil")
|
||||
function techage.register_liquid(full_container, empty_container, container_size, inv_item)
|
||||
LiquidDef[full_container] = {container = empty_container, size = container_size, inv_item = inv_item}
|
||||
ContainerDef[empty_container] = ContainerDef[empty_container] or {}
|
||||
ContainerDef[empty_container][inv_item] = full_container
|
||||
IsLiquid[inv_item] = true
|
||||
if inv_item == "techage:water" and container_size == 1 then
|
||||
techage.register_water_bucket(empty_container, full_container)
|
||||
end
|
||||
end
|
||||
|
||||
local function get_liquid_def(full_container)
|
||||
return LiquidDef[full_container]
|
||||
end
|
||||
|
||||
local function get_container_def(container_name)
|
||||
return ContainerDef[container_name]
|
||||
end
|
||||
|
||||
local function is_container_empty(container_name)
|
||||
return ContainerDef[container_name]
|
||||
end
|
||||
|
||||
local function get_full_container(empty_container, inv_item)
|
||||
return ContainerDef[empty_container] and ContainerDef[empty_container][inv_item]
|
||||
end
|
||||
|
||||
-- used by filler
|
||||
local function fill_container(pos, inv, empty_container)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
nvm.liquid = nvm.liquid or {}
|
||||
nvm.liquid.amount = nvm.liquid.amount or 0
|
||||
local full_container = get_full_container(empty_container, nvm.liquid.name)
|
||||
if empty_container and full_container then
|
||||
local ldef = get_liquid_def(full_container)
|
||||
if ldef and nvm.liquid.amount - ldef.size >= 0 then
|
||||
if inv:room_for_item("dst", {name = full_container}) then
|
||||
inv:add_item("dst", {name = full_container})
|
||||
nvm.liquid.amount = nvm.liquid.amount - ldef.size
|
||||
if nvm.liquid.amount == 0 then
|
||||
nvm.liquid.name = nil
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
-- undo
|
||||
inv:add_item("src", {name = empty_container})
|
||||
return false
|
||||
end
|
||||
|
||||
-- used by filler
|
||||
local function empty_container(pos, inv, full_container)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
nvm.liquid = nvm.liquid or {}
|
||||
nvm.liquid.amount = nvm.liquid.amount or 0
|
||||
local ndef_lqd = LQD(pos)
|
||||
local tank_size = (ndef_lqd and ndef_lqd.capa) or 0
|
||||
local ldef = get_liquid_def(full_container)
|
||||
if ldef and (not nvm.liquid.name or ldef.inv_item == nvm.liquid.name) then
|
||||
if nvm.liquid.amount + ldef.size <= tank_size then
|
||||
if inv:room_for_item("dst", {name = ldef.container}) then
|
||||
inv:add_item("dst", {name = ldef.container})
|
||||
nvm.liquid.amount = nvm.liquid.amount + ldef.size
|
||||
nvm.liquid.name = ldef.inv_item
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
-- undo
|
||||
inv:add_item("src", {name = full_container})
|
||||
return false
|
||||
end
|
||||
|
||||
-- check if the wielded empty container can be replaced by a full
|
||||
-- container and added to the players inventory
|
||||
local function fill_on_punch(nvm, empty_container, item_count, puncher)
|
||||
nvm.liquid = nvm.liquid or {}
|
||||
nvm.liquid.amount = nvm.liquid.amount or 0
|
||||
local full_container = get_full_container(empty_container, nvm.liquid.name)
|
||||
if empty_container and full_container then
|
||||
local item = {name = full_container}
|
||||
local ldef = get_liquid_def(full_container)
|
||||
if ldef and nvm.liquid.amount - ldef.size >= 0 then
|
||||
if item_count > 1 then -- can't be simply replaced?
|
||||
-- check for extra free space
|
||||
local inv = puncher:get_inventory()
|
||||
if inv:room_for_item("main", {name = full_container}) then
|
||||
-- add full container and return
|
||||
-- the empty once - 1
|
||||
inv:add_item("main", {name = full_container})
|
||||
item = {name = empty_container, count = item_count - 1}
|
||||
else
|
||||
return -- no free space
|
||||
end
|
||||
end
|
||||
nvm.liquid.amount = nvm.liquid.amount - ldef.size
|
||||
if nvm.liquid.amount == 0 then
|
||||
nvm.liquid.name = nil
|
||||
end
|
||||
return item -- to be added to the players inv.
|
||||
end
|
||||
elseif nvm.liquid.name and not IsLiquid[nvm.liquid.name] then
|
||||
if empty_container == "" then
|
||||
local count = math.max(nvm.liquid.amount, 99)
|
||||
local name = nvm.liquid.name
|
||||
nvm.liquid.amount = nvm.liquid.amount - count
|
||||
if nvm.liquid.amount == 0 then
|
||||
nvm.liquid.name = nil
|
||||
end
|
||||
return {name = name, count = count}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function legacy_items(full_container, item_count)
|
||||
if full_container == "techage:isobutane" then
|
||||
return {container = "", size = item_count, inv_item = full_container}
|
||||
elseif full_container == "techage:oil_source" then
|
||||
return {container = "", size = item_count, inv_item = full_container}
|
||||
end
|
||||
end
|
||||
|
||||
-- check if the wielded full container can be emptied into the tank
|
||||
local function empty_on_punch(pos, nvm, full_container, item_count)
|
||||
nvm.liquid = nvm.liquid or {}
|
||||
nvm.liquid.amount = nvm.liquid.amount or 0
|
||||
local lqd_def = get_liquid_def(full_container) or legacy_items(full_container, item_count)
|
||||
local ndef_lqd = LQD(pos)
|
||||
if lqd_def and ndef_lqd then
|
||||
local tank_size = ndef_lqd.capa or 0
|
||||
if not nvm.liquid.name or lqd_def.inv_item == nvm.liquid.name then
|
||||
if nvm.liquid.amount + lqd_def.size <= tank_size then
|
||||
nvm.liquid.amount = nvm.liquid.amount + lqd_def.size
|
||||
nvm.liquid.name = lqd_def.inv_item
|
||||
return {name = lqd_def.container}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function techage.liquid.on_punch(pos, node, puncher, pointed_thing)
|
||||
local public = M(pos):get_int("public") == 1
|
||||
if not public and minetest.is_protected(pos, puncher:get_player_name()) then
|
||||
return
|
||||
end
|
||||
|
||||
local nvm = techage.get_nvm(pos)
|
||||
local mem = techage.get_mem(pos)
|
||||
mem.blocking_time = mem.blocking_time or 0
|
||||
if mem.blocking_time > techage.SystemTime then
|
||||
return
|
||||
end
|
||||
|
||||
local wielded_item = puncher:get_wielded_item():get_name()
|
||||
local item_count = puncher:get_wielded_item():get_count()
|
||||
local new_item = fill_on_punch(nvm, wielded_item, item_count, puncher)
|
||||
or empty_on_punch(pos, nvm, wielded_item, item_count)
|
||||
if new_item then
|
||||
puncher:set_wielded_item(new_item)
|
||||
M(pos):set_string("formspec", techage.fuel.formspec(pos, nvm))
|
||||
mem.blocking_time = techage.SystemTime + BLOCKING_TIME
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
function techage.liquid.get_liquid_amount(nvm)
|
||||
if nvm.liquid and nvm.liquid.amount then
|
||||
return nvm.liquid.amount
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
techage.liquid.get_liquid_def = get_liquid_def
|
||||
techage.liquid.get_container_def = get_container_def
|
||||
techage.liquid.is_container_empty = is_container_empty
|
||||
techage.liquid.get_full_container = get_full_container
|
||||
techage.liquid.fill_container = fill_container
|
||||
techage.liquid.empty_container = empty_container
|
||||
techage.liquid.fill_on_punch = fill_on_punch
|
||||
techage.liquid.empty_on_punch = empty_on_punch
|
91
techage/basis/manual.lua
Normal file
91
techage/basis/manual.lua
Normal file
@ -0,0 +1,91 @@
|
||||
techage.manual_DE = {}
|
||||
|
||||
techage.manual_DE.aTitel = {
|
||||
"1,SaferLua Controller with Periphery",
|
||||
"2,SaferLua Controller",
|
||||
"3,Central Server",
|
||||
"3,SaferLua Controller Terminal",
|
||||
}
|
||||
|
||||
techage.manual_DE.aText = {
|
||||
"",
|
||||
|
||||
"The SaferLua Controller is a small computer programmable in Lua to control your machinery.\n"..
|
||||
|
||||
"In contrast to the SmartLine Controller this controller allows to implement larger and smarter control and monitoring tasks.\n"..
|
||||
|
||||
"\n"..
|
||||
|
||||
"The controller can be programmed in SaferLua a subset of Lua for safe and secure Lua programs the Minetest server.\n"..
|
||||
|
||||
"\n",
|
||||
|
||||
"The Server node can be placed everywhere. It can also be used for communication purposes between several Controllers.\n"..
|
||||
|
||||
"The Server has a form to enter valid usernames for server access.\n"..
|
||||
|
||||
"\n"..
|
||||
|
||||
"The controller has a menu form with the following tabs:\n"..
|
||||
|
||||
"\n"..
|
||||
|
||||
" - the 'init' tab for the initialization code block\n"..
|
||||
|
||||
" - the 'func' tab for the Lua functions\n"..
|
||||
|
||||
" - the 'loop' tab for the main code block\n"..
|
||||
|
||||
" - the 'outp' tab for debugging outputs via '$print()'\n"..
|
||||
|
||||
" - the 'notes' tab for your code snippets or other notes\n"..
|
||||
|
||||
" - the 'help' tab with information to the available commands\n"..
|
||||
|
||||
"\n"..
|
||||
|
||||
"\n"..
|
||||
|
||||
"The controller needs battery power to work.\n"..
|
||||
|
||||
"\n",
|
||||
|
||||
"The Terminal is used to send command strings to the controller.\n"..
|
||||
|
||||
"In turn\\, the controller can send text strings to the terminal.\n"..
|
||||
|
||||
"The Terminal has a help system for internal commands. Its supports the following commands:\n"..
|
||||
|
||||
"\n"..
|
||||
|
||||
" - 'clear' = clear the screen\n"..
|
||||
|
||||
" - 'help' = output this message\n"..
|
||||
|
||||
" - 'pub' = switch terminal to public use (everybody can enter commands)\n"..
|
||||
|
||||
" - 'priv' = switch terminal to private use (only the owner can enter commands)\n"..
|
||||
|
||||
" - 'send <num> on/off' = send on/off event to e. g. lamps (for testing purposes)\n"..
|
||||
|
||||
" - 'msg <num> <text>' = send a text message to another Controller (for testing purposes)\n"..
|
||||
|
||||
"\n"..
|
||||
|
||||
"\n",
|
||||
|
||||
}
|
||||
|
||||
techage.manual_DE.aItemName = {
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
}
|
||||
|
||||
techage.manual_DE.aPlanTable = {
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
}
|
98
techage/basis/mark.lua
Normal file
98
techage/basis/mark.lua
Normal file
@ -0,0 +1,98 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
mark.lua:
|
||||
|
||||
]]--
|
||||
|
||||
local marker_region = {}
|
||||
|
||||
function techage.unmark_region(name)
|
||||
if marker_region[name] ~= nil then --marker already exists
|
||||
--wip: make the area stay loaded somehow
|
||||
for _, entity in ipairs(marker_region[name]) do
|
||||
entity:remove()
|
||||
end
|
||||
marker_region[name] = nil
|
||||
end
|
||||
end
|
||||
|
||||
function techage.mark_region(name, pos1, pos2, owner, secs)
|
||||
|
||||
if not name or not pos1 or not pos2 then return end
|
||||
|
||||
techage.unmark_region(name)
|
||||
|
||||
local thickness = 0.2
|
||||
local sizex, sizey, sizez = (1 + pos2.x - pos1.x) / 2, (1 + pos2.y - pos1.y) / 2, (1 + pos2.z - pos1.z) / 2
|
||||
local markers = {}
|
||||
|
||||
--XY plane markers
|
||||
for _, z in ipairs({pos1.z - 0.5, pos2.z + 0.5}) do
|
||||
local marker = minetest.add_entity({x=pos1.x + sizex - 0.5, y=pos1.y + sizey - 0.5, z=z}, "techage:region_cube")
|
||||
if marker ~= nil then
|
||||
marker:set_properties({
|
||||
visual_size={x=sizex * 2, y=sizey * 2},
|
||||
--collisionbox = {-sizex, -sizey, -thickness, sizex, sizey, thickness},
|
||||
collisionbox = {0,0,0, 0,0,0},
|
||||
})
|
||||
if owner then
|
||||
marker:set_nametag_attributes({text = owner})
|
||||
end
|
||||
marker:get_luaentity().player_name = name
|
||||
table.insert(markers, marker)
|
||||
end
|
||||
end
|
||||
|
||||
--YZ plane markers
|
||||
for _, x in ipairs({pos1.x - 0.5, pos2.x + 0.5}) do
|
||||
local marker = minetest.add_entity({x=x, y=pos1.y + sizey - 0.5, z=pos1.z + sizez - 0.5}, "techage:region_cube")
|
||||
if marker ~= nil then
|
||||
marker:set_properties({
|
||||
visual_size={x=sizez * 2, y=sizey * 2},
|
||||
--collisionbox = {-thickness, -sizey, -sizez, thickness, sizey, sizez},
|
||||
collisionbox = {0,0,0, 0,0,0},
|
||||
})
|
||||
marker:set_yaw(math.pi / 2)
|
||||
marker:get_luaentity().player_name = name
|
||||
table.insert(markers, marker)
|
||||
end
|
||||
end
|
||||
|
||||
marker_region[name] = markers
|
||||
minetest.after(secs or 20, techage.unmark_region, name)
|
||||
end
|
||||
|
||||
function techage.switch_region(name, pos1, pos2)
|
||||
if marker_region[name] ~= nil then --marker already exists
|
||||
techage.unmark_region(name)
|
||||
else
|
||||
techage.mark_region(name, pos1, pos2)
|
||||
end
|
||||
end
|
||||
|
||||
minetest.register_entity(":techage:region_cube", {
|
||||
initial_properties = {
|
||||
visual = "upright_sprite",
|
||||
textures = {"techage_cube_mark.png"},
|
||||
use_texture_alpha = true,
|
||||
physical = false,
|
||||
glow = 12,
|
||||
},
|
||||
on_step = function(self, dtime)
|
||||
if marker_region[self.player_name] == nil then
|
||||
self.object:remove()
|
||||
return
|
||||
end
|
||||
end,
|
||||
on_punch = function(self, hitter)
|
||||
techage.unmark_region(self.player_name)
|
||||
end,
|
||||
})
|
128
techage/basis/mark2.lua
Normal file
128
techage/basis/mark2.lua
Normal file
@ -0,0 +1,128 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
mark.lua:
|
||||
|
||||
]]--
|
||||
|
||||
local marker_region = {}
|
||||
|
||||
function techage.unmark_position(name)
|
||||
if marker_region[name] ~= nil then --marker already exists
|
||||
--wip: make the area stay loaded somehow
|
||||
for _, entity in ipairs(marker_region[name]) do
|
||||
entity:remove()
|
||||
end
|
||||
marker_region[name] = nil
|
||||
end
|
||||
end
|
||||
|
||||
function techage.mark_position(name, pos, nametag, color, time)
|
||||
local marker = minetest.add_entity(pos, "techage:position_cube")
|
||||
if marker ~= nil then
|
||||
marker:set_nametag_attributes({color = color, text = nametag})
|
||||
marker:get_luaentity().player_name = name
|
||||
if not marker_region[name] then
|
||||
marker_region[name] = {}
|
||||
end
|
||||
marker_region[name][#marker_region[name] + 1] = marker
|
||||
end
|
||||
minetest.after(time or 30, techage.unmark_position, name)
|
||||
end
|
||||
|
||||
function techage.mark_cube(name, pos1, pos2, nametag, color, time)
|
||||
local new_x = pos1.x + ((pos2.x - pos1.x) / 2)
|
||||
local new_y = pos1.y + ((pos2.y - pos1.y) / 2)
|
||||
local new_z = pos1.z + ((pos2.z - pos1.z) / 2)
|
||||
local size_x = math.abs(pos1.x - pos2.x) + 1
|
||||
local size_y = math.abs(pos1.y - pos2.y) + 1
|
||||
local size_z = math.abs(pos1.z - pos2.z) + 1
|
||||
|
||||
local marker = minetest.add_entity(
|
||||
{x = new_x, y = new_y, z = new_z}, "techage:position_cube")
|
||||
if marker ~= nil then
|
||||
marker:set_nametag_attributes({color = color, text = nametag, visual_size = {x = size_x, y = size_y, z = size_z}})
|
||||
marker:get_luaentity().player_name = name
|
||||
marker:set_properties({visual_size = {x = size_x, y = size_y, z = size_z}})
|
||||
if not marker_region[name] then
|
||||
marker_region[name] = {}
|
||||
end
|
||||
marker_region[name][#marker_region[name] + 1] = marker
|
||||
end
|
||||
minetest.after(time or 30, techage.unmark_position, name)
|
||||
end
|
||||
|
||||
minetest.register_entity(":techage:position_cube", {
|
||||
initial_properties = {
|
||||
visual = "cube",
|
||||
textures = {
|
||||
"techage_cube_mark.png",
|
||||
"techage_cube_mark.png",
|
||||
"techage_cube_mark.png",
|
||||
"techage_cube_mark.png",
|
||||
"techage_cube_mark.png",
|
||||
"techage_cube_mark.png",
|
||||
},
|
||||
use_texture_alpha = true,
|
||||
physical = false,
|
||||
visual_size = {x = 1.1, y = 1.1},
|
||||
collisionbox = {-0.55,-0.55,-0.55, 0.55,0.55,0.55},
|
||||
glow = 8,
|
||||
},
|
||||
on_step = function(self, dtime)
|
||||
if marker_region[self.player_name] == nil then
|
||||
self.object:remove()
|
||||
return
|
||||
end
|
||||
end,
|
||||
on_punch = function(self, hitter)
|
||||
techage.unmark_position(self.player_name)
|
||||
end,
|
||||
})
|
||||
|
||||
function techage.mark_side(name, pos, dir, nametag, color, time)
|
||||
local v = vector.multiply(tubelib2.Dir6dToVector[dir or 0], 0.7)
|
||||
local pos2 = vector.add(pos, v)
|
||||
|
||||
local marker = minetest.add_entity(pos2, "techage:position_side")
|
||||
if marker ~= nil then
|
||||
marker:set_nametag_attributes({color = color, text = nametag})
|
||||
marker:get_luaentity().player_name = name
|
||||
if dir == 2 or dir == 4 then
|
||||
marker:setyaw(math.pi / 2)
|
||||
end
|
||||
|
||||
if not marker_region[name] then
|
||||
marker_region[name] = {}
|
||||
end
|
||||
marker_region[name][#marker_region[name] + 1] = marker
|
||||
end
|
||||
minetest.after(time or 30, techage.unmark_position, name)
|
||||
end
|
||||
|
||||
minetest.register_entity(":techage:position_side", {
|
||||
initial_properties = {
|
||||
visual = "upright_sprite",
|
||||
textures = {"techage_side_mark.png"},
|
||||
physical = false,
|
||||
visual_size = {x = 1.1, y = 1.1, z = 1.1},
|
||||
collisionbox = {-0.55,-0.55,-0.55, 0.55,0.55,0.55},
|
||||
glow = 12,
|
||||
},
|
||||
on_step = function(self, dtime)
|
||||
if marker_region[self.player_name] == nil then
|
||||
self.object:remove()
|
||||
return
|
||||
end
|
||||
end,
|
||||
on_punch = function(self, hitter)
|
||||
techage.unmark_position(self.player_name)
|
||||
end,
|
||||
})
|
122
techage/basis/mark_lib.lua
Normal file
122
techage/basis/mark_lib.lua
Normal file
@ -0,0 +1,122 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2020-2021 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
Block marker lib for door/move/fly controller
|
||||
|
||||
]]--
|
||||
|
||||
local MAX_NUM = 128
|
||||
local marker = {}
|
||||
|
||||
local MarkedNodes = {} -- t[player] = {{entity, pos},...}
|
||||
local MaxNumber = {}
|
||||
local CurrentPos -- to mark punched entities
|
||||
|
||||
local function unmark_position(name, pos)
|
||||
pos = vector.round(pos)
|
||||
for idx,item in ipairs(MarkedNodes[name] or {}) do
|
||||
if vector.equals(pos, item.pos) then
|
||||
item.entity:remove()
|
||||
table.remove(MarkedNodes[name], idx)
|
||||
CurrentPos = pos
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function marker.unmark_all(name)
|
||||
for _,item in ipairs(MarkedNodes[name] or {}) do
|
||||
item.entity:remove()
|
||||
end
|
||||
MarkedNodes[name] = nil
|
||||
end
|
||||
|
||||
local function mark_position(name, pos)
|
||||
pos = vector.round(pos)
|
||||
if not CurrentPos or not vector.equals(pos, CurrentPos) then -- entity not punched?
|
||||
if #MarkedNodes[name] < MaxNumber[name] then
|
||||
local entity = minetest.add_entity(pos, "techage:block_marker")
|
||||
if entity ~= nil then
|
||||
entity:get_luaentity().player_name = name
|
||||
table.insert(MarkedNodes[name], {pos = pos, entity = entity})
|
||||
end
|
||||
CurrentPos = nil
|
||||
return true
|
||||
end
|
||||
end
|
||||
CurrentPos = nil
|
||||
end
|
||||
|
||||
function marker.get_poslist(name)
|
||||
local idx = 0
|
||||
local lst = {}
|
||||
for _,item in ipairs(MarkedNodes[name] or {}) do
|
||||
table.insert(lst, item.pos)
|
||||
idx = idx + 1
|
||||
if idx >= MAX_NUM then break end
|
||||
end
|
||||
return lst
|
||||
end
|
||||
|
||||
minetest.register_on_punchnode(function(pos, node, puncher, pointed_thing)
|
||||
if puncher and puncher:is_player() then
|
||||
local name = puncher:get_player_name()
|
||||
|
||||
if not MarkedNodes[name] then
|
||||
return
|
||||
end
|
||||
|
||||
mark_position(name, pointed_thing.under)
|
||||
end
|
||||
end)
|
||||
|
||||
function marker.start(name, max_num)
|
||||
MaxNumber[name] = max_num or 99
|
||||
MarkedNodes[name] = {}
|
||||
end
|
||||
|
||||
function marker.stop(name)
|
||||
MarkedNodes[name] = nil
|
||||
MaxNumber[name] = nil
|
||||
end
|
||||
|
||||
minetest.register_entity(":techage:block_marker", {
|
||||
initial_properties = {
|
||||
visual = "cube",
|
||||
textures = {
|
||||
"techage_cube_mark.png",
|
||||
"techage_cube_mark.png",
|
||||
"techage_cube_mark.png",
|
||||
"techage_cube_mark.png",
|
||||
"techage_cube_mark.png",
|
||||
"techage_cube_mark.png",
|
||||
},
|
||||
physical = false,
|
||||
visual_size = {x=1.1, y=1.1},
|
||||
collisionbox = {-0.55,-0.55,-0.55, 0.55,0.55,0.55},
|
||||
glow = 8,
|
||||
},
|
||||
on_step = function(self, dtime)
|
||||
self.ttl = (self.ttl or 2400) - 1
|
||||
if self.ttl <= 0 then
|
||||
local pos = self.object:get_pos()
|
||||
unmark_position(self.player_name, pos)
|
||||
end
|
||||
end,
|
||||
on_punch = function(self, hitter)
|
||||
local pos = self.object:get_pos()
|
||||
local name = hitter:get_player_name()
|
||||
if name == self.player_name then
|
||||
unmark_position(name, pos)
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
return marker
|
561
techage/basis/node_states.lua
Normal file
561
techage/basis/node_states.lua
Normal file
@ -0,0 +1,561 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019-2023 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
A state model/class for TechAge nodes.
|
||||
|
||||
]]--
|
||||
|
||||
|
||||
--[[
|
||||
|
||||
Node states:
|
||||
|
||||
+-----------------------------------+ +------------+
|
||||
| | | |
|
||||
| V V |
|
||||
| +---------+ |
|
||||
| | | |
|
||||
| +---------| STOPPED | |
|
||||
| | | | |
|
||||
| button | +---------+ |
|
||||
| | ^ |
|
||||
button | V | button |
|
||||
| +---------+ | | button
|
||||
| +--------->| |---------+ |
|
||||
| | power | RUNNING | |
|
||||
| | +------| |---------+ |
|
||||
| | | +---------+ | |
|
||||
| | | ^ | | |
|
||||
| | | | | | |
|
||||
| | V | V V |
|
||||
| +---------+ +----------+ +---------+ |
|
||||
| | | | | | | |
|
||||
+---| NOPOWER | | STANDBY/ | | FAULT |----------+
|
||||
| | | BLOCKED | | |
|
||||
+---------+ +----------+ +---------+
|
||||
|
||||
|
||||
| cycle time operational needs power
|
||||
+---------+------------+-------------+-------------
|
||||
| RUNNING normal yes yes
|
||||
| BLOCKED long yes no
|
||||
| STANDBY long yes no
|
||||
| NOPOWER long no no
|
||||
| FAULT none no no
|
||||
| STOPPED none no no
|
||||
|
||||
Node nvm data:
|
||||
"techage_state" - node state, like "RUNNING"
|
||||
"techage_countdown" - countdown to standby mode
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local S = function(pos) if pos then return minetest.pos_to_string(pos) end end
|
||||
local P = minetest.string_to_pos
|
||||
local M = minetest.get_meta
|
||||
local N = techage.get_node_lvm
|
||||
|
||||
local MAX_CYCLE_TIME = 20
|
||||
|
||||
--
|
||||
-- TechAge machine states
|
||||
--
|
||||
|
||||
techage.RUNNING = 1 -- in normal operation/turned on
|
||||
techage.BLOCKED = 2 -- a pushing node is blocked due to a full destination inventory
|
||||
techage.STANDBY = 3 -- nothing to do (e.g. no input items), or node (world) not loaded
|
||||
techage.NOPOWER = 4 -- only for power consuming nodes, no operation
|
||||
techage.FAULT = 5 -- any fault state (e.g. wrong source items), which can be fixed by the player
|
||||
techage.STOPPED = 6 -- not operational/turned off
|
||||
techage.UNLOADED = 7 -- Map block unloaded
|
||||
techage.INACTIVE = 8 -- Map block loaded but node is not actively working
|
||||
|
||||
techage.StatesImg = {
|
||||
"techage_inv_button_on.png",
|
||||
"techage_inv_button_warning.png",
|
||||
"techage_inv_button_standby.png",
|
||||
"techage_inv_button_nopower.png",
|
||||
"techage_inv_button_error.png",
|
||||
"techage_inv_button_off.png",
|
||||
}
|
||||
|
||||
local function error(pos, msg)
|
||||
minetest.log("error", "[TA states] "..msg.." at "..S(pos).." "..N(pos).name)
|
||||
end
|
||||
|
||||
-- Return state button image for the node inventory
|
||||
function techage.state_button(state)
|
||||
if state and state < 7 and state > 0 then
|
||||
return techage.StatesImg[state]
|
||||
end
|
||||
return "techage_inv_button_off.png"
|
||||
end
|
||||
|
||||
function techage.get_power_image(pos, nvm)
|
||||
local node = techage.get_node_lvm(pos)
|
||||
local s = "3" -- electrical power
|
||||
if string.find(node.name, "techage:ta2") then
|
||||
s = "2" -- axles power
|
||||
end
|
||||
return "techage_inv_powerT"..s..".png"
|
||||
end
|
||||
|
||||
-- State string based on button states
|
||||
techage.StateStrings = {"running", "blocked", "standby", "nopower", "fault", "stopped"}
|
||||
|
||||
--
|
||||
-- Local States
|
||||
--
|
||||
local RUNNING = techage.RUNNING
|
||||
local BLOCKED = techage.BLOCKED
|
||||
local STANDBY = techage.STANDBY
|
||||
local NOPOWER = techage.NOPOWER
|
||||
local FAULT = techage.FAULT
|
||||
local STOPPED = techage.STOPPED
|
||||
|
||||
|
||||
--
|
||||
-- NodeStates Class Functions
|
||||
--
|
||||
techage.NodeStates = {}
|
||||
local NodeStates = techage.NodeStates
|
||||
|
||||
local function can_start(pos, nvm)
|
||||
--if false, node goes in FAULT
|
||||
return true
|
||||
end
|
||||
|
||||
local function has_power(pos, nvm)
|
||||
--if false, node goes in NOPOWER
|
||||
return true
|
||||
end
|
||||
|
||||
local function swap_node(pos, new_name, old_name)
|
||||
local node = techage.get_node_lvm(pos)
|
||||
if node.name == new_name then
|
||||
return
|
||||
end
|
||||
if node.name == old_name then
|
||||
node.name = new_name
|
||||
minetest.swap_node(pos, node)
|
||||
end
|
||||
end
|
||||
|
||||
-- true if node_timer should be executed
|
||||
function techage.is_operational(nvm)
|
||||
local state = nvm.techage_state or STOPPED
|
||||
return state < NOPOWER
|
||||
end
|
||||
|
||||
function techage.is_running(nvm)
|
||||
return (nvm.techage_state or STOPPED) == RUNNING
|
||||
end
|
||||
|
||||
-- consumes power
|
||||
function techage.needs_power(nvm)
|
||||
local state = nvm.techage_state or STOPPED
|
||||
return state == RUNNING or state == NOPOWER
|
||||
end
|
||||
|
||||
-- consumes power
|
||||
function techage.needs_power2(state)
|
||||
state = state or STOPPED
|
||||
return state == RUNNING or state == NOPOWER
|
||||
end
|
||||
|
||||
function techage.get_state_string(nvm)
|
||||
return techage.StateStrings[nvm.techage_state or STOPPED]
|
||||
end
|
||||
|
||||
function NodeStates:new(attr)
|
||||
local o = {
|
||||
-- mandatory
|
||||
cycle_time = attr.cycle_time, -- for running state
|
||||
standby_ticks = attr.standby_ticks, -- for standby state
|
||||
-- optional
|
||||
countdown_ticks = attr.countdown_ticks or 1,
|
||||
node_name_passive = attr.node_name_passive,
|
||||
node_name_active = attr.node_name_active,
|
||||
infotext_name = attr.infotext_name,
|
||||
has_power = attr.has_power or has_power,
|
||||
can_start = attr.can_start or can_start,
|
||||
start_node = attr.start_node,
|
||||
stop_node = attr.stop_node,
|
||||
formspec_func = attr.formspec_func,
|
||||
on_state_change = attr.on_state_change,
|
||||
quick_start = attr.quick_start,
|
||||
}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end
|
||||
|
||||
function NodeStates:node_init(pos, nvm, number)
|
||||
nvm.techage_state = STOPPED
|
||||
M(pos):set_string("node_number", number)
|
||||
if self.infotext_name then
|
||||
M(pos):set_string("infotext", self.infotext_name.." "..number..": stopped")
|
||||
end
|
||||
if self.formspec_func then
|
||||
M(pos):set_string("formspec", self.formspec_func(self, pos, nvm))
|
||||
end
|
||||
end
|
||||
|
||||
-- to be used to re-start the timer outside of node_timer()
|
||||
local function start_timer_delayed(pos, cycle_time)
|
||||
local t = minetest.get_node_timer(pos)
|
||||
t:stop()
|
||||
if cycle_time > 0.9 then
|
||||
minetest.after(0.1, t.start, t, cycle_time)
|
||||
else
|
||||
error(pos, "invalid cycle_time")
|
||||
end
|
||||
end
|
||||
|
||||
function NodeStates:stop(pos, nvm)
|
||||
local state = nvm.techage_state or STOPPED
|
||||
nvm.techage_state = STOPPED
|
||||
if self.stop_node then
|
||||
self.stop_node(pos, nvm, state)
|
||||
end
|
||||
if self.node_name_passive then
|
||||
swap_node(pos, self.node_name_passive, self.node_name_active)
|
||||
end
|
||||
if self.infotext_name then
|
||||
local number = M(pos):get_string("node_number")
|
||||
M(pos):set_string("infotext", self.infotext_name.." "..number..": stopped")
|
||||
end
|
||||
if self.formspec_func then
|
||||
nvm.ta_state_tooltip = "stopped"
|
||||
M(pos):set_string("formspec", self.formspec_func(self, pos, nvm))
|
||||
end
|
||||
if self.on_state_change then
|
||||
self.on_state_change(pos, state, STOPPED)
|
||||
end
|
||||
if minetest.get_node_timer(pos):is_started() then
|
||||
minetest.get_node_timer(pos):stop()
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function NodeStates:start(pos, nvm)
|
||||
local state = nvm.techage_state or STOPPED
|
||||
if state ~= RUNNING and state ~= FAULT then
|
||||
local res = self.can_start(pos, nvm, state)
|
||||
if res ~= true then
|
||||
self:fault(pos, nvm, res)
|
||||
return false
|
||||
end
|
||||
if not self.has_power(pos, nvm, state) then
|
||||
self:nopower(pos, nvm)
|
||||
return false
|
||||
end
|
||||
nvm.techage_state = RUNNING
|
||||
if self.start_node then
|
||||
self.start_node(pos, nvm, state)
|
||||
end
|
||||
nvm.techage_countdown = self.countdown_ticks
|
||||
if self.node_name_active then
|
||||
swap_node(pos, self.node_name_active, self.node_name_passive)
|
||||
end
|
||||
if self.infotext_name then
|
||||
local number = M(pos):get_string("node_number")
|
||||
M(pos):set_string("infotext", self.infotext_name.." "..number..": running")
|
||||
end
|
||||
if self.formspec_func then
|
||||
nvm.ta_state_tooltip = "running"
|
||||
M(pos):set_string("formspec", self.formspec_func(self, pos, nvm))
|
||||
end
|
||||
if minetest.get_node_timer(pos):is_started() then
|
||||
minetest.get_node_timer(pos):stop()
|
||||
end
|
||||
if self.on_state_change then
|
||||
self.on_state_change(pos, state, RUNNING)
|
||||
end
|
||||
start_timer_delayed(pos, self.cycle_time)
|
||||
|
||||
if self.quick_start and state == STOPPED then
|
||||
self.quick_start(pos, 0)
|
||||
end
|
||||
self:trigger_state(pos, nvm)
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function NodeStates:standby(pos, nvm, err_string)
|
||||
local state = nvm.techage_state or STOPPED
|
||||
if state == RUNNING or state == BLOCKED then
|
||||
nvm.techage_state = STANDBY
|
||||
if self.node_name_passive then
|
||||
swap_node(pos, self.node_name_passive, self.node_name_active)
|
||||
end
|
||||
if self.infotext_name then
|
||||
local number = M(pos):get_string("node_number")
|
||||
M(pos):set_string("infotext", self.infotext_name.." "..number..": "..(err_string or "standby"))
|
||||
end
|
||||
if self.formspec_func then
|
||||
nvm.ta_state_tooltip = err_string or "standby"
|
||||
M(pos):set_string("formspec", self.formspec_func(self, pos, nvm))
|
||||
end
|
||||
if self.on_state_change then
|
||||
self.on_state_change(pos, state, STANDBY)
|
||||
end
|
||||
start_timer_delayed(pos, self.cycle_time * self.standby_ticks)
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- special case of standby for pushing nodes
|
||||
function NodeStates:blocked(pos, nvm, err_string)
|
||||
local state = nvm.techage_state or STOPPED
|
||||
if state == RUNNING then
|
||||
nvm.techage_state = BLOCKED
|
||||
if self.node_name_passive then
|
||||
swap_node(pos, self.node_name_passive, self.node_name_active)
|
||||
end
|
||||
if self.infotext_name then
|
||||
local number = M(pos):get_string("node_number")
|
||||
M(pos):set_string("infotext", self.infotext_name.." "..number..": "..(err_string or "blocked"))
|
||||
end
|
||||
if self.formspec_func then
|
||||
nvm.ta_state_tooltip = err_string or "blocked"
|
||||
M(pos):set_string("formspec", self.formspec_func(self, pos, nvm))
|
||||
end
|
||||
if self.on_state_change then
|
||||
self.on_state_change(pos, state, BLOCKED)
|
||||
end
|
||||
start_timer_delayed(pos, self.cycle_time * self.standby_ticks)
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function NodeStates:nopower(pos, nvm, err_string)
|
||||
local state = nvm.techage_state or RUNNING
|
||||
if state ~= NOPOWER then
|
||||
nvm.techage_state = NOPOWER
|
||||
if self.node_name_passive then
|
||||
swap_node(pos, self.node_name_passive, self.node_name_active)
|
||||
end
|
||||
if self.infotext_name then
|
||||
local number = M(pos):get_string("node_number")
|
||||
M(pos):set_string("infotext", self.infotext_name.." "..number..": "..(err_string or "no power"))
|
||||
end
|
||||
if self.formspec_func then
|
||||
nvm.ta_state_tooltip = err_string or "no power"
|
||||
M(pos):set_string("formspec", self.formspec_func(self, pos, nvm))
|
||||
end
|
||||
if self.on_state_change then
|
||||
self.on_state_change(pos, state, NOPOWER)
|
||||
end
|
||||
start_timer_delayed(pos, self.cycle_time * self.standby_ticks)
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function NodeStates:fault(pos, nvm, err_string)
|
||||
local state = nvm.techage_state or STOPPED
|
||||
err_string = err_string or "fault"
|
||||
if state == RUNNING or state == STOPPED then
|
||||
nvm.techage_state = FAULT
|
||||
if self.node_name_passive then
|
||||
swap_node(pos, self.node_name_passive, self.node_name_active)
|
||||
end
|
||||
if self.infotext_name then
|
||||
local number = M(pos):get_string("node_number")
|
||||
M(pos):set_string("infotext", self.infotext_name.." "..number..": "..err_string)
|
||||
end
|
||||
if self.formspec_func then
|
||||
nvm.ta_state_tooltip = err_string or "fault"
|
||||
M(pos):set_string("formspec", self.formspec_func(self, pos, nvm))
|
||||
end
|
||||
if self.on_state_change then
|
||||
self.on_state_change(pos, state, FAULT)
|
||||
end
|
||||
minetest.get_node_timer(pos):stop()
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function NodeStates:get_state(nvm)
|
||||
return nvm.techage_state or techage.STOPPED
|
||||
end
|
||||
|
||||
-- keep the timer running?
|
||||
function NodeStates:is_active(nvm)
|
||||
local state = nvm.techage_state or STOPPED
|
||||
return state < FAULT
|
||||
end
|
||||
|
||||
function NodeStates:start_if_standby(pos)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
if nvm.techage_state == STANDBY then
|
||||
self:start(pos, nvm)
|
||||
end
|
||||
end
|
||||
|
||||
-- To be called if node is idle.
|
||||
-- If countdown reaches zero, the node is set to STANDBY.
|
||||
function NodeStates:idle(pos, nvm)
|
||||
local countdown = (nvm.techage_countdown or 0) - 1
|
||||
nvm.techage_countdown = countdown
|
||||
if countdown <= 0 then
|
||||
self:standby(pos, nvm)
|
||||
end
|
||||
end
|
||||
|
||||
-- To be called after successful node action to raise the timer
|
||||
-- and keep the node in state RUNNING
|
||||
function NodeStates:keep_running(pos, nvm, val)
|
||||
-- set to RUNNING if not already done
|
||||
if nvm.techage_state ~= RUNNING then
|
||||
self:start(pos, nvm)
|
||||
end
|
||||
nvm.techage_countdown = val or 4
|
||||
nvm.last_active = minetest.get_gametime()
|
||||
end
|
||||
|
||||
function NodeStates:trigger_state(pos, nvm)
|
||||
nvm.last_active = minetest.get_gametime()
|
||||
end
|
||||
|
||||
-- Start/stop node based on button events.
|
||||
-- if function returns false, no button was pressed
|
||||
function NodeStates:state_button_event(pos, nvm, fields)
|
||||
if fields.state_button ~= nil then
|
||||
local state = nvm.techage_state or STOPPED
|
||||
if state == STOPPED or state == STANDBY or state == BLOCKED then
|
||||
if not self:start(pos, nvm) and (state == STANDBY or state == BLOCKED) then
|
||||
self:stop(pos, nvm)
|
||||
end
|
||||
elseif state == RUNNING or state == FAULT or state == NOPOWER then
|
||||
self:stop(pos, nvm)
|
||||
end
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function NodeStates:get_state_button_image(nvm)
|
||||
local state = nvm.techage_state or STOPPED
|
||||
return techage.state_button(state)
|
||||
end
|
||||
|
||||
function NodeStates:get_state_tooltip(nvm)
|
||||
local tp = nvm.ta_state_tooltip or ""
|
||||
return tp..";#0C3D32;#FFFFFF"
|
||||
end
|
||||
|
||||
-- command interface
|
||||
function NodeStates:on_receive_message(pos, topic, payload)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
if topic == "on" then
|
||||
self:start(pos, techage.get_nvm(pos))
|
||||
return true
|
||||
elseif topic == "off" then
|
||||
self:stop(pos, techage.get_nvm(pos))
|
||||
return true
|
||||
elseif topic == "state" then
|
||||
local node = minetest.get_node(pos)
|
||||
if node.name == "ignore" then -- unloaded node?
|
||||
return "unloaded"
|
||||
elseif nvm.techage_state == RUNNING then
|
||||
local ttl = (nvm.last_active or 0) + MAX_CYCLE_TIME
|
||||
if ttl < minetest.get_gametime() then
|
||||
return "inactive"
|
||||
end
|
||||
end
|
||||
return techage.get_state_string(techage.get_nvm(pos))
|
||||
elseif topic == "fuel" then
|
||||
return techage.fuel.get_fuel_amount(nvm)
|
||||
elseif topic == "load" then
|
||||
return techage.liquid.get_liquid_amount(nvm)
|
||||
else
|
||||
return "unsupported"
|
||||
end
|
||||
end
|
||||
|
||||
function NodeStates:on_beduino_receive_cmnd(pos, topic, payload)
|
||||
if topic == 1 then
|
||||
if payload[1] == 0 then
|
||||
self:stop(pos, techage.get_nvm(pos))
|
||||
return 0
|
||||
else
|
||||
self:start(pos, techage.get_nvm(pos))
|
||||
return 0
|
||||
end
|
||||
else
|
||||
return 2 -- unknown or invalid topic
|
||||
end
|
||||
end
|
||||
|
||||
function NodeStates:on_beduino_request_data(pos, topic, payload)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
if topic == 128 then
|
||||
return 0, techage.get_node_lvm(pos).name
|
||||
elseif topic == 129 then
|
||||
local node = minetest.get_node(pos)
|
||||
if node.name == "ignore" then -- unloaded node?
|
||||
return 0, {techage.UNLOADED}
|
||||
elseif nvm.techage_state == RUNNING then
|
||||
local ttl = (nvm.last_active or 0) + MAX_CYCLE_TIME
|
||||
if ttl < minetest.get_gametime() then
|
||||
return 0, {techage.INACTIVE}
|
||||
end
|
||||
end
|
||||
return 0, {nvm.techage_state or STOPPED}
|
||||
else
|
||||
return 2, "" -- topic is unknown or invalid
|
||||
end
|
||||
end
|
||||
|
||||
function NodeStates.get_beduino_state(pos)
|
||||
local node = minetest.get_node(pos)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
if node.name == "ignore" then -- unloaded node?
|
||||
return 0, {techage.UNLOADED}
|
||||
elseif nvm.techage_state == RUNNING then
|
||||
local ttl = (nvm.last_active or 0) + MAX_CYCLE_TIME
|
||||
if ttl < minetest.get_gametime() then
|
||||
return 0, {techage.INACTIVE}
|
||||
end
|
||||
end
|
||||
return 0, {nvm.techage_state or STOPPED}
|
||||
end
|
||||
|
||||
-- restart timer
|
||||
function NodeStates:on_node_load(pos)
|
||||
local nvm = techage.get_nvm(pos)
|
||||
local state = nvm.techage_state or STOPPED
|
||||
if state == RUNNING then
|
||||
minetest.get_node_timer(pos):start(self.cycle_time)
|
||||
elseif state < FAULT then
|
||||
minetest.get_node_timer(pos):start(self.cycle_time * self.standby_ticks)
|
||||
end
|
||||
end
|
||||
|
||||
minetest.register_node("techage:defect_dummy", {
|
||||
description = "Corrupted Node (to be replaced)",
|
||||
tiles = {
|
||||
"techage_filling_ta2.png^techage_frame_ta2.png",
|
||||
"techage_filling_ta2.png^techage_frame_ta2.png",
|
||||
"techage_filling_ta2.png^techage_frame_ta2.png^techage_appl_defect.png",
|
||||
"techage_filling_ta2.png^techage_frame_ta2.png^techage_appl_defect.png",
|
||||
"techage_filling_ta2.png^techage_frame_ta2.png^techage_appl_defect.png",
|
||||
"techage_filling_ta2.png^techage_frame_ta2.png^techage_appl_defect.png",
|
||||
},
|
||||
drop = "",
|
||||
groups = {cracky=2, crumbly=2, choppy=2, not_in_creative_inventory=1},
|
||||
is_ground_content = false,
|
||||
})
|
171
techage/basis/node_store.lua
Normal file
171
techage/basis/node_store.lua
Normal file
@ -0,0 +1,171 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2019-2020 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
Data storage system for node related volatile and non-volatile data.
|
||||
Non-volatile data is stored from time to time and at shutdown.
|
||||
Volatile data is lost at every shutdown.
|
||||
|
||||
]]--
|
||||
|
||||
local NvmStore = {} -- non-volatile data cache
|
||||
local MemStore = {} -- volatile data cache
|
||||
|
||||
local N = function(pos) print(minetest.pos_to_string(pos), minetest.get_node(pos).name) end
|
||||
|
||||
-------------------------------------------------------------------
|
||||
-- Backend
|
||||
-------------------------------------------------------------------
|
||||
local MP = minetest.get_modpath("techage")
|
||||
local techage_use_sqlite = minetest.settings:get_bool('techage_use_sqlite', false)
|
||||
local backend
|
||||
|
||||
if techage_use_sqlite then
|
||||
backend = dofile(MP .. "/basis/nodedata_sqlite.lua")
|
||||
else
|
||||
backend = dofile(MP .. "/basis/nodedata_meta.lua")
|
||||
end
|
||||
|
||||
-- return keys for mapblock and inner-mapblock addressing based on the node position
|
||||
local function get_keys(pos)
|
||||
local kx1, kx2 = math.floor(pos.x / 16) + 2048, pos.x % 16
|
||||
local ky1, ky2 = math.floor(pos.y / 16) + 2048, pos.y % 16
|
||||
local kz1, kz2 = math.floor(pos.z / 16) + 2048, pos.z % 16
|
||||
return kx1 * 4096 * 4096 + ky1 * 4096 + kz1, kx2 * 16 * 16 + ky2 * 16 + kz2
|
||||
end
|
||||
|
||||
local function pos_from_key(key1, key2)
|
||||
|
||||
local x1 = (math.floor(key1 / (4096 * 4096)) - 2048) * 16
|
||||
local y1 = ((math.floor(key1 / 4096) % 4096) - 2048) * 16
|
||||
local z1 = ((key1 % 4096) - 2048) * 16
|
||||
local x2 = math.floor(key2 / (16 * 16))
|
||||
local y2 = math.floor(key2 / 16) % 16
|
||||
local z2 = key2 % 16
|
||||
|
||||
return {x = x1 + x2, y = y1 + y2, z = z1 + z2}
|
||||
end
|
||||
|
||||
local function debug(key1, item)
|
||||
--local pos1 = pos_from_key(key1, 0)
|
||||
--local pos2 = {x = pos1.x + 15, y = pos1.y + 15, z = pos1.z + 15}
|
||||
--techage.mark_region("mapblock", pos1, pos2, "singleplayer", 5)
|
||||
|
||||
local cnt = 0
|
||||
for key2, tbl in pairs(item) do
|
||||
if key2 ~= "in_use" then
|
||||
cnt = cnt + 1
|
||||
--N(pos_from_key(key1, key2))
|
||||
end
|
||||
end
|
||||
print("mapblock", string.format("%09X", key1), cnt.." nodes")
|
||||
end
|
||||
|
||||
|
||||
-------------------------------------------------------------------
|
||||
-- Storage scheduler
|
||||
-------------------------------------------------------------------
|
||||
local CYCLE_TIME = 600 -- store data every 10 min
|
||||
local JobQueue = {}
|
||||
local first = 0
|
||||
local last = -1
|
||||
local SystemTime = 0
|
||||
|
||||
local function push(key)
|
||||
last = last + 1
|
||||
JobQueue[last] = {key = key, time = SystemTime + CYCLE_TIME}
|
||||
end
|
||||
|
||||
local function pop()
|
||||
if first > last then return end
|
||||
local item = JobQueue[first]
|
||||
if item.time <= SystemTime then
|
||||
JobQueue[first] = nil -- to allow garbage collection
|
||||
first = first + 1
|
||||
return item.key
|
||||
end
|
||||
end
|
||||
|
||||
-- check every 100 msec if any data has to be stored
|
||||
minetest.register_globalstep(function(dtime)
|
||||
SystemTime = SystemTime + dtime
|
||||
local key = pop()
|
||||
if key and NvmStore[key] then
|
||||
-- minetest.log("warning",
|
||||
-- string.format("[TA Storage] SystemTime = %.3f, #JobQueue = %d, in_use = %s",
|
||||
-- SystemTime, last - first, NvmStore[key].in_use))
|
||||
local t = minetest.get_us_time()
|
||||
if NvmStore[key].in_use then
|
||||
NvmStore[key].in_use = nil
|
||||
backend.store_mapblock_data(key, NvmStore[key])
|
||||
push(key)
|
||||
else
|
||||
NvmStore[key] = nil -- remove unused data from cache
|
||||
end
|
||||
t = minetest.get_us_time() - t
|
||||
if t > 20000 then
|
||||
minetest.log("warning", "[TA Storage] duration = "..(t/1000.0).." ms")
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
-------------------------------------------------------------------
|
||||
-- Store/Restore NVM data
|
||||
-------------------------------------------------------------------
|
||||
NvmStore = backend.restore_at_startup()
|
||||
|
||||
minetest.register_on_shutdown(function()
|
||||
backend.freeze_at_shutdown(NvmStore)
|
||||
end)
|
||||
|
||||
-------------------------------------------------------------------
|
||||
-- API functions
|
||||
-------------------------------------------------------------------
|
||||
-- Returns volatile node data as table
|
||||
function techage.get_mem(pos)
|
||||
local hash = minetest.hash_node_position(pos)
|
||||
if not MemStore[hash] then
|
||||
MemStore[hash] = {}
|
||||
end
|
||||
return MemStore[hash]
|
||||
end
|
||||
|
||||
-- Returns non-volatile node data as table
|
||||
function techage.get_nvm(pos)
|
||||
local key1, key2 = get_keys(pos)
|
||||
|
||||
if not NvmStore[key1] then
|
||||
NvmStore[key1] = backend.get_mapblock_data(key1)
|
||||
push(key1)
|
||||
end
|
||||
|
||||
local block = NvmStore[key1]
|
||||
block.in_use = true
|
||||
if not block[key2] then
|
||||
block[key2] = backend.get_node_data(pos)
|
||||
end
|
||||
return block[key2]
|
||||
end
|
||||
|
||||
function techage.peek_nvm(pos)
|
||||
local key1, key2 = get_keys(pos)
|
||||
local block = NvmStore[key1] or {}
|
||||
return block[key2] or {}
|
||||
end
|
||||
|
||||
-- To be called when a node is removed
|
||||
function techage.del_mem(pos)
|
||||
local hash = minetest.hash_node_position(pos)
|
||||
MemStore[hash] = nil
|
||||
|
||||
local key1, key2 = get_keys(pos)
|
||||
NvmStore[key1] = NvmStore[key1] or backend.get_mapblock_data(key1)
|
||||
NvmStore[key1][key2] = nil
|
||||
backend.store_mapblock_data(key1, NvmStore[key1])
|
||||
end
|
103
techage/basis/nodedata_meta.lua
Normal file
103
techage/basis/nodedata_meta.lua
Normal file
@ -0,0 +1,103 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2020 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
Storage backend for node related data as node metadata
|
||||
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local M = minetest.get_meta
|
||||
|
||||
local storage = techage.storage
|
||||
|
||||
-------------------------------------------------------------------
|
||||
-- Marshaling
|
||||
-------------------------------------------------------------------
|
||||
local use_marshal = minetest.settings:get_bool('techage_use_marshal', false)
|
||||
local MAR_MAGIC = 0x8e
|
||||
|
||||
-- default functions
|
||||
local serialize = minetest.serialize
|
||||
local deserialize = minetest.deserialize
|
||||
|
||||
if use_marshal then
|
||||
if not techage.IE then
|
||||
error("Please add 'secure.trusted_mods = techage' to minetest.conf!")
|
||||
end
|
||||
local marshal = techage.IE.require("marshal")
|
||||
if not marshal then
|
||||
error("Please install marshal via 'luarocks install lua-marshal'")
|
||||
end
|
||||
|
||||
serialize = marshal.encode
|
||||
|
||||
deserialize = function(s)
|
||||
if s ~= "" then
|
||||
if s:byte(1) == MAR_MAGIC then
|
||||
return marshal.decode(s)
|
||||
else
|
||||
return minetest.deserialize(s)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------
|
||||
-- API functions
|
||||
-------------------------------------------------------------------
|
||||
local api = {}
|
||||
|
||||
function api.get_mapblock_data(key)
|
||||
return {}
|
||||
end
|
||||
|
||||
function api.store_mapblock_data(key, mapblock_data)
|
||||
for key, item in pairs(mapblock_data) do
|
||||
if key ~= "in_use" then
|
||||
local pos = item and item._POS_
|
||||
if pos then
|
||||
item._POS_ = nil
|
||||
local data = serialize(item)
|
||||
item._POS_ = pos
|
||||
local meta = M(pos)
|
||||
meta:set_string("ta_data", data)
|
||||
meta:mark_as_private("ta_data")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function api.get_node_data(pos)
|
||||
local tbl = {}
|
||||
local s = M(pos):get_string("ta_data")
|
||||
|
||||
if s ~= "" then
|
||||
tbl = deserialize(s) or {}
|
||||
end
|
||||
tbl._POS_ = table.copy(pos)
|
||||
|
||||
return tbl
|
||||
end
|
||||
|
||||
-- Meta data can't be written reliable at shutdown,
|
||||
-- so we have to store/restore the data differently
|
||||
function api.freeze_at_shutdown(data)
|
||||
storage:set_string("shutdown_nodedata", serialize(data))
|
||||
end
|
||||
|
||||
function api.restore_at_startup()
|
||||
local s = storage:get_string("shutdown_nodedata")
|
||||
if s ~= "" then
|
||||
return deserialize(s) or {}
|
||||
end
|
||||
return {}
|
||||
end
|
||||
|
||||
return api
|
123
techage/basis/nodedata_sqlite.lua
Normal file
123
techage/basis/nodedata_sqlite.lua
Normal file
@ -0,0 +1,123 @@
|
||||
--[[
|
||||
|
||||
TechAge
|
||||
=======
|
||||
|
||||
Copyright (C) 2020 Joachim Stolberg
|
||||
|
||||
AGPL v3
|
||||
See LICENSE.txt for more information
|
||||
|
||||
Storage backend for node related data via sqlite database
|
||||
|
||||
]]--
|
||||
|
||||
-- for lazy programmers
|
||||
local M = minetest.get_meta
|
||||
|
||||
-------------------------------------------------------------------
|
||||
-- Database
|
||||
-------------------------------------------------------------------
|
||||
local MN = minetest.get_current_modname()
|
||||
local WP = minetest.get_worldpath()
|
||||
local use_marshal = minetest.settings:get_bool('techage_use_marshal', false)
|
||||
local MAR_MAGIC = 0x8e
|
||||
|
||||
if not techage.IE then
|
||||
error("Please add 'secure.trusted_mods = techage' to minetest.conf!")
|
||||
end
|
||||
|
||||
local sqlite3 = techage.IE.require("lsqlite3")
|
||||
local marshal = techage.IE.require("marshal")
|
||||
|
||||
if not sqlite3 then
|
||||
error("Please install sqlite3 via 'luarocks install lsqlite3'")
|
||||
end
|
||||
if not marshal then
|
||||
error("Please install marshal via 'luarocks install lua-marshal'")
|
||||
end
|
||||
|
||||
local db = sqlite3.open(WP.."/techage_nodedata.sqlite")
|
||||
local ROW = sqlite3.ROW
|
||||
|
||||
-- Prevent use of this db instance.
|
||||
if sqlite3 then sqlite3 = nil end
|
||||
|
||||
db:exec[[
|
||||
CREATE TABLE mapblocks(id INTEGER PRIMARY KEY, key INTEGER, data BLOB);
|
||||
CREATE UNIQUE INDEX idx ON mapblocks(key);
|
||||
]]
|
||||
|
||||
local set = db:prepare("INSERT or REPLACE INTO mapblocks VALUES(NULL, ?, ?);")
|
||||
local get = db:prepare("SELECT * FROM mapblocks WHERE key=?;")
|
||||
|
||||
local function set_block(key, data)
|
||||
set:reset()
|
||||
set:bind(1, key)
|
||||
set:bind_blob(2, data)
|
||||
set:step()
|
||||
end
|
||||
|
||||
local function get_block(key)
|
||||
get:reset()
|
||||
get:bind(1, key)
|
||||
if get:step() == ROW then
|
||||
return get:get_value(2)
|
||||
end
|
||||
end
|
||||
|
||||
-------------------------------------------------------------------
|
||||
-- API functions
|
||||
-------------------------------------------------------------------
|
||||
local api = {}
|
||||
|
||||
function api.store_mapblock_data(key, mapblock_data)
|
||||
if use_marshal and mapblock_data then
|
||||
local data = marshal.encode(mapblock_data)
|
||||
if data then
|
||||
set_block(key, data)
|
||||
end
|
||||
else
|
||||
set_block(key, minetest.serialize(mapblock_data))
|
||||
end
|
||||
end
|
||||
|
||||
function api.get_mapblock_data(key)
|
||||
local s = get_block(key)
|
||||
if s then
|
||||
if s:byte(1) == MAR_MAGIC then
|
||||
return marshal.decode(s)
|
||||
else
|
||||
return minetest.deserialize(s)
|
||||
end
|
||||
end
|
||||
api.store_mapblock_data(key, {})
|
||||
return {}
|
||||
end
|
||||
|
||||
function api.get_node_data(pos)
|
||||
-- legacy data available?
|
||||
local s = M(pos):get_string("ta_data")
|
||||
if s ~= "" then
|
||||
M(pos):set_string("ta_data", "")
|
||||
if s:byte(1) == MAR_MAGIC then
|
||||
return marshal.decode(s)
|
||||
else
|
||||
return minetest.deserialize(s)
|
||||
end
|
||||
end
|
||||
return {}
|
||||
end
|
||||
|
||||
function api.freeze_at_shutdown(data)
|
||||
for key, item in pairs(data) do
|
||||
api.store_mapblock_data(key, item)
|
||||
end
|
||||
end
|
||||
|
||||
function api.restore_at_startup()
|
||||
-- nothing to restore
|
||||
return {}
|
||||
end
|
||||
|
||||
return api
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user