built on 04/02/2023 15:45:23

This commit is contained in:
Joachim Stolberg 2023-02-04 15:45:23 +01:00
parent e4f4a04f0b
commit 1088fb8bfb
62 changed files with 800 additions and 458 deletions

View File

@ -212,8 +212,12 @@ minetest.register_node("hyperloop:shaft2", {
light_source = 2, light_source = 2,
sunlight_propagates = true, sunlight_propagates = true,
is_ground_content = false, is_ground_content = false,
----- To be unbreakable -----
on_blast = function() end,
on_destruct = function () end,
can_dig = function() return false end,
diggable = false, diggable = false,
groups = {cracky = 1, not_in_creative_inventory=1}, groups = {cracky = 1, not_in_creative_inventory=1, unbreakable=1},
sounds = default.node_sound_metal_defaults(), sounds = default.node_sound_metal_defaults(),
}) })
@ -255,8 +259,12 @@ minetest.register_node("hyperloop:shaftA2", {
light_source = 2, light_source = 2,
sunlight_propagates = true, sunlight_propagates = true,
is_ground_content = false, is_ground_content = false,
----- To be unbreakable -----
on_blast = function() end,
on_destruct = function () end,
can_dig = function() return false end,
diggable = false, diggable = false,
groups = {cracky = 1, not_in_creative_inventory=1}, groups = {cracky = 1, not_in_creative_inventory=1, unbreakable=1},
sounds = default.node_sound_metal_defaults(), sounds = default.node_sound_metal_defaults(),
}) })

View File

@ -173,8 +173,12 @@ minetest.register_node("hyperloop:tubeS2", {
light_source = 2, light_source = 2,
sunlight_propagates = true, sunlight_propagates = true,
is_ground_content = false, is_ground_content = false,
----- To be unbreakable -----
on_blast = function() end,
on_destruct = function () end,
can_dig = function() return false end,
diggable = false, diggable = false,
groups = {cracky = 1, not_in_creative_inventory=1}, groups = {cracky = 1, not_in_creative_inventory=1, unbreakable=1},
sounds = default.node_sound_metal_defaults(), sounds = default.node_sound_metal_defaults(),
}) })
@ -263,8 +267,12 @@ minetest.register_node("hyperloop:tubeA2", {
light_source = 2, light_source = 2,
sunlight_propagates = true, sunlight_propagates = true,
is_ground_content = false, is_ground_content = false,
----- To be unbreakable -----
on_blast = function() end,
on_destruct = function () end,
can_dig = function() return false end,
diggable = false, diggable = false,
groups = {cracky = 1, not_in_creative_inventory=1}, groups = {cracky = 1, not_in_creative_inventory=1, unbreakable=1},
sounds = default.node_sound_metal_defaults(), sounds = default.node_sound_metal_defaults(),
}) })

View File

@ -44,6 +44,7 @@ The mod features are:
- Ingame documentation (German and English), based on the mod "doc" - Ingame documentation (German and English), based on the mod "doc"
- API to register carts from other mods - API to register carts from other mods
- chat command '/mycart <num>' to output cart state and location - chat command '/mycart <num>' to output cart state and location
- Command interface for Techage (Lua and ICTA) and for Beduino Controllers
Technical Background Technical Background
@ -118,6 +119,45 @@ The "No speed limit" sign can be used to remove the speed limit.
The speed limit signs must be placed next to the track so that they can The speed limit signs must be placed next to the track so that they can
be read from the cart. This allows different speeds in each direction of travel. be read from the cart. This allows different speeds in each direction of travel.
## Command Interface
### Techage ICTA Controller
The ICTA Controller support the conditions:
- "read cart state" (function returns "stopped" or "running")
- "read cart location" (function returns the distance or the station/buffer name)
See help page of the ICTA controller block.
### Techage Lua Controller
The Lua controller support the functions:
- `$cart_state(num)` (function returns "stopped" or "running")
- `$cart_location(num)` (function returns the distance or the station/buffer name)
See help page of the Lua controller block.
### Cart Terminal
The Cart Terminal has a Techage command interface with the commands:
| Command | Data | Description |
| ---------- | ---------- | ------------------------------------------------------- |
| `state` | \<cart-ID> | Returns `unknown`, `stopped`, or `running` |
| `distance` | \<cart-ID> | Returns the distance from the cart to the Cart Terminal |
### Beduino Controller
The Cart Terminal has a Beduino command interface with the commands:
| Command | Topic | Data | Response | Description |
| -------- | ----- | --------- | ---------- | ------------------------------------------------------- |
| State | 129 | [cart-id] | [state] | Returns 0 = UNKNOWN, 1 = STOPPED, 2 = RUNNING |
| Distance | 130 | [cart-id] | [distance] | Returns the distance from the cart to the Cart Terminal |
Migration to v2 Migration to v2
--------------- ---------------
@ -153,3 +193,4 @@ History
Speed limit signs and cart terminal added Speed limit signs and cart terminal added
2021-09-02 V2.01 Chat command /stopcart added 2021-09-02 V2.01 Chat command /stopcart added
2021-10-18 V2.02 Cart reproduction bug fixed 2021-10-18 V2.02 Cart reproduction bug fixed
2023-01-04 V2.03 Techage and Beduino command interface added

93
minecart/beduino.lua Normal file
View File

@ -0,0 +1,93 @@
--[[
Minecart
========
Copyright (C) 2019-2023 Joachim Stolberg
MIT
See license.txt for more information
]]--
local minecart_lib = [[
// Minecart library for reading status and distance
// from running carts. To do this, a cart terminal
// must be connected to an I/O Module.
import "lib/techage.c"
var payload[1];
var resp[1];
// Read cart state.
// Parameters:
// - ip_port: IOM port to the Cart Terminal
// - card_id: Cart number
// Function returns:
// - 0 for unknown/missing
// - 1 for stopped
// - 2 for running
func mc_get_state(io_port, cart_id) {
var sts;
payload[0] = cart_id;
request_data(io_port, 129, payload, resp);
if(sts == 0) {
return resp[0];
}
return 0;
}
// Read cart distance.
// Parameters:
// - ip_port: IOM port to the Cart Terminal
// - card_id: Cart number
// Function returns the distance between
// Cart Terminal and cart in meter.
func mc_get_distance(io_port, cart_id) {
var sts;
payload[0] = cart_id;
request_data(io_port, 130, payload, resp);
if(sts == 0) {
return resp[0];
}
return 0;
}
]]
local minecart_demo = [[
import "sys/stdio.asm"
import "sys/os.c"
import "lib/minecart.c"
func init() {
setstdout(1); // use terminal window for stdout
putstr("### Minecart Demo ###\n");
}
func cart_state(io_port, cart_id) {
putstr("Cart #");
putnum(cart_id);
putstr(": state = ");
putnum(mc_get_state(io_port, cart_id));
putstr(", distance = ");
putnum(mc_get_distance(io_port, cart_id));
putstr("\n");
}
func loop() {
// Adapt IO port and cart ID to your needs
cart_state(0, 1);
sleep(20);
}
]]
minetest.register_on_mods_loaded(function()
if minetest.global_exists("vm16") and minetest.global_exists("beduino") then
vm16.register_ro_file("beduino", "lib/minecart.c", minecart_lib)
vm16.register_ro_file("beduino", "demo/minecart.c", minecart_demo)
end
end)

View File

@ -94,8 +94,8 @@ function minecart.untake_items(pos, param2, stack)
local def = RegisteredInventories[node.name] local def = RegisteredInventories[node.name]
local inv = minetest.get_inventory({type="node", pos=npos}) local inv = minetest.get_inventory({type="node", pos=npos})
if def and inv and def.put_listname then if def and inv and def.take_listname then
return inv:add_item(def.put_listname, stack) return inv:add_item(def.take_listname, stack)
elseif def and def.untake_item then elseif def and def.untake_item then
return def.untake_item(npos, stack) return def.untake_item(npos, stack)
else else

View File

@ -3,7 +3,7 @@
Minecart Minecart
======== ========
Copyright (C) 2019-2021 Joachim Stolberg Copyright (C) 2019-2023 Joachim Stolberg
MIT MIT
See license.txt for more information See license.txt for more information
@ -13,7 +13,7 @@
minecart = {} minecart = {}
-- Version for compatibility checks, see readme.md/history -- Version for compatibility checks, see readme.md/history
minecart.version = 2.02 minecart.version = 2.03
minecart.hopper_enabled = minetest.settings:get_bool("minecart_hopper_enabled") ~= false minecart.hopper_enabled = minetest.settings:get_bool("minecart_hopper_enabled") ~= false
minecart.teleport_enabled = minetest.settings:get_bool("minecart_teleport_enabled") == true minecart.teleport_enabled = minetest.settings:get_bool("minecart_teleport_enabled") == true
@ -39,6 +39,7 @@ dofile(MP .. "/protection.lua")
dofile(MP .. "/signs.lua") dofile(MP .. "/signs.lua")
dofile(MP .. "/terminal.lua") dofile(MP .. "/terminal.lua")
dofile(MP .. "/pusher.lua") dofile(MP .. "/pusher.lua")
dofile(MP .. "/beduino.lua")
if minecart.hopper_enabled then if minecart.hopper_enabled then
dofile(MP .. "/hopper.lua") dofile(MP .. "/hopper.lua")

View File

@ -3,7 +3,7 @@
Minecart Minecart
======== ========
Copyright (C) 2019-2021 Joachim Stolberg Copyright (C) 2019-2023 Joachim Stolberg
MIT MIT
See license.txt for more information See license.txt for more information
@ -92,6 +92,21 @@ local function get_cart_state_and_loc(name, userID, query_pos)
return "unknown", 0, "unknown" return "unknown", 0, "unknown"
end end
-- Return the cart distance to the query_pos.
local function get_cart_distance(name, userID, query_pos)
if tCartsOnRail[name] and tCartsOnRail[name][userID] then
local cart = tCartsOnRail[name][userID]
if cart.last_pos or cart.pos then
if cart.objID == 0 then -- stopped
return math.floor(vector.distance(cart.pos or cart.last_pos, query_pos))
else
return math.floor(vector.distance(cart.last_pos or cart.pos, query_pos))
end
end
end
return 0
end
local function get_cart_info(owner, userID, query_pos) local function get_cart_info(owner, userID, query_pos)
local state, loc, name = get_cart_state_and_loc(owner, userID, query_pos) local state, loc, name = get_cart_state_and_loc(owner, userID, query_pos)
local cart_type = minecart.tCartTypes[name] or "unknown" local cart_type = minecart.tCartTypes[name] or "unknown"
@ -318,6 +333,10 @@ function minecart.cmnd_cart_location(name, userID, query_pos)
return loc return loc
end end
function minecart.cmnd_cart_distance(name, userID, query_pos)
return get_cart_distance(name, userID, query_pos)
end
function minecart.get_cart_list(pos, name) function minecart.get_cart_list(pos, name)
local userIDs = {} local userIDs = {}
local carts = {} local carts = {}

View File

@ -3,7 +3,7 @@
Minecart Minecart
======== ========
Copyright (C) 2019-2021 Joachim Stolberg Copyright (C) 2019-2023 Joachim Stolberg
MIT MIT
See license.txt for more information See license.txt for more information
@ -59,6 +59,11 @@ minetest.register_node("minecart:terminal", {
local meta = M(pos) local meta = M(pos)
meta:set_string("owner", placer:get_player_name()) meta:set_string("owner", placer:get_player_name())
meta:set_string("formspec", formspec(pos, "")) meta:set_string("formspec", formspec(pos, ""))
if minetest.global_exists("techage") then
local number = techage.add_node(pos, "minecart:terminal")
meta:set_string("node_number", number)
meta:set_string("infotext", "Cart Terminal " .. number)
end
minetest.get_node_timer(pos):start(2) minetest.get_node_timer(pos):start(2)
end, end,
@ -88,3 +93,42 @@ minetest.register_craft({
{"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"}, {"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"},
}, },
}) })
minetest.register_on_mods_loaded(function()
if minetest.global_exists("techage") then
techage.register_node({"minecart:terminal"}, {
on_recv_message = function(pos, src, topic, payload)
local number = tonumber(payload)
if number then
local owner = M(pos):get_string("owner")
if topic == "state" then
return minecart.cmnd_cart_state(owner, number)
elseif topic == "distance" then
return minecart.cmnd_cart_distance(owner, number, pos)
else
return "unsupported"
end
end
end,
on_beduino_receive_cmnd = function(pos, src, topic, payload)
return 2 -- unknown or invalid topic
end,
on_beduino_request_data = function(pos, src, topic, payload)
if topic == 128 then
return 0, "minecart:terminal"
elseif topic == 129 then -- state
local owner = M(pos):get_string("owner")
local STATE = {unknown = 0, stopped = 1, running = 2}
local state = STATE[minecart.cmnd_cart_state(owner, payload[1])] or 0
return 0, {state}
elseif topic == 130 then -- distance
local owner = M(pos):get_string("owner")
local dist = minecart.cmnd_cart_distance(owner, payload[1], pos)
return 0, {dist}
else
return 2, "" -- topic is unknown or invalid
end
end,
})
end
end)

View File

@ -268,9 +268,11 @@ signs_bot.register_botcommand("fall_down", {
local sts, pos3 = minetest.line_of_sight(pos1, pos2) local sts, pos3 = minetest.line_of_sight(pos1, pos2)
if sts == false then if sts == false then
sts, _ = minetest.spawn_falling_node(mem.robot_pos) sts, _ = minetest.spawn_falling_node(mem.robot_pos)
mem.stored_node = get_node_lvm(pos3)
minetest.swap_node(pos3, {name="air"})
if sts then if sts then
mem.bot_falling = 2 mem.bot_falling = 2
mem.robot_pos = {x=pos3.x, y=pos3.y+1, z=pos3.z} mem.robot_pos = {x=pos3.x, y=pos3.y, z=pos3.z}
return signs_bot.BUSY return signs_bot.BUSY
end end
end end

View File

@ -63,10 +63,12 @@ if farming.mod == "redo" then
-- everything except cocoa (these can only be placed on jungletree) -- everything except cocoa (these can only be placed on jungletree)
if name ~= "farming:cocoa_beans" then if name ~= "farming:cocoa_beans" then
local step = def.steps local step = def.steps
if step then
while fp_grows(def, step) do step = step + 1 end while fp_grows(def, step) do step = step + 1 end
end end
end end
end end
end
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
-- Ethereal Farming -- Ethereal Farming

View File

@ -89,6 +89,19 @@ Available worlds will be converted to 'lsqlite3', but there is no way back, so:
### History ### History
**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** **2022-09-03 V1.09**
- Change the way items are pushed - Change the way items are pushed
- Add "Flow Limiter" mode to TA4 pump and TA4 pusher - Add "Flow Limiter" mode to TA4 pump and TA4 pusher

View File

@ -266,6 +266,11 @@ if techage.max_num_forceload_blocks > 0 then
output = "techage:forceloadtile", output = "techage:forceloadtile",
recipe = {"techage:forceload"}, recipe = {"techage:forceload"},
}) })
minetest.register_craft({
type = "shapeless",
output = "techage:forceload",
recipe = {"techage:forceloadtile"},
})
end end
minetest.register_on_joinplayer(function(player) minetest.register_on_joinplayer(function(player)

View File

@ -17,7 +17,7 @@ local M = minetest.get_meta
local S = techage.S local S = techage.S
-- Consumer Related Data -- Consumer Related Data
local CRD = function(pos) return (minetest.registered_nodes[techage.get_node_lvm(pos).name] or {}).consumer end local CRD = function(pos) return (minetest.registered_nodes[techage.get_node_lvm(pos).name] or {}).consumer or {} end
local STANDBY_TICKS = 3 local STANDBY_TICKS = 3
local COUNTDOWN_TICKS = 4 local COUNTDOWN_TICKS = 4

View File

@ -341,7 +341,7 @@ local tubing = {
CRD(pos).State:stop(pos, nvm) CRD(pos).State:stop(pos, nvm)
config_item(pos, payload) config_item(pos, payload)
return 0 return 0
elseif topic == 68 then -- Set push limit elseif topic == 68 or topic == 20 then -- Set push limit
local nvm = techage.get_nvm(pos) local nvm = techage.get_nvm(pos)
CRD(pos).State:stop(pos, nvm) CRD(pos).State:stop(pos, nvm)
set_limit(pos, nvm, payload[1]) set_limit(pos, nvm, payload[1])

View File

@ -196,6 +196,9 @@ local function quarry_task(pos, crd, nvm)
pos1.y = y_curr pos1.y = y_curr
pos2.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 nvm.level = y_first - y_curr
if minetest.is_area_protected(pos1, pos2, owner, 5) then if minetest.is_area_protected(pos1, pos2, owner, 5) then
@ -382,6 +385,11 @@ local tubing = {
end, end,
on_node_load = function(pos) on_node_load = function(pos)
CRD(pos).State:on_node_load(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, end,
} }

View File

@ -346,6 +346,20 @@ local function count_number_of_chests(pos)
M(pos):set_int("stacksize", STACK_SIZE * cnt) M(pos):set_int("stacksize", STACK_SIZE * cnt)
end 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 function search_chest_in_front(pos, node)
local dir = techage.side_to_outdir("F", node.param2) local dir = techage.side_to_outdir("F", node.param2)
local pos1 = tubelib2.get_pos(pos, dir) local pos1 = tubelib2.get_pos(pos, dir)
@ -529,6 +543,10 @@ minetest.register_node("techage:ta4_chest", {
after_place_node = function(pos, placer) after_place_node = function(pos, placer)
local node = minetest.get_node(pos) 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 if search_chest_in_front(pos, node) then
node.name = "techage:ta4_chest_dummy" node.name = "techage:ta4_chest_dummy"
minetest.swap_node(pos, node) minetest.swap_node(pos, node)
@ -667,6 +685,18 @@ techage.register_node({"techage:ta4_chest_dummy"}, {
end 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({ minetest.register_craft({
type = "shapeless", type = "shapeless",
output = "techage:ta4_chest", output = "techage:ta4_chest",

View File

@ -3,7 +3,7 @@
TechAge TechAge
======= =======
Copyright (C) 2020-2022 Joachim Stolberg Copyright (C) 2020-2023 Joachim Stolberg
AGPL v3 AGPL v3
See LICENSE.txt for more information See LICENSE.txt for more information
@ -30,26 +30,6 @@ local function lvect_add_vec(lvect1, offs)
return lvect2 return lvect2
end end
local function lvect_add(lvect1, lvect2)
if not lvect1 or not lvect2 then return end
local lvect3 = {}
for i, v in ipairs(lvect1) do
lvect3[#lvect3 + 1] = vector.add(v, lvect2[i])
end
return lvect3
end
local function lvect_subtract(lvect1, lvect2)
if not lvect1 or not lvect2 then return end
local lvect3 = {}
for i, v in ipairs(lvect1) do
lvect3[#lvect3 + 1] = vector.subtract(v, lvect2[i])
end
return lvect3
end
-- yaw in radiant -- yaw in radiant
local function rotate(v, yaw) local function rotate(v, yaw)
local sinyaw = math.sin(yaw) local sinyaw = math.sin(yaw)
@ -61,7 +41,6 @@ local function set_node(item)
local dest_pos = item.dest_pos local dest_pos = item.dest_pos
local name = item.name or "air" local name = item.name or "air"
local param2 = item.param2 or 0 local param2 = item.param2 or 0
local metadata = item.metadata or {}
local nvm = techage.get_nvm(item.base_pos) local nvm = techage.get_nvm(item.base_pos)
local node = techage.get_node_lvm(dest_pos) local node = techage.get_node_lvm(dest_pos)
local ndef1 = minetest.registered_nodes[name] local ndef1 = minetest.registered_nodes[name]
@ -100,7 +79,7 @@ local function push(item)
queue[last] = item queue[last] = item
end end
local function pop(nvm, time) local function pop()
if first > last then return end if first > last then return end
local item = queue[first] local item = queue[first]
queue[first] = nil -- to allow garbage collection queue[first] = nil -- to allow garbage collection
@ -408,6 +387,9 @@ local function entity_to_node(pos, obj)
end end
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 function node_to_entity(base_pos, start_pos, dest_pos)
local meta = M(start_pos) local meta = M(start_pos)
local node, metadata local node, metadata
@ -464,9 +446,9 @@ local function determine_dir(pos1, pos2)
return {x=0, y=0, z=0} return {x=0, y=0, z=0}
end end
local function move_entity(obj, dest_pos, dir, is_corner) local function move_entity(obj, next_pos, dir, is_corner)
local self = obj:get_luaentity() local self = obj:get_luaentity()
self.dest_pos = dest_pos self.next_pos = next_pos
self.dir = dir self.dir = dir
if is_corner then if is_corner then
local vel = vector.multiply(dir, math.min(CORNER_SPEED, self.max_speed)) local vel = vector.multiply(dir, math.min(CORNER_SPEED, self.max_speed))
@ -477,7 +459,7 @@ local function move_entity(obj, dest_pos, dir, is_corner)
end end
local function moveon_entity(obj, self, pos1) local function moveon_entity(obj, self, pos1)
local pos2 = next_path_pos(pos1, self.lpath, self.path_idx) local pos2 = next_path_pos(pos1, self.lmove, self.path_idx)
if pos2 then if pos2 then
self.path_idx = self.path_idx + 1 self.path_idx = self.path_idx + 1
local dir = determine_dir(pos1, pos2) local dir = determine_dir(pos1, pos2)
@ -486,43 +468,6 @@ local function moveon_entity(obj, self, pos1)
end end
end end
-- Handover the entity to the next movecontroller
local function handover_to(obj, self, pos1)
if self.handover then
local info = techage.get_node_info(self.handover)
if info and info.name == "techage:ta4_movecontroller" then
local meta = M(info.pos)
if self.move2to1 then
self.handover = meta:contains("handoverA") and meta:get_string("handoverA") or nil
else
self.handover = meta:contains("handoverB") and meta:get_string("handoverB") or nil
end
self.lpath = flylib.to_path(meta:get_string("path"))
if pos1 and self.lpath then
self.path_idx = 2
if self.move2to1 then
self.lpath[1] = vector.multiply(self.lpath[1], - 1)
end
local pos2 = next_path_pos(pos1, self.lpath, 1)
local dir = determine_dir(pos1, pos2)
if not self.handover then
local nvm = techage.get_nvm(info.pos)
nvm.lpos1 = nvm.lpos1 or {}
if self.move2to1 then
nvm.lpos1[self.pos1_idx] = pos2
else
nvm.lpos1[self.pos1_idx] = pos1
end
end
move_entity(obj, pos2, dir)
return true
end
end
end
end
minetest.register_entity("techage:move_item", { minetest.register_entity("techage:move_item", {
initial_properties = { initial_properties = {
pointable = true, pointable = true,
@ -538,63 +483,48 @@ minetest.register_entity("techage:move_item", {
on_step = function(self, dtime, moveresult) on_step = function(self, dtime, moveresult)
local stop_obj = function(obj, self) local stop_obj = function(obj, self)
local dest_pos = self.dest_pos local next_pos = self.next_pos
obj:move_to(self.dest_pos, true) obj:move_to(self.next_pos, true)
obj:set_acceleration({x=0, y=0, z=0}) obj:set_acceleration({x=0, y=0, z=0})
obj:set_velocity({x=0, y=0, z=0}) obj:set_velocity({x=0, y=0, z=0})
self.dest_pos = nil self.next_pos = nil
self.old_dist = nil self.old_dist = nil
return dest_pos return next_pos
end end
if self.dest_pos then if self.next_pos then
local obj = self.object local obj = self.object
local pos = obj:get_pos() local pos = obj:get_pos()
local dist = vector.distance(pos, self.dest_pos) local dist = vector.distance(pos, self.next_pos)
local speed = calc_speed(obj:get_velocity()) local speed = calc_speed(obj:get_velocity())
self.old_dist = self.old_dist or dist self.old_dist = self.old_dist or dist
-- Landing if self.lmove and self.lmove[self.path_idx] then
if self.lpath and self.lpath[self.path_idx] then
if dist < 1 or dist > self.old_dist then if dist < 1 or dist > self.old_dist then
local dest_pos = stop_obj(obj, self) -- change of direction
if not moveon_entity(obj, self, dest_pos) then local next_pos = stop_obj(obj, self)
minetest.after(0.5, entity_to_node, dest_pos, obj) if not moveon_entity(obj, self, next_pos) then
minetest.after(0.5, entity_to_node, next_pos, obj)
end end
return return
end end
elseif self.handover and dist < 0.2 or dist > self.old_dist then elseif dist < 0.05 or dist > self.old_dist then
local dest_pos = stop_obj(obj, self) -- Landing
if not handover_to(obj, self, dest_pos) then local next_pos = stop_obj(obj, self)
minetest.after(0.5, entity_to_node, dest_pos, obj) local dest_pos = self.item.dest_pos or next_pos
end
return
else
if dist < 0.05 or dist > self.old_dist then
local dest_pos = stop_obj(obj, self)
minetest.after(0.5, entity_to_node, dest_pos, obj) minetest.after(0.5, entity_to_node, dest_pos, obj)
return return
end end
end
self.old_dist = dist self.old_dist = dist
-- Braking or limit max speed -- Braking or limit max speed
if self.handover then
if speed > (dist * 4) or speed > self.max_speed then
speed = math.min(speed, math.max(dist * 4, MIN_SPEED))
local vel = vector.multiply(self.dir,speed)
obj:set_velocity(vel)
obj:set_acceleration({x=0, y=0, z=0})
end
else
if speed > (dist * 2) or speed > self.max_speed then if speed > (dist * 2) or speed > self.max_speed then
speed = math.min(speed, math.max(dist * 2, MIN_SPEED)) speed = math.min(speed, math.max(dist * 2, MIN_SPEED))
local vel = vector.multiply(self.dir,speed) local vel = vector.multiply(self.dir,speed)
obj:set_velocity(vel) obj:set_velocity(vel)
obj:set_acceleration({x=0, y=0, z=0}) obj:set_acceleration({x=0, y=0, z=0})
end end
end
monitoring_trigger_entity(self.item) monitoring_trigger_entity(self.item)
end end
@ -618,30 +548,34 @@ local function is_simple_node(pos)
return not techage.is_air_like(node.name) and techage.can_dig_node(node.name, ndef) return not techage.is_air_like(node.name) and techage.can_dig_node(node.name, ndef)
end end
local function move_node(pos, pos1_idx, start_pos, lpath, max_speed, height, move2to1, handover, cpos) -- Move node from 'pos1' to the destination, calculated by means of 'lmove'
local pos2 = next_path_pos(start_pos, lpath, 1) -- * 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 -- optional for non-player objects
local yoffs = M(pos):get_float("offset") local yoffs = meta:get_float("offset")
if pos2 then if pos2 then
local dir = determine_dir(start_pos, pos2) local dir = determine_dir(pos1, pos2)
local obj = node_to_entity(pos, start_pos, pos2) local obj = node_to_entity(pos, pos1, dest_pos)
if obj then if obj then
local offs = {x=0, y=height or 1, z=0} local offs = {x=0, y=height or 1, z=0}
attach_objects(start_pos, offs, obj, yoffs) attach_objects(pos1, offs, obj, yoffs)
if dir.y == 0 then if dir.y == 0 then
if (dir.x ~= 0 and dir.z == 0) or (dir.x == 0 and dir.z ~= 0) then if (dir.x ~= 0 and dir.z == 0) or (dir.x == 0 and dir.z ~= 0) then
attach_objects(start_pos, dir, obj, yoffs) attach_objects(pos1, dir, obj, yoffs)
end end
end end
local self = obj:get_luaentity() local self = obj:get_luaentity()
self.path_idx = 2 self.path_idx = 2
self.pos1_idx = pos1_idx self.lmove = lmove
self.lpath = lpath
self.max_speed = max_speed self.max_speed = max_speed
self.move2to1 = move2to1
self.handover = handover
self.yoffs = yoffs self.yoffs = yoffs
move_entity(obj, pos2, dir) move_entity(obj, pos2, dir)
return true return true
@ -651,14 +585,20 @@ local function move_node(pos, pos1_idx, start_pos, lpath, max_speed, height, mov
end end
end end
local function move_nodes(pos, meta, nvm, lpath, max_speed, height, move2to1, handover) -- 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") local owner = meta:get_string("owner")
techage.counting_add(owner, #lpath, #nvm.lpos1 * #lpath) techage.counting_add(owner, #lmove, #nvm.lpos1 * #lmove)
for idx = 1, #nvm.lpos1 do for idx = 1, #nvm.lpos1 do
local pos1 = nvm.lpos1[idx] local pos1 = nvm.lpos1[idx]
local pos2 = nvm.lpos2[idx] local pos2 = nvm.lpos2[idx]
--print("move_nodes", idx, P2S(pos1), P2S(pos2)) --print("multi_move_nodes", idx, P2S(pos1), P2S(pos2))
if move2to1 then if move2to1 then
pos1, pos2 = pos2, pos1 pos1, pos2 = pos2, pos1
@ -666,7 +606,7 @@ local function move_nodes(pos, meta, nvm, lpath, max_speed, height, move2to1, ha
if not minetest.is_protected(pos1, owner) and not minetest.is_protected(pos2, owner) then 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 is_simple_node(pos1) and is_valid_dest(pos2) then
if move_node(pos, idx, pos1, lpath, max_speed, height, move2to1, handover) == false 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")) meta:set_string("status", S("No valid node at the start position"))
return false return false
end end
@ -691,21 +631,27 @@ local function move_nodes(pos, meta, nvm, lpath, max_speed, height, move2to1, ha
return true return true
end end
-- Move nodes from lpos1 by the given x/y/z 'line' -- Move the nodes from lpos1 to lpos2.
local function move_nodes2(pos, meta, lpos1, line, max_speed, height) -- * 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") local owner = meta:get_string("owner")
lpos1 = lpos1 or {}
techage.counting_add(owner, #lpos1) techage.counting_add(owner, #lpos1)
local lpos2 = {} local lpos2 = {}
for idx = 1, #lpos1 do for idx = 1, #lpos1 do
local pos1 = lpos1[idx] local pos1 = lpos1[idx]
local pos2 = vector.add(lpos1[idx], line) local pos2 = vector.add(lpos1[idx], move)
lpos2[idx] = pos2 lpos2[idx] = pos2
if not minetest.is_protected(pos1, owner) and not minetest.is_protected(pos2, owner) then 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 is_simple_node(pos1) and is_valid_dest(pos2) then
move_node(pos, idx, pos1, {line}, max_speed, height, false, false) move_node(pos, meta, pos1, {move}, max_speed, height)
else else
if not is_simple_node(pos1) then if not is_simple_node(pos1) then
meta:set_string("status", S("No valid node at the start position")) meta:set_string("status", S("No valid node at the start position"))
@ -728,13 +674,14 @@ local function move_nodes2(pos, meta, lpos1, line, max_speed, height)
return true, lpos2 return true, lpos2
end end
-- 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) function flylib.move_to_other_pos(pos, move2to1)
local meta = M(pos) local meta = M(pos)
local nvm = techage.get_nvm(pos) local nvm = techage.get_nvm(pos)
local lpath, err = flylib.to_path(meta:get_string("path")) or {} 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 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 height = meta:contains("height") and meta:get_float("height") or 1
local handover
if err or nvm.running then return false end if err or nvm.running then return false end
@ -742,33 +689,28 @@ function flylib.move_to_other_pos(pos, move2to1)
max_speed = techage.in_range(max_speed, MIN_SPEED, MAX_SPEED) max_speed = techage.in_range(max_speed, MIN_SPEED, MAX_SPEED)
nvm.lpos1 = nvm.lpos1 or {} nvm.lpos1 = nvm.lpos1 or {}
local offs = dest_offset(lpath) local offs = dest_offset(lmove)
if move2to1 then if move2to1 then
lpath = reverse_path(lpath) lmove = reverse_path(lmove)
end end
-- calc destination positions -- calc destination positions
nvm.lpos2 = lvect_add_vec(nvm.lpos1, offs) nvm.lpos2 = lvect_add_vec(nvm.lpos1, offs)
if move2to1 then nvm.running = multi_move_nodes(pos, meta, nvm, lmove, max_speed, height, move2to1)
handover = meta:contains("handoverA") and meta:get_string("handoverA") or nil
else
handover = meta:contains("handoverB") and meta:get_string("handoverB") or nil
end
nvm.running = move_nodes(pos, meta, nvm, lpath, max_speed, height, move2to1, handover)
nvm.moveBA = nvm.running and not move2to1 nvm.moveBA = nvm.running and not move2to1
return nvm.running return nvm.running
end end
function flylib.move_to(pos, line) -- `move` the movement as a vector
function flylib.move_to(pos, move)
local meta = M(pos) local meta = M(pos)
local nvm = techage.get_nvm(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 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 max_speed = meta:contains("max_speed") and meta:get_int("max_speed") or MAX_SPEED
local resp
if nvm.running then return false end if nvm.running then return false end
nvm.running, nvm.lastpos = move_nodes2(pos, meta, nvm.lastpos or nvm.lpos1, line, max_speed, height) nvm.running, nvm.lastpos = move_nodes(pos, meta, nvm.lastpos or nvm.lpos1, move, max_speed, height)
return nvm.running return nvm.running
end end
@ -782,27 +724,28 @@ function flylib.reset_move(pos)
if nvm.lpos1 and nvm.lpos1[1] then if nvm.lpos1 and nvm.lpos1[1] then
local move = vector.subtract(nvm.lpos1[1], (nvm.lastpos or nvm.lpos1)[1]) local move = vector.subtract(nvm.lpos1[1], (nvm.lastpos or nvm.lpos1)[1])
local resp
nvm.running, nvm.lastpos = move_nodes2(pos, meta, nvm.lastpos or nvm.lpos1, move, max_speed, height) nvm.running, nvm.lastpos = move_nodes(pos, meta, nvm.lastpos or nvm.lpos1, move, max_speed, height)
return nvm.running return nvm.running
end end
return false return false
end 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" -- rot is one of "l", "r", "2l", "2r"
-- cpos is the center pos (optional) function flylib.rotate_nodes(pos, lpos, rot)
function flylib.rotate_nodes(pos, posses1, rot)
local meta = M(pos) local meta = M(pos)
local owner = meta:get_string("owner") 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 cpos = meta:contains("center") and flylib.to_vector(meta:get_string("center"))
local posses2 = techage.rotate_around_center(posses1, rot, cpos) local lpos2 = techage.rotate_around_center(lpos, rot, cpos)
local param2 local param2
local nodes2 = {} local nodes2 = {}
techage.counting_add(owner, #posses1 * 2) techage.counting_add(owner, #lpos * 2)
for i, pos1 in ipairs(posses1) do for i, pos1 in ipairs(lpos) do
local node = techage.get_node_lvm(pos1) local node = techage.get_node_lvm(pos1)
if rot == "l" then if rot == "l" then
param2 = techage.param2_turn_right(node.param2) param2 = techage.param2_turn_right(node.param2)
@ -813,7 +756,7 @@ function flylib.rotate_nodes(pos, posses1, rot)
end end
if not minetest.is_protected(pos1, owner) and is_simple_node(pos1) then if not minetest.is_protected(pos1, owner) and is_simple_node(pos1) then
minetest.remove_node(pos1) minetest.remove_node(pos1)
nodes2[#nodes2 + 1] = {pos = posses2[i], name = node.name, param2 = param2} nodes2[#nodes2 + 1] = {pos = lpos2[i], name = node.name, param2 = param2}
end end
end end
for _,item in ipairs(nodes2) do for _,item in ipairs(nodes2) do
@ -821,7 +764,7 @@ function flylib.rotate_nodes(pos, posses1, rot)
minetest.add_node(item.pos, {name = item.name, param2 = item.param2}) minetest.add_node(item.pos, {name = item.name, param2 = item.param2})
end end
end end
return posses2 return lpos2
end end
function flylib.exchange_node(pos, name, param2) function flylib.exchange_node(pos, name, param2)

View File

@ -3,7 +3,7 @@
TechAge TechAge
======= =======
Copyright (C) 2019-2022 Joachim Stolberg Copyright (C) 2019-2023 Joachim Stolberg
AGPL v3 AGPL v3
See LICENSE.txt for more information See LICENSE.txt for more information
@ -520,6 +520,20 @@ function NodeStates:on_beduino_request_data(pos, topic, payload)
end end
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 -- restart timer
function NodeStates:on_node_load(pos) function NodeStates:on_node_load(pos)
local nvm = techage.get_nvm(pos) local nvm = techage.get_nvm(pos)

View File

@ -55,6 +55,6 @@ minetest.register_on_mods_loaded(function()
beduino.lib.register_SystemHandler(0x140, ta_kv_init) beduino.lib.register_SystemHandler(0x140, ta_kv_init)
beduino.lib.register_SystemHandler(0x141, ta_kv_add) beduino.lib.register_SystemHandler(0x141, ta_kv_add)
beduino.lib.register_SystemHandler(0x142, ta_kv_get) beduino.lib.register_SystemHandler(0x142, ta_kv_get)
vm16.register_ro_file("beduino", "ta_kvstore.c", kvstore_c) vm16.register_ro_file("beduino", "lib/ta_kvstore.c", kvstore_c)
end end
end) end)

View File

@ -30,7 +30,7 @@ local TOTAL_MAX = INV_SIZE * FUEL_STACK_MAX
local function count_coal(metadata) local function count_coal(metadata)
local total = 0 local total = 0
for _,stack in pairs(metadata.inventory.fuel) do for _,stack in pairs(metadata.inventory.fuel or {}) do
total = total + stack:get_count() total = total + stack:get_count()
end end
return total return total

View File

@ -115,7 +115,7 @@ techage.Items = {
ta3_sequencer = "techage:ta3_sequencer", ta3_sequencer = "techage:ta3_sequencer",
ta3_timer = "techage:ta3_timer", ta3_timer = "techage:ta3_timer",
ta3_terminal = "techage:terminal2", ta3_terminal = "techage:terminal2",
ta3_signallamp = "techage:signal_lamp_off", ta3_colorlamp = "techage:color_lamp_off",
ta3_doorblock = "techage:doorblock20", ta3_doorblock = "techage:doorblock20",
ta3_programmer = "techage:programmer", ta3_programmer = "techage:programmer",
ta3_doorcontroller = "techage:ta3_doorcontroller", ta3_doorcontroller = "techage:ta3_doorcontroller",

View File

@ -113,7 +113,7 @@ techage.manual_DE.aTitel = {
"3,TA3 Sequenzer / Sequencer", "3,TA3 Sequenzer / Sequencer",
"3,TA3 Timer", "3,TA3 Timer",
"3,TA3 Terminal", "3,TA3 Terminal",
"3,TechAge Signallampe / Signal Lamp", "3,TechAge Farblampe / Color Lamp",
"3,Tür/Tor Blöcke / Door/Gate Blocks", "3,Tür/Tor Blöcke / Door/Gate Blocks",
"3,TA3 Tür Controller / Door Controller", "3,TA3 Tür Controller / Door Controller",
"3,TA3 Tür Controller II / Door Controller II", "3,TA3 Tür Controller II / Door Controller II",
@ -1133,7 +1133,7 @@ techage.manual_DE.aText = {
"\n".. "\n"..
"\n".. "\n"..
"\n", "\n",
"Die Signallampe kann mit 'on'/'off' Kommando ein- bzw. ausgeschaltet werden. Diese Lampe braucht keinen Strom und\n".. "Die Farblampe kann mit 'on'/'off' Kommando ein- bzw. ausgeschaltet werden. Diese Lampe braucht keinen Strom und\n"..
"kann mit der Spritzpistole aus der Mod \"Unified Dyes\" und über Lua/Beduino Kommandos eingefärbt werden.\n".. "kann mit der Spritzpistole aus der Mod \"Unified Dyes\" und über Lua/Beduino Kommandos eingefärbt werden.\n"..
"\n".. "\n"..
"Mit dem Chat-Kommando '/ta_color' wird die Farbpalette mit den Werten für die Lua/Beduino Kommandos angezeigt und mit '/ta_send color <num>' kann die Farbe geändert werden.\n".. "Mit dem Chat-Kommando '/ta_color' wird die Farbpalette mit den Werten für die Lua/Beduino Kommandos angezeigt und mit '/ta_send color <num>' kann die Farbe geändert werden.\n"..
@ -2282,7 +2282,7 @@ techage.manual_DE.aItemName = {
"ta3_sequencer", "ta3_sequencer",
"ta3_timer", "ta3_timer",
"ta3_terminal", "ta3_terminal",
"ta3_signallamp", "ta3_colorlamp",
"ta3_doorblock", "ta3_doorblock",
"ta3_doorcontroller", "ta3_doorcontroller",
"ta3_doorcontroller", "ta3_doorcontroller",

View File

@ -113,7 +113,7 @@ techage.manual_EN.aTitel = {
"3,TA3 Sequencer", "3,TA3 Sequencer",
"3,TA3 Timer", "3,TA3 Timer",
"3,TA3 Terminal", "3,TA3 Terminal",
"3,TechAge Signal Lamp", "3,TechAge Color Lamp",
"3,Door/Gate Blocks", "3,Door/Gate Blocks",
"3,TA3 Door Controller", "3,TA3 Door Controller",
"3,TA3 Door Controller II", "3,TA3 Door Controller II",
@ -2286,7 +2286,7 @@ techage.manual_EN.aItemName = {
"ta3_sequencer", "ta3_sequencer",
"ta3_timer", "ta3_timer",
"ta3_terminal", "ta3_terminal",
"ta3_signallamp", "ta3_colorlamp",
"ta3_doorblock", "ta3_doorblock",
"ta3_doorcontroller", "ta3_doorcontroller",
"ta3_doorcontroller", "ta3_doorcontroller",

View File

@ -217,7 +217,6 @@ minetest.register_node("techage:ta5_fr_controller_pas", {
on_timer = node_timer, on_timer = node_timer,
after_dig_node = after_dig_node, after_dig_node = after_dig_node,
on_receive_fields = on_receive_fields, on_receive_fields = on_receive_fields,
drawtype = "nodebox",
paramtype2 = "facedir", paramtype2 = "facedir",
groups = {choppy=2, cracky=2, crumbly=2}, groups = {choppy=2, cracky=2, crumbly=2},
is_ground_content = false, is_ground_content = false,
@ -257,7 +256,6 @@ minetest.register_node("techage:ta5_fr_controller_act", {
on_timer = node_timer, on_timer = node_timer,
after_dig_node = after_dig_node, after_dig_node = after_dig_node,
on_receive_fields = on_receive_fields, on_receive_fields = on_receive_fields,
drawtype = "nodebox",
paramtype2 = "facedir", paramtype2 = "facedir",
groups = {choppy=2, cracky=2, crumbly=2, not_in_creative_inventory=1}, groups = {choppy=2, cracky=2, crumbly=2, not_in_creative_inventory=1},
drop = "", drop = "",

View File

@ -54,7 +54,6 @@ minetest.register_node("techage:ta5_magnet1", {
Cable:after_dig_node(pos) Cable:after_dig_node(pos)
techage.del_mem(pos) techage.del_mem(pos)
end, end,
drawtype = "nodebox",
paramtype2 = "facedir", paramtype2 = "facedir",
groups = {choppy=2, cracky=2, crumbly=2}, groups = {choppy=2, cracky=2, crumbly=2},
is_ground_content = false, is_ground_content = false,
@ -88,7 +87,6 @@ minetest.register_node("techage:ta5_magnet2", {
Cable:after_dig_node(pos) Cable:after_dig_node(pos)
techage.del_mem(pos) techage.del_mem(pos)
end, end,
drawtype = "nodebox",
paramtype2 = "facedir", paramtype2 = "facedir",
groups = {choppy=2, cracky=2, crumbly=2}, groups = {choppy=2, cracky=2, crumbly=2},
is_ground_content = false, is_ground_content = false,

View File

@ -3,7 +3,7 @@
TechAge TechAge
======= =======
Copyright (C) 2019-2022 Joachim Stolberg Copyright (C) 2019-2023 Joachim Stolberg
AGPL v3 AGPL v3
See LICENSE.txt for more information See LICENSE.txt for more information
@ -13,7 +13,7 @@
techage = {} techage = {}
-- Version for compatibility checks, see readme.md/history -- Version for compatibility checks, see readme.md/history
techage.version = 1.08 techage.version = 1.10
if minetest.global_exists("tubelib") then if minetest.global_exists("tubelib") then
minetest.log("error", "[techage] Techage can't be used together with the mod tubelib!") minetest.log("error", "[techage] Techage can't be used together with the mod tubelib!")
@ -27,8 +27,8 @@ elseif minetest.global_exists("techpack") then
elseif minetest.global_exists("tubelib2") and tubelib2.version < 2.2 then elseif minetest.global_exists("tubelib2") and tubelib2.version < 2.2 then
minetest.log("error", "[techage] Techage requires tubelib2 version 2.2 or newer!") minetest.log("error", "[techage] Techage requires tubelib2 version 2.2 or newer!")
return return
elseif minetest.global_exists("minecart") and minecart.version < 1.08 then elseif minetest.global_exists("minecart") and minecart.version < 2.03 then
minetest.log("error", "[techage] Techage requires minecart version 1.08 or newer!") minetest.log("error", "[techage] Techage requires minecart version 2.03 or newer!")
return return
elseif minetest.global_exists("lcdlib") and lcdlib.version < 1.01 then elseif minetest.global_exists("lcdlib") and lcdlib.version < 1.01 then
minetest.log("error", "[techage] Techage requires lcdlib version 1.01 or newer!") minetest.log("error", "[techage] Techage requires lcdlib version 1.01 or newer!")

View File

@ -43,6 +43,13 @@ else
sounds = default.node_sound_stone_defaults(), sounds = default.node_sound_stone_defaults(),
}) })
-- Needs to be a techage recipe, not to overwrite the clay/bakedclay recipe
techage.furnace.register_recipe({
output = "techage:cement_block",
recipe = {
"default:clay",
},
})
techage.add_grinder_recipe({input="techage:cement_block", output="techage:cement_powder"}) techage.add_grinder_recipe({input="techage:cement_block", output="techage:cement_powder"})
techage.add_grinder_recipe({input="bakedclay:white", output="techage:cement_powder"}) techage.add_grinder_recipe({input="bakedclay:white", output="techage:cement_powder"})
end end

View File

@ -30,7 +30,7 @@ if minetest.global_exists("mesecon") then
}, },
time = 6, time = 6,
}) })
else end
techage.furnace.register_recipe({ techage.furnace.register_recipe({
output = "techage:ta4_silicon_wafer 16", output = "techage:ta4_silicon_wafer 16",
recipe = { recipe = {
@ -41,5 +41,5 @@ else
}, },
time = 6, time = 6,
}) })
end

View File

@ -171,11 +171,26 @@ minetest.register_craft({
}, },
}) })
local function contains(table, element)
for _, value in pairs(table) do
if value == element then
return true
end
end
return false
end
function techage.register_flower(name) function techage.register_flower(name)
if contains(Flowers, name) then
return
end
Flowers[#Flowers+1] = name Flowers[#Flowers+1] = name
end end
function techage.register_plant(name) function techage.register_plant(name)
if contains(Plants, name) then
return
end
Plants[name] = true Plants[name] = true
end end
@ -201,4 +216,5 @@ minetest.after(1, function()
end end
end end
end end
-- print(dump(Flowers))
end) end)

View File

@ -343,7 +343,7 @@ techage.register_node({"techage:t4_pump", "techage:t4_pump_on"}, {
end end
end, end,
on_beduino_receive_cmnd = function(pos, src, topic, payload) on_beduino_receive_cmnd = function(pos, src, topic, payload)
if topic == 69 and payload then -- Set pump limit if (topic == 69 or topic == 21) and payload then -- Set pump limit
local nvm = techage.get_nvm(pos) local nvm = techage.get_nvm(pos)
State4:stop(pos, nvm) State4:stop(pos, nvm)
if payload[1] > 0 then if payload[1] > 0 then

View File

@ -3,7 +3,7 @@
TechAge TechAge
======= =======
Copyright (C) 2017-2020 Joachim Stolberg Copyright (C) 2017-2023 Joachim Stolberg
AGPL v3 AGPL v3
See LICENSE.txt for more information See LICENSE.txt for more information
@ -394,11 +394,16 @@ minetest.register_craft({
}, },
}) })
techage.register_node({"techage:ta3_button_off", "techage:ta3_button_on"}, {})
techage.register_node({ techage.register_node({
"techage:ta4_button_off", "techage:ta4_button_on", "techage:ta4_button_off", "techage:ta4_button_on",
}, { }, {
on_recv_message = function(pos, src, topic, payload) on_recv_message = function(pos, src, topic, payload)
if topic == "name" then if topic == "state" then
local name = techage.get_node_lvm(pos).name
return name == "techage:ta4_button_on" and "on" or "off"
elseif topic == "name" then
local mem = techage.get_mem(pos) local mem = techage.get_mem(pos)
return mem.clicker or "" return mem.clicker or ""
elseif topic == "time" then elseif topic == "time" then
@ -409,7 +414,10 @@ techage.register_node({
end end
end, end,
on_beduino_request_data = function(pos, src, topic, payload) on_beduino_request_data = function(pos, src, topic, payload)
if topic == 144 then -- Player Name if topic == 131 then -- State
local name = techage.get_node_lvm(pos).name
return 0, name == "techage:ta4_button_on" and {1} or {0}
elseif topic == 144 then -- Player Name
local mem = techage.get_mem(pos) local mem = techage.get_mem(pos)
return 0, mem.clicker return 0, mem.clicker
elseif topic == 149 then --time elseif topic == 149 then --time

View File

@ -244,6 +244,8 @@ minetest.register_node("techage:ta4_button_2x", {
sounds = default.node_sound_glass_defaults(), sounds = default.node_sound_glass_defaults(),
}) })
techage.register_node({"techage:ta4_button_2x"}, {})
minetest.register_craft({ minetest.register_craft({
output = "techage:ta4_button_2x", output = "techage:ta4_button_2x",
recipe = { recipe = {
@ -252,3 +254,12 @@ minetest.register_craft({
{"", "", ""}, {"", "", ""},
}, },
}) })
minetest.register_craft({
output = "techage:ta4_button_off 2",
recipe = {
{"", "", ""},
{"", "techage:ta4_button_2x", ""},
{"", "", ""},
},
})

View File

@ -300,6 +300,8 @@ minetest.register_node("techage:ta4_button_4x", {
sounds = default.node_sound_glass_defaults(), sounds = default.node_sound_glass_defaults(),
}) })
techage.register_node({"techage:ta4_button_4x"}, {})
minetest.register_craft({ minetest.register_craft({
output = "techage:ta4_button_4x", output = "techage:ta4_button_4x",
recipe = { recipe = {
@ -308,3 +310,12 @@ minetest.register_craft({
{"", "", ""}, {"", "", ""},
}, },
}) })
minetest.register_craft({
output = "techage:ta4_button_off 4",
recipe = {
{"", "", ""},
{"", "techage:ta4_button_4x", ""},
{"", "", ""},
},
})

View File

@ -3,7 +3,7 @@
TechAge TechAge
======= =======
Copyright (C) 2017-2022 Joachim Stolberg Copyright (C) 2017-2023 Joachim Stolberg
AGPL v3 AGPL v3
See LICENSE.txt for more information See LICENSE.txt for more information
@ -21,7 +21,7 @@ local logic = techage.logic
local BLOCKING_TIME = 8 -- seconds local BLOCKING_TIME = 8 -- seconds
local ON_TIME = 1 local ON_TIME = 1
local WRENCH_MENU = { local WRENCH_MENU3 = {
{ {
type = "dropdown", type = "dropdown",
choices = "1,2,4,6,8,12,16", choices = "1,2,4,6,8,12,16",
@ -47,6 +47,46 @@ local WRENCH_MENU = {
} }
} }
local WRENCH_MENU4 = {
{
type = "dropdown",
choices = "1,2,4,6,8,12,16",
name = "ontime",
label = S("On Time") .. " [s]",
tooltip = S("The time between the 'on' and 'off' commands."),
default = "1",
},
{
type = "dropdown",
choices = "2,4,6,8,12,16,20",
name = "blockingtime",
label = S("Blocking Time") .. " [s]",
tooltip = S("The time after the 'off' command\nuntil the next 'on' command is accepted."),
default = "8",
},
{
type = "number",
name = "countdown",
label = S("Countdown"),
tooltip = S("Counts down the number of items passed through\nand only triggers an 'on' command when it reaches zero."),
default = "0",
},
{
type = "output",
name = "countdown",
label = S("Current countdown"),
tooltip = S("Current countdown value."),
default = "0",
},
{
type = "items",
name = "config",
label = S("Configured Items"),
tooltip = S("Items which generate an 'on' command.\nIf empty, all passed items generate an 'on' command."),
size = 4,
}
}
local function switch_on(pos) local function switch_on(pos)
local mem = techage.get_mem(pos) local mem = techage.get_mem(pos)
local t = minetest.get_gametime() local t = minetest.get_gametime()
@ -122,6 +162,17 @@ local function after_dig_node(pos, oldnode, oldmetadata, digger)
techage.del_mem(pos) techage.del_mem(pos)
end end
local function ta_after_formspec(pos, fields, playername)
if fields.save then
local nvm = techage.get_nvm(pos)
local val = M(pos):get_int("countdown") or 0
if val > 0 then
nvm.countdown = val
else
nvm.countdown = nil
end
end
end
minetest.register_node("techage:ta3_detector_off", { minetest.register_node("techage:ta3_detector_off", {
description = S("TA3 Detector"), description = S("TA3 Detector"),
@ -139,7 +190,7 @@ minetest.register_node("techage:ta3_detector_off", {
on_receive_fields = on_receive_fields, on_receive_fields = on_receive_fields,
techage_set_numbers = techage_set_numbers, techage_set_numbers = techage_set_numbers,
after_dig_node = after_dig_node, after_dig_node = after_dig_node,
ta3_formspec = WRENCH_MENU, ta3_formspec = WRENCH_MENU3,
on_rotate = screwdriver.disallow, on_rotate = screwdriver.disallow,
paramtype = "light", paramtype = "light",
@ -167,7 +218,7 @@ minetest.register_node("techage:ta3_detector_on", {
on_rotate = screwdriver.disallow, on_rotate = screwdriver.disallow,
techage_set_numbers = techage_set_numbers, techage_set_numbers = techage_set_numbers,
after_dig_node = after_dig_node, after_dig_node = after_dig_node,
ta3_formspec = WRENCH_MENU, ta3_formspec = WRENCH_MENU3,
paramtype2 = "facedir", paramtype2 = "facedir",
groups = {choppy=2, cracky=2, crumbly=2, not_in_creative_inventory=1}, groups = {choppy=2, cracky=2, crumbly=2, not_in_creative_inventory=1},
@ -192,7 +243,8 @@ minetest.register_node("techage:ta4_detector_off", {
on_receive_fields = on_receive_fields, on_receive_fields = on_receive_fields,
techage_set_numbers = techage_set_numbers, techage_set_numbers = techage_set_numbers,
after_dig_node = after_dig_node, after_dig_node = after_dig_node,
ta3_formspec = WRENCH_MENU, ta4_formspec = WRENCH_MENU4,
ta_after_formspec = ta_after_formspec,
on_rotate = screwdriver.disallow, on_rotate = screwdriver.disallow,
paramtype = "light", paramtype = "light",
@ -220,7 +272,8 @@ minetest.register_node("techage:ta4_detector_on", {
on_rotate = screwdriver.disallow, on_rotate = screwdriver.disallow,
techage_set_numbers = techage_set_numbers, techage_set_numbers = techage_set_numbers,
after_dig_node = after_dig_node, after_dig_node = after_dig_node,
ta3_formspec = WRENCH_MENU, ta4_formspec = WRENCH_MENU4,
ta_after_formspec = ta_after_formspec,
paramtype2 = "facedir", paramtype2 = "facedir",
groups = {choppy=2, cracky=2, crumbly=2, not_in_creative_inventory=1}, groups = {choppy=2, cracky=2, crumbly=2, not_in_creative_inventory=1},
@ -268,13 +321,22 @@ techage.register_node({"techage:ta4_detector_off", "techage:ta4_detector_on"}, {
if leftover then if leftover then
local inv = minetest.get_inventory({type = "node", pos = pos}) local inv = minetest.get_inventory({type = "node", pos = pos})
if not inv or inv:is_empty("cfg") or inv:contains_item("cfg", ItemStack(stack:get_name())) then if not inv or inv:is_empty("cfg") or inv:contains_item("cfg", ItemStack(stack:get_name())) then
switch_on(pos)
local nvm = techage.get_nvm(pos) local nvm = techage.get_nvm(pos)
if leftover == true then local num_moved = stack:get_count()
nvm.counter = (nvm.counter or 0) + stack:get_count() if leftover ~= true then
else num_moved = num_moved - leftover:get_count()
nvm.counter = (nvm.counter or 0) + stack:get_count() - leftover:get_count()
end end
if nvm.countdown and nvm.countdown > 0 then
nvm.countdown = nvm.countdown - num_moved
if nvm.countdown <= 0 then
M(pos):set_int("countdown", 0)
switch_on(pos)
end
elseif nvm.countdown == nil then
switch_on(pos)
end
nvm.counter = (nvm.counter or 0) + num_moved
end end
return leftover return leftover
end end
@ -286,9 +348,15 @@ techage.register_node({"techage:ta4_detector_off", "techage:ta4_detector_on"}, {
if topic == "count" then if topic == "count" then
local nvm = techage.get_nvm(pos) local nvm = techage.get_nvm(pos)
return nvm.counter or 0 return nvm.counter or 0
elseif topic == "countdown" then
local nvm = techage.get_nvm(pos)
nvm.countdown = tonumber(payload) or 0
M(pos):set_int("countdown", nvm.countdown)
return true
elseif topic == "reset" then elseif topic == "reset" then
local nvm = techage.get_nvm(pos) local nvm = techage.get_nvm(pos)
nvm.counter = 0 nvm.counter = 0
nvm.countdown = nil
return true return true
else else
return "unsupported" return "unsupported"
@ -298,6 +366,12 @@ techage.register_node({"techage:ta4_detector_off", "techage:ta4_detector_on"}, {
if topic == 6 then -- Detector Block Reset if topic == 6 then -- Detector Block Reset
local nvm = techage.get_nvm(pos) local nvm = techage.get_nvm(pos)
nvm.counter = 0 nvm.counter = 0
nvm.countdown = nil
return 0
elseif topic == 5 then -- Detector Block Countdown
local nvm = techage.get_nvm(pos)
nvm.countdown = tonumber(payload[1]) or 0
M(pos):set_int("countdown", nvm.countdown)
return 0 return 0
else else
return 2 return 2

View File

@ -3,7 +3,7 @@
TechAge TechAge
======= =======
Copyright (C) 2022 Joachim Stolberg Copyright (C) 2022-2023 Joachim Stolberg
AGPL v3 AGPL v3
See LICENSE.txt for more information See LICENSE.txt for more information
@ -104,7 +104,6 @@ local function register_signallamp(name, description, tiles_off, tiles_on, node_
paramtype = "light", paramtype = "light",
paramtype2 = "color", paramtype2 = "color",
--palette = "techage_palette256.png",
palette = COLORED and "unifieddyes_palette_extended.png" or "techage_palette256.png", palette = COLORED and "unifieddyes_palette_extended.png" or "techage_palette256.png",
groups = {choppy=2, cracky=1, not_in_creative_inventory=1, ud_param2_colorable = 1}, groups = {choppy=2, cracky=1, not_in_creative_inventory=1, ud_param2_colorable = 1},
@ -148,7 +147,7 @@ local function register_signallamp(name, description, tiles_off, tiles_on, node_
local node = techage.get_node_lvm(pos) local node = techage.get_node_lvm(pos)
switch_off(pos, node) switch_off(pos, node)
return 0 return 0
elseif topic == 70 then elseif topic == 70 or topic == 22 then
local node = techage.get_node_lvm(pos) local node = techage.get_node_lvm(pos)
switch_on(pos, node, nil, payload[1]) switch_on(pos, node, nil, payload[1])
return 0 return 0
@ -196,8 +195,8 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
end) end)
register_signallamp("techage:signal_lamp", register_signallamp("techage:color_lamp",
S("TechAge Signal Lamp"), S("TechAge Color Lamp"),
{"techage_signal_lamp.png^[colorize:#000000:80"}, {"techage_signal_lamp.png^[colorize:#000000:80"},
{"techage_signal_lamp.png"}, {"techage_signal_lamp.png"},
{ {
@ -209,8 +208,8 @@ register_signallamp("techage:signal_lamp",
} }
) )
register_signallamp("techage:signal_lamp2", register_signallamp("techage:color_lamp2",
S("TechAge Signal Lamp 2 "), S("TechAge Color Lamp 2"),
{"techage_signallamp2.png^[colorize:#000000:80"}, {"techage_signallamp2.png^[colorize:#000000:80"},
{"techage_signallamp2.png"} {"techage_signallamp2.png"}
) )
@ -232,3 +231,8 @@ minetest.register_craft({
{"", "techage:vacuum_tube", ""}, {"", "techage:vacuum_tube", ""},
}, },
}) })
minetest.register_alias("techage:signal_lamp_off", "techage:color_lamp_off")
minetest.register_alias("techage:signal_lamp2_off", "techage:color_lamp2_off")
minetest.register_alias("techage:signal_lamp_on", "techage:color_lamp_on")
minetest.register_alias("techage:signal_lamp2_on", "techage:color_lamp2_on")

View File

@ -628,14 +628,14 @@ Im privaten Modus (private) kann das Terminal nur von Spielern verwendet werden,
[ta3_terminal|image] [ta3_terminal|image]
### TechAge Signallampe / Signal Lamp ### TechAge Farblampe / Color Lamp
Die Signallampe kann mit `on`/`off` Kommando ein- bzw. ausgeschaltet werden. Diese Lampe braucht keinen Strom und Die Farblampe kann mit `on`/`off` Kommando ein- bzw. ausgeschaltet werden. Diese Lampe braucht keinen Strom und
kann mit der Spritzpistole aus der Mod "Unified Dyes" und über Lua/Beduino Kommandos eingefärbt werden. kann mit der Spritzpistole aus der Mod "Unified Dyes" und über Lua/Beduino Kommandos eingefärbt werden.
Mit dem Chat-Kommando `/ta_color` wird die Farbpalette mit den Werten für die Lua/Beduino Kommandos angezeigt und mit `/ta_send color <num>` kann die Farbe geändert werden. Mit dem Chat-Kommando `/ta_color` wird die Farbpalette mit den Werten für die Lua/Beduino Kommandos angezeigt und mit `/ta_send color <num>` kann die Farbe geändert werden.
[ta3_signallamp|image] [ta3_colorlamp|image]
### Tür/Tor Blöcke / Door/Gate Blocks ### Tür/Tor Blöcke / Door/Gate Blocks

View File

@ -627,13 +627,13 @@ In public mode, all players can use the preconfigured keys.
[ta3_terminal|image] [ta3_terminal|image]
### TechAge Signal Lamp ### TechAge Color Lamp
The signal lamp can be switched on or off with the `on` / `off` command. This lamp does not need electricity and can be colored with the airbrush tool from the mod Unified Dyes" and via Lua/Beduino commands. The signal lamp can be switched on or off with the `on` / `off` command. This lamp does not need electricity and can be colored with the airbrush tool from the mod Unified Dyes" and via Lua/Beduino commands.
With the chat command `/ta_color` the color palette with the values for the Lua/Beduino commands is displayed and with `/ta_send color <num>` the color can be changed. With the chat command `/ta_color` the color palette with the values for the Lua/Beduino commands is displayed and with `/ta_send color <num>` the color can be changed.
[ta3_signallamp|image] [ta3_colorlamp|image]
### Door/Gate Blocks ### Door/Gate Blocks

View File

@ -363,6 +363,7 @@ Please note, that this is not a technical distinction, only a logical.
| "state" | one of: "running", "stopped", "blocked", "standby", "fault", or "unloaded" | Techage machine state, used by many machines | | "state" | one of: "running", "stopped", "blocked", "standby", "fault", or "unloaded" | Techage machine state, used by many machines |
| "state" | one of: "red", "amber", "green", "off" | Signal Tower state | | "state" | one of: "red", "amber", "green", "off" | Signal Tower state |
| "state" | one of: "empty", "loaded", "full" | State of a chest or Sensor Chest | | "state" | one of: "empty", "loaded", "full" | State of a chest or Sensor Chest |
| "state" | one of: "on", "off" | State of a TA4 Button |
| "fuel" | number | fuel value of a fuel consuming block | | "fuel" | number | fuel value of a fuel consuming block |
| "depth" | number | Read the current depth value of a quarry block (1..80) | | "depth" | number | Read the current depth value of a quarry block (1..80) |
| "load" | number | Read the load value in percent (0..100) of a tank, silo, accu, or battery block, or from the Signs Bot Box. Silo and tank return two values: The percentage value and the absolute value in units.<br /> Example: percent, absolute = $send_cmnd("223", "load") | | "load" | number | Read the load value in percent (0..100) of a tank, silo, accu, or battery block, or from the Signs Bot Box. Silo and tank return two values: The percentage value and the absolute value in units.<br /> Example: percent, absolute = $send_cmnd("223", "load") |
@ -396,7 +397,8 @@ Please note, that this is not a technical distinction, only a logical.
| "port" | string<br />`<color>=on/off` | Enable/disable a Distributor filter slot..<br />Example: `"yellow=on"`<br />colors: red, green, blue, yellow | | "port" | string<br />`<color>=on/off` | Enable/disable a Distributor filter slot..<br />Example: `"yellow=on"`<br />colors: red, green, blue, yellow |
| "config" | "\<slot> \<item list>" | Configure a Distributor filter slot, like: "red default:dirt dye:blue" | | "config" | "\<slot> \<item list>" | Configure a Distributor filter slot, like: "red default:dirt dye:blue" |
| "text" | text string | Text to be used for the Sensor Chest menu | | "text" | text string | Text to be used for the Sensor Chest menu |
| "reset" | nil | Reset the item counter of the TA4 Item Detector block | | "reset" | nil | Reset item and countdown counters of the TA4 Item Detector block |
| "countdown" | number | Set countdown counter of the TA4 Item Detector block to the given value and<br />start countdown mode. |
| "limit" | number | Configure a TA4 Pusher with the number of items that are allowed to be pushed ("flow limiter" mode)<br />limit = 0 turns off the "flow limiter" mode | | "limit" | number | Configure a TA4 Pusher with the number of items that are allowed to be pushed ("flow limiter" mode)<br />limit = 0 turns off the "flow limiter" mode |
| "limit" | number | Configure a TA4 Pump with the number of liquid units that are allowed to be pumped ("flow limiter" mode)<br />limit = 0 turns off the "flow limiter" mode | | "limit" | number | Configure a TA4 Pump with the number of liquid units that are allowed to be pumped ("flow limiter" mode)<br />limit = 0 turns off the "flow limiter" mode |
| "config" | item string | Configure the TA4 pusher.<br />Example: `wool:blue` | | "config" | item string | Configure the TA4 pusher.<br />Example: `wool:blue` |
@ -417,7 +419,7 @@ Please note, that this is not a technical distinction, only a logical.
| "stop" | nil | Stop command for the TA4 Sequencer. | | "stop" | nil | Stop command for the TA4 Sequencer. |
| "gain" | volume | Set volume of the sound block (`volume` is a value between 0 and 1.0) | | "gain" | volume | Set volume of the sound block (`volume` is a value between 0 and 1.0) |
| "sound" | index | Select sound sample of the sound block | | "sound" | index | Select sound sample of the sound block |
| "color" | \<color> | Set the color of the TechAge Signal Lamp and TechAge Signal Lamp 2 (color = 0..255) | | "color" | \<color> | Set the color of the TechAge Color Lamp and TechAge Color Lamp 2 (color = 0..255) |
### Server and Terminal Functions ### Server and Terminal Functions

View File

@ -1,85 +0,0 @@
# Techage/Beduino I/O Module
I/O modules support the following functions:
### event
Every signal that is sent to an I/O module triggers an event on the controller.
Events can be queried using the `event()` function.
If the function returns the value `1`, one or more signals have been received.
Calling `event()` resets the event flag.
```c
event()
```
### read
Read a value from a remote techage block.
- *port* is the I/O module port number
- *cmnd* is the command, like `IO_STATE` (see example code "ta_cmnd.c")
```c
read(port, cmnd)
```
### send_cmnd
Send a command to a techage block (see [commands](https://github.com/joe7575/beduino/blob/main/BEPs/bep-005_ta_cmnd.md)).
- *port* is the I/O module port number
- *topic* is a number from the list of [Beduino commands](https://github.com/joe7575/beduino/blob/main/BEPs/bep-005_ta_cmnd.md)
- *payload* is an array or a string with additional information, depending on the command. If no additional commands are required, "" can be used.
```c
send_cmnd(port, topic, payload)
```
### request_data
Request information from a techage block (see [commands](https://github.com/joe7575/beduino/blob/main/BEPs/bep-005_ta_cmnd.md)).
- *port* is the I/O module port number
- *topic* is a number from the list of [Beduino commands](https://github.com/joe7575/beduino/blob/main/BEPs/bep-005_ta_cmnd.md)
- *payload* is an array or a string with additional information, depending on the command. If no additional commands are required, "" can be used.
- *resp* is an array for the response data. The array must be defined large enough to hold the response data.
```c
request_data(port, topic, payload, resp)
```
## Functions for TA4 Display and TA4 Display XL
### clear_screen
Clear the display.
- *port* is the I/O module port number
```c
clear_screen(port)
```
### append_line
Add a new line to the display.
- *port* is the I/O module port number
- *text* is the text for one line
```c
append_line(port, text)
```
### write_line
Overwrite a text line with the given string.
- *port* is the I/O module port number
- *row* ist the display line/row (1-5)
- *text* is the text for one line
```c
write_line(port, row, text)
```

View File

@ -5,8 +5,8 @@ The key/value store simplifies the handling/comparison of strings.
The following example shows the use of the Key/Value Store, here to check the names from the Player Detector: The following example shows the use of the Key/Value Store, here to check the names from the Player Detector:
```c ```c
import "ta_kvstore.c" import "lib/ta_kvstore.c"
import "ta_iom.c" import "lib/ta_iom.c"
var s[16]; var s[16];

View File

@ -112,7 +112,7 @@
- [TA3 Sequenzer / Sequencer](./manual_ta3_DE.md#ta3-sequenzer--sequencer) - [TA3 Sequenzer / Sequencer](./manual_ta3_DE.md#ta3-sequenzer--sequencer)
- [TA3 Timer](./manual_ta3_DE.md#ta3-timer) - [TA3 Timer](./manual_ta3_DE.md#ta3-timer)
- [TA3 Terminal](./manual_ta3_DE.md#ta3-terminal) - [TA3 Terminal](./manual_ta3_DE.md#ta3-terminal)
- [TechAge Signallampe / Signal Lamp](./manual_ta3_DE.md#techage-signallampe--signal-lamp) - [TechAge Farblampe / Color Lamp](./manual_ta3_DE.md#techage-farblampe--color-lamp)
- [Tür/Tor Blöcke / Door/Gate Blocks](./manual_ta3_DE.md#türtor-blöcke--doorgate-blocks) - [Tür/Tor Blöcke / Door/Gate Blocks](./manual_ta3_DE.md#türtor-blöcke--doorgate-blocks)
- [TA3 Tür Controller / Door Controller](./manual_ta3_DE.md#ta3-tür-controller--door-controller) - [TA3 Tür Controller / Door Controller](./manual_ta3_DE.md#ta3-tür-controller--door-controller)
- [TA3 Tür Controller II / Door Controller II](./manual_ta3_DE.md#ta3-tür-controller-ii--door-controller-ii) - [TA3 Tür Controller II / Door Controller II](./manual_ta3_DE.md#ta3-tür-controller-ii--door-controller-ii)

View File

@ -112,7 +112,7 @@
- [TA3 Sequencer](./manual_ta3_EN.md#ta3-sequencer) - [TA3 Sequencer](./manual_ta3_EN.md#ta3-sequencer)
- [TA3 Timer](./manual_ta3_EN.md#ta3-timer) - [TA3 Timer](./manual_ta3_EN.md#ta3-timer)
- [TA3 Terminal](./manual_ta3_EN.md#ta3-terminal) - [TA3 Terminal](./manual_ta3_EN.md#ta3-terminal)
- [TechAge Signal Lamp](./manual_ta3_EN.md#techage-signal-lamp) - [TechAge Color Lamp](./manual_ta3_EN.md#techage-color-lamp)
- [Door/Gate Blocks](./manual_ta3_EN.md#doorgate-blocks) - [Door/Gate Blocks](./manual_ta3_EN.md#doorgate-blocks)
- [TA3 Door Controller](./manual_ta3_EN.md#ta3-door-controller) - [TA3 Door Controller](./manual_ta3_EN.md#ta3-door-controller)
- [TA3 Door Controller II](./manual_ta3_EN.md#ta3-door-controller-ii) - [TA3 Door Controller II](./manual_ta3_EN.md#ta3-door-controller-ii)

View File

@ -3,12 +3,12 @@
TechAge TechAge
======= =======
Copyright (C) 2020-2022 Joachim Stolberg Copyright (C) 2020-2023 Joachim Stolberg
AGPL v3 AGPL v3
See LICENSE.txt for more information See LICENSE.txt for more information
TA4 Move Controller TA5 Fly Controller
]]-- ]]--
@ -42,6 +42,13 @@ local WRENCH_MENU = {
tooltip = S("Value in the range of 0.0 to 1.0"), tooltip = S("Value in the range of 0.0 to 1.0"),
default = "1.0", default = "1.0",
}, },
{
type = "float",
name = "offset",
label = S("Object offset"),
tooltip = S("Y-offset for non-player objects like vehicles (-0.5 to 0.5)"),
default = "0.0",
},
} }
local function formspec(nvm, meta) local function formspec(nvm, meta)
@ -165,8 +172,6 @@ minetest.register_node("techage:ta5_flycontroller", {
elseif fields.moveAB then elseif fields.moveAB then
meta:set_string("status", "") meta:set_string("status", "")
if fly.move_to_other_pos(pos, false) then if fly.move_to_other_pos(pos, false) then
nvm.moveBA = true
nvm.running = true
meta:set_string("formspec", formspec(nvm, meta)) meta:set_string("formspec", formspec(nvm, meta))
local name = player:get_player_name() local name = player:get_player_name()
mark.stop(name) mark.stop(name)
@ -175,8 +180,6 @@ minetest.register_node("techage:ta5_flycontroller", {
elseif fields.moveBA then elseif fields.moveBA then
meta:set_string("status", "") meta:set_string("status", "")
if fly.move_to_other_pos(pos, true) then if fly.move_to_other_pos(pos, true) then
nvm.moveBA = false
nvm.running = true
meta:set_string("formspec", formspec(nvm, meta)) meta:set_string("formspec", formspec(nvm, meta))
local name = player:get_player_name() local name = player:get_player_name()
mark.stop(name) mark.stop(name)
@ -185,8 +188,6 @@ minetest.register_node("techage:ta5_flycontroller", {
elseif fields.move then elseif fields.move then
meta:set_string("status", "") meta:set_string("status", "")
if fly.move_to_other_pos(pos, nvm.moveBA) then if fly.move_to_other_pos(pos, nvm.moveBA) then
nvm.moveBA = nvm.moveBA == false
nvm.running = true
meta:set_string("formspec", formspec(nvm, meta)) meta:set_string("formspec", formspec(nvm, meta))
local name = player:get_player_name() local name = player:get_player_name()
mark.stop(name) mark.stop(name)
@ -219,17 +220,11 @@ techage.register_node({"techage:ta5_flycontroller"}, {
elseif topic == "state" then elseif topic == "state" then
return nvm.running and "running" or "stopped" return nvm.running and "running" or "stopped"
elseif topic == "a2b" then elseif topic == "a2b" then
nvm.moveBA = true
nvm.running = true
return fly.move_to_other_pos(pos, false) return fly.move_to_other_pos(pos, false)
elseif topic == "b2a" then elseif topic == "b2a" then
nvm.moveBA = false
nvm.running = true
return fly.move_to_other_pos(pos, true) return fly.move_to_other_pos(pos, true)
elseif topic == "move" then elseif topic == "move" then
nvm.moveBA = nvm.moveBA == false return fly.move_to_other_pos(pos, nvm.moveBA)
nvm.running = true
return fly.move_to_other_pos(pos, nvm.moveBA == false)
end end
return false return false
end, end,
@ -237,17 +232,11 @@ techage.register_node({"techage:ta5_flycontroller"}, {
local nvm = techage.get_nvm(pos) local nvm = techage.get_nvm(pos)
if topic == 11 then if topic == 11 then
if payload[1] == 1 then if payload[1] == 1 then
nvm.moveBA = true
nvm.running = true
return fly.move_to_other_pos(pos, false) and 0 or 3 return fly.move_to_other_pos(pos, false) and 0 or 3
elseif payload[1] == 2 then elseif payload[1] == 2 then
nvm.moveBA = false
nvm.running = true
return fly.move_to_other_pos(pos, true) and 0 or 3 return fly.move_to_other_pos(pos, true) and 0 or 3
elseif payload[1] == 3 then elseif payload[1] == 3 then
nvm.moveBA = nvm.moveBA == false return fly.move_to_other_pos(pos, nvm.moveBA) and 0 or 3
nvm.running = true
return fly.move_to_other_pos(pos, nvm.moveBA == false) and 0 or 3
end end
else else
return 2 return 2
@ -260,6 +249,10 @@ techage.register_node({"techage:ta5_flycontroller"}, {
end end
return 2, "" return 2, ""
end, end,
on_node_load = function(pos, node)
local nvm = techage.get_nvm(pos)
nvm.running = false
end,
}) })
minetest.register_craft({ minetest.register_craft({

View File

@ -34,22 +34,6 @@ local WRENCH_MENU = {
tooltip = S("Maximum speed for moving blocks"), tooltip = S("Maximum speed for moving blocks"),
default = "8", default = "8",
}, },
{
type = "number",
name = "handoverB",
label = S("Handover to B"),
tooltip = S("Number of the next movecontroller"),
default = "",
check = techage.check_number,
},
{
type = "number",
name = "handoverA",
label = S("Handover to A"),
tooltip = S("Number of the previous movecontroller"),
default = "",
check = techage.check_number,
},
{ {
type = "float", type = "float",
name = "height", name = "height",

View File

@ -116,8 +116,9 @@ techage.register_node({"techage:ta3_soundblock"}, {
end end
end, end,
on_beduino_receive_cmnd = function(pos, src, topic, payload) on_beduino_receive_cmnd = function(pos, src, topic, payload)
print("ta3_soundblock", topic, payload[1], payload[2])
if topic == 1 then if topic == 1 then
if payload[1] == 0 then if payload[1] == 1 then
play_predefined_sound(pos) play_predefined_sound(pos)
return 0 return 0
end end

View File

@ -28,6 +28,7 @@ techage.power = {}
-- Helper function -- Helper function
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
local function round(val) local function round(val)
val = tonumber(val) or 0
if val > 100 then if val > 100 then
return math.floor(val + 0.5) return math.floor(val + 0.5)
elseif val > 10 then elseif val > 10 then

View File

@ -222,13 +222,29 @@ local function on_place(itemstack, placer, pointed_thing)
end end
end end
local function repair(itemstack, placer, pointed_thing)
if pointed_thing.type == "node" then
local pos = pointed_thing.under
if not placer or minetest.is_protected(pos, placer:get_player_name()) then
return
end
local number = techage.get_node_number(pos)
if number and not techage.get_node_info(number) then
techage.repair_number(pos)
minetest.chat_send_player(placer:get_player_name(), "Node repaired!")
itemstack:add_wear(65636/200)
return itemstack
end
end
end
minetest.register_tool("techage:repairkit", { minetest.register_tool("techage:repairkit", {
description = S("TechAge Repair Kit"), description = S("TechAge Repair Kit"),
inventory_image = "techage_repairkit.png", inventory_image = "techage_repairkit.png",
wield_image = "techage_repairkit.png^[transformR270", wield_image = "techage_repairkit.png^[transformR270",
groups = {cracky=1, book=1}, groups = {cracky=1, book=1},
--on_use = repair, on_use = repair,
--on_place = repair, on_place = repair,
node_placement_prediction = "", node_placement_prediction = "",
stack_max = 1, stack_max = 1,
}) })
@ -254,3 +270,12 @@ minetest.register_craft({
{"default:steel_ingot", "", ""}, {"default:steel_ingot", "", ""},
}, },
}) })
--minetest.register_craft({
-- output = "techage:repairkit",
-- recipe = {
-- {"", "", ""},
-- {"", "techage:end_wrench", ""},
-- {"", "", ""},
-- },
--})

View File

@ -102,3 +102,16 @@ Other files from Wikimedia Commons:
RealBadAngel: (CC-BY-4.0) RealBadAngel: (CC-BY-4.0)
* Everything else. * Everything else.
## Sounds
* [`bell.ogg`](https://freesound.org/people/bennstir/sounds/81072/) by bennstir, CC 4.0
* [`electricity.ogg`](https://freesound.org/people/Halleck/sounds/19486/) by Halleck, CC 4.0 (cut)
* [`pageflip1.ogg`](https://freesound.org/people/themfish/sounds/45823/) by themfish, CC 4.0 (cut, slowed down)
* `pageflip2.ogg` (derived from `pageflip1.ogg`)
* [`trash.ogg`](https://freesound.org/people/OwlStorm/sounds/151231/) by OwlStorm, CC 0 (speed up)
* [`trash_all.ogg`](https://freesound.org/people/abel_K/sounds/68280/) by abel_K, Sampling Plus 1.0 (speed up)
* [`ui_click.ogg`](https://freesound.org/people/lartti/sounds/527569/) by lartti, CC 0 (cut)
* [`ui_morning.ogg`](https://freesound.org/people/InspectorJ/sounds/439472/) by InspectorJ, CC 4.0
* [`ui_owl.ogg`](https://freesound.org/people/manda_g/sounds/54987/) by manda_g, Sampling Plus 1.0 (cut)

View File

@ -145,50 +145,18 @@ minetest.after(0.01, function()
end end
end end
-- Step 1: group-indexed lookup table for items -- Step 1: Initialize cache for looking up groups
local spec_matcher = {} unified_inventory.init_matching_cache()
for _, name in ipairs(ui.items_list) do
-- we only need to care about groups, exact items are handled separately
for group, value in pairs(minetest.registered_items[name].groups) do
if value and value ~= 0 then
if not spec_matcher[group] then
spec_matcher[group] = {}
end
spec_matcher[group][name] = true
end
end
end
-- Step 2: Find all matching items for the given spec (groups) -- Step 2: Find all matching items for the given spec (groups)
local function get_matching_spec_items(specname) local get_matching_spec_items = unified_inventory.get_matching_items
if specname:sub(1,6) ~= "group:" then
return { [specname] = true }
end
local accepted = {} for outputitemname, recipes in pairs(ui.crafts_for.recipe) do
for i, group in ipairs(specname:sub(7):split(",")) do
if i == 1 then
-- First step: Copy all possible item names in this group
for name, _ in pairs(spec_matcher[group] or {}) do
accepted[name] = true
end
else
-- Perform filtering
if spec_matcher[group] then
for name, _ in pairs(accepted) do
accepted[name] = spec_matcher[group][name]
end
else
-- No matching items
return {}
end
end
end
return accepted
end
for _, recipes in pairs(ui.crafts_for.recipe) do
-- List of crafts that return this item string (variable "_") -- List of crafts that return this item string (variable "_")
-- Problem: The group cache must be initialized after all mods finished loading
-- thus, invalid recipes might be indexed. Hence perform filtering with `new_recipe_list`
local new_recipe_list = {}
for _, recipe in ipairs(recipes) do for _, recipe in ipairs(recipes) do
local ingredient_items = {} local ingredient_items = {}
for _, spec in pairs(recipe.items) do for _, spec in pairs(recipe.items) do
@ -204,8 +172,15 @@ minetest.after(0.01, function()
end end
table.insert(ui.crafts_for.usage[name], recipe) table.insert(ui.crafts_for.usage[name], recipe)
end end
if next(ingredient_items) then
-- There's at least one known ingredient: mark as good recipe
-- PS: What whatll be done about partially incomplete recipes?
table.insert(new_recipe_list, recipe)
end end
end end
ui.crafts_for.recipe[outputitemname] = new_recipe_list
end
for _, callback in ipairs(ui.initialized_callbacks) do for _, callback in ipairs(ui.initialized_callbacks) do
callback() callback()

View File

@ -10,25 +10,26 @@ local F = minetest.formspec_escape
local ui = unified_inventory local ui = unified_inventory
ui.register_page("bags", { ui.register_page("bags", {
get_formspec = function(player) get_formspec = function(player, perplayer_formspec)
local player_name = player:get_player_name() local player_name = player:get_player_name()
return { formspec = table.concat({ local std_inv_x = perplayer_formspec.std_inv_x
ui.style_full.standard_inv_bg, local formspec = {
ui.single_slot(0.925, 1.5), perplayer_formspec.standard_inv_bg,
ui.single_slot(3.425, 1.5), "label[", perplayer_formspec.form_header_x, ",",
ui.single_slot(5.925, 1.5), perplayer_formspec.form_header_y, ";", F(S("Bags")), "]",
ui.single_slot(8.425, 1.5),
"label["..ui.style_full.form_header_x..","..ui.style_full.form_header_y..";" .. F(S("Bags")) .. "]",
"button[0.6125,2.75;1.875,0.75;bag1;" .. F(S("Bag @1", 1)) .. "]",
"button[3.1125,2.75;1.875,0.75;bag2;" .. F(S("Bag @1", 2)) .. "]",
"button[5.6125,2.75;1.875,0.75;bag3;" .. F(S("Bag @1", 3)) .. "]",
"button[8.1125,2.75;1.875,0.75;bag4;" .. F(S("Bag @1", 4)) .. "]",
"listcolors[#00000000;#00000000]", "listcolors[#00000000;#00000000]",
"list[detached:" .. F(player_name) .. "_bags;bag1;1.075,1.65;1,1;]", }
"list[detached:" .. F(player_name) .. "_bags;bag2;3.575,1.65;1,1;]",
"list[detached:" .. F(player_name) .. "_bags;bag3;6.075,1.65;1,1;]", for i = 1, 4 do
"list[detached:" .. F(player_name) .. "_bags;bag4;8.575,1.65;1,1;]" local x = std_inv_x + i * 2.5
}) } formspec[#formspec + 1] = ui.single_slot(x - 1.875, 1.5)
formspec[#formspec + 1] = string.format("list[detached:%s_bags;bag%i;%.3f,1.65;1,1;]",
F(player_name), i, x - 1.725)
formspec[#formspec + 1] = string.format("button[%.4f,2.75;1.875,0.75;bag%i;%s]",
x - 2.1875, i, F(S("Bag @1", i)))
end
return { formspec = table.concat(formspec) }
end, end,
}) })
@ -36,7 +37,6 @@ ui.register_button("bags", {
type = "image", type = "image",
image = "ui_bags_icon.png", image = "ui_bags_icon.png",
tooltip = S("Bags"), tooltip = S("Bags"),
hide_lite=true
}) })
local function get_player_bag_stack(player, i) local function get_player_bag_stack(player, i)
@ -48,23 +48,38 @@ end
for bag_i = 1, 4 do for bag_i = 1, 4 do
ui.register_page("bag" .. bag_i, { ui.register_page("bag" .. bag_i, {
get_formspec = function(player) get_formspec = function(player, perplayer_formspec)
local stack = get_player_bag_stack(player, bag_i) local stack = get_player_bag_stack(player, bag_i)
local image = stack:get_definition().inventory_image local image = stack:get_definition().inventory_image
local slots = stack:get_definition().groups.bagslots local slots = stack:get_definition().groups.bagslots
local std_inv_x = perplayer_formspec.std_inv_x
local lite_mode = perplayer_formspec.is_lite_mode
local bag_inv_y, header_x, header_y = 1.5, 0.3, 0.65
if lite_mode then
bag_inv_y = 0.5
header_x = perplayer_formspec.form_header_x
header_y = perplayer_formspec.form_header_y
end
local formspec = { local formspec = {
ui.style_full.standard_inv_bg, perplayer_formspec.standard_inv_bg,
ui.make_inv_img_grid(0.3, 1.5, 8, slots/8), ui.make_inv_img_grid(std_inv_x, bag_inv_y, 8, slots/8),
"image[9.2,0.4;1,1;" .. image .. "]", "label[", header_x, ",", header_y, ";", F(S("Bag @1", bag_i)), "]",
"label[0.3,0.65;" .. F(S("Bag @1", bag_i)) .. "]",
"listcolors[#00000000;#00000000]", "listcolors[#00000000;#00000000]",
"listring[current_player;main]", "listring[current_player;main]",
string.format("list[current_player;bag%icontents;%f,%f;8,3;]", string.format("list[current_player;bag%icontents;%f,%f;8,3;]",
bag_i, 0.3 + ui.list_img_offset, 1.5 + ui.list_img_offset), bag_i, std_inv_x + ui.list_img_offset, bag_inv_y + ui.list_img_offset),
"listring[current_name;bag" .. bag_i .. "contents]", "listring[current_name;bag", bag_i, "contents]",
} }
if lite_mode then
return { formspec = table.concat(formspec) }
end
local n = #formspec + 1 local n = #formspec + 1
formspec[n] = "image[" .. std_inv_x + 8.9 .. ",0.4;1,1;" .. image .. "]"
n = n + 1
local player_name = player:get_player_name() -- For if statement. local player_name = player:get_player_name() -- For if statement.
if ui.trash_enabled if ui.trash_enabled

View File

@ -57,30 +57,47 @@ end)
local function apply_new_filter(player, search_text, new_dir) local function apply_new_filter(player, search_text, new_dir)
local player_name = player:get_player_name() local player_name = player:get_player_name()
minetest.sound_play("click", {to_player=player_name, gain = 0.1}) minetest.sound_play("ui_click", {to_player=player_name, gain = 0.1})
ui.apply_filter(player, search_text, new_dir) ui.apply_filter(player, search_text, new_dir)
ui.current_searchbox[player_name] = search_text ui.current_searchbox[player_name] = search_text
ui.set_inventory_formspec(player, ui.current_page[player_name]) ui.set_inventory_formspec(player, ui.current_page[player_name])
end end
minetest.register_on_player_receive_fields(function(player, formname, fields) -- Search box handling
local function receive_fields_searchbox(player, formname, fields)
local player_name = player:get_player_name() local player_name = player:get_player_name()
local ui_peruser,draw_lite_mode = unified_inventory.get_per_player_formspec(player_name) -- always take new search text, even if not searching on it yet
if fields.searchbox and fields.searchbox ~= ui.current_searchbox[player_name] then
ui.current_searchbox[player_name] = fields.searchbox
end
if fields.searchbutton
or fields.key_enter_field == "searchbox" then
if ui.current_searchbox[player_name] ~= ui.activefilter[player_name] then
ui.apply_filter(player, ui.current_searchbox[player_name], "nochange")
ui.set_inventory_formspec(player, ui.current_page[player_name])
minetest.sound_play("paperflip2",
{to_player=player_name, gain = 1.0})
end
elseif fields.searchresetbutton then
if ui.activefilter[player_name] ~= "" then
apply_new_filter(player, "", "nochange")
end
end
end
minetest.register_on_player_receive_fields(function(player, formname, fields)
if formname ~= "" then if formname ~= "" then
return return
end end
-- always take new search text, even if not searching on it yet receive_fields_searchbox(player, formname, fields)
local dirty_search_filter = false
if fields.searchbox local player_name = player:get_player_name()
and fields.searchbox ~= unified_inventory.current_searchbox[player_name] then
unified_inventory.current_searchbox[player_name] = fields.searchbox
dirty_search_filter = true
end
local ui_peruser,draw_lite_mode = unified_inventory.get_per_player_formspec(player_name)
local clicked_category local clicked_category
for name, value in pairs(fields) do for name, value in pairs(fields) do
@ -114,7 +131,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
for i, def in pairs(unified_inventory.buttons) do for i, def in pairs(unified_inventory.buttons) do
if fields[def.name] then if fields[def.name] then
def.action(player) def.action(player)
minetest.sound_play("click", minetest.sound_play("ui_click",
{to_player=player_name, gain = 0.1}) {to_player=player_name, gain = 0.1})
return return
end end
@ -179,7 +196,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
end end
end end
if clicked_item then if clicked_item then
minetest.sound_play("click", minetest.sound_play("ui_click",
{to_player=player_name, gain = 0.1}) {to_player=player_name, gain = 0.1})
local page = unified_inventory.current_page[player_name] local page = unified_inventory.current_page[player_name]
local player_creative = unified_inventory.is_creative(player_name) local player_creative = unified_inventory.is_creative(player_name)
@ -201,25 +218,11 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
end end
end end
if fields.searchbutton
or fields.key_enter_field == "searchbox" then
if dirty_search_filter then
ui.apply_filter(player, ui.current_searchbox[player_name], "nochange")
ui.set_inventory_formspec(player, ui.current_page[player_name])
minetest.sound_play("paperflip2",
{to_player=player_name, gain = 1.0})
end
elseif fields.searchresetbutton then
if ui.activefilter[player_name] ~= "" then
apply_new_filter(player, "", "nochange")
end
end
-- alternate buttons -- alternate buttons
if not (fields.alternate or fields.alternate_prev) then if not (fields.alternate or fields.alternate_prev) then
return return
end end
minetest.sound_play("click", minetest.sound_play("ui_click",
{to_player=player_name, gain = 0.1}) {to_player=player_name, gain = 0.1})
local item_name = unified_inventory.current_item[player_name] local item_name = unified_inventory.current_item[player_name]
if not item_name then if not item_name then

View File

@ -24,7 +24,9 @@ Grouped by use-case, afterwards sorted alphabetically.
Callbacks Callbacks
--------- ---------
Register a callback that will be run whenever a craft is registered via unified_inventory.register_craft: Register a callback that will be run whenever a craft is registered via unified_inventory.register_craft.
This callback is run before any recipe ingredients checks, hence it is also executed on recipes that are
purged after all mods finished loading.
unified_inventory.register_on_craft_registered( unified_inventory.register_on_craft_registered(
function (item_name, options) function (item_name, options)

View File

@ -1,4 +1,5 @@
local S = minetest.get_translator("unified_inventory") local S = minetest.get_translator("unified_inventory")
local ui = unified_inventory
function unified_inventory.extract_groupnames(groupname) function unified_inventory.extract_groupnames(groupname)
local specname = ItemStack(groupname):get_name() local specname = ItemStack(groupname):get_name()
@ -26,6 +27,7 @@ end
-- It may be a comma-separated list of group names. This is really a -- It may be a comma-separated list of group names. This is really a
-- "group:..." ingredient specification, minus the "group:" prefix. -- "group:..." ingredient specification, minus the "group:" prefix.
-- TODO Replace this with the more efficient spec matcher (below)
local function compute_group_item(group_name_list) local function compute_group_item(group_name_list)
local group_names = group_name_list:split(",") local group_names = group_name_list:split(",")
local candidate_items = {} local candidate_items = {}
@ -84,3 +86,61 @@ function unified_inventory.get_group_item(group_name)
return group_item_cache[group_name] return group_item_cache[group_name]
end end
--[[
This is for filtering known items by groups
e.g. find all items that match "group:flower,yellow" (flower AND yellow groups)
]]
local spec_matcher = {}
function unified_inventory.init_matching_cache()
for _, name in ipairs(ui.items_list) do
-- we only need to care about groups, exact items are handled separately
for group, value in pairs(minetest.registered_items[name].groups) do
if value and value ~= 0 then
if not spec_matcher[group] then
spec_matcher[group] = {}
end
spec_matcher[group][name] = true
end
end
end
end
--[[
Retrieves all matching items
Arguments:
specname (string): Item name or group(s) to filter
Output:
{
matchingitem1 = true,
...
}
]]
function unified_inventory.get_matching_items(specname)
if specname:sub(1,6) ~= "group:" then
return { [specname] = true }
end
local accepted = {}
for i, group in ipairs(specname:sub(7):split(",")) do
if i == 1 then
-- First step: Copy all possible item names in this group
for name, _ in pairs(spec_matcher[group] or {}) do
accepted[name] = true
end
else
-- Perform filtering
if spec_matcher[group] then
for name, _ in pairs(accepted) do
accepted[name] = spec_matcher[group][name]
end
else
-- No matching items
return {}
end
end
end
return accepted
end

View File

@ -274,9 +274,11 @@ local function formspec_add_item_browser(player, formspec, ui_peruser)
end end
end end
end end
formspec[n] = string.format("label[%f,%f;%s: %s]", formspec[n] = "style[page_number;content_offset=0]"
ui_peruser.page_buttons_x + ui_peruser.btn_spc * (ui_peruser.is_lite_mode and 1 or 2), formspec[n + 1] = string.format("image_button[%f,%f;%f,0.4;;page_number;%s: %s;false;false;]",
ui_peruser.page_buttons_y + 0.1 + ui_peruser.btn_spc * 2, ui_peruser.page_buttons_x,
ui_peruser.page_buttons_y + ui_peruser.btn_spc * 2 - 0.1,
ui_peruser.btn_spc * (bn - 1) + ui_peruser.btn_size,
F(S("Page")), S("@1 of @2",page2,pagemax)) F(S("Page")), S("@1 of @2",page2,pagemax))
end end

View File

@ -126,25 +126,18 @@ Example output:
} }
--]] --]]
function unified_inventory.find_usable_items(inv_items, craft_items) function unified_inventory.find_usable_items(inv_items, craft_items)
local get_group = minetest.get_item_group
local result = {} local result = {}
for craft_item in pairs(craft_items) do for craft_item in pairs(craft_items) do
local group = craft_item:match("^group:(.+)") -- may specify group:type1,type2
local items = unified_inventory.get_matching_items(craft_item)
local found = {} local found = {}
for itemname, _ in pairs(items) do
if group ~= nil then if inv_items[itemname] then
for inv_item in pairs(inv_items) do found[itemname] = true
if get_group(inv_item, group) > 0 then
found[inv_item] = true
end end
end end
else
if inv_items[craft_item] ~= nil then
found[craft_item] = true
end
end
result[craft_item] = found result[craft_item] = found
end end

View File

@ -98,7 +98,7 @@ ui.register_button("misc_set_day", {
action = function(player) action = function(player)
local player_name = player:get_player_name() local player_name = player:get_player_name()
if minetest.check_player_privs(player_name, {settime=true}) then if minetest.check_player_privs(player_name, {settime=true}) then
minetest.sound_play("birds", minetest.sound_play("ui_morning",
{to_player=player_name, gain = 1.0}) {to_player=player_name, gain = 1.0})
minetest.set_timeofday((6000 % 24000) / 24000) minetest.set_timeofday((6000 % 24000) / 24000)
minetest.chat_send_player(player_name, minetest.chat_send_player(player_name,
@ -122,7 +122,7 @@ ui.register_button("misc_set_night", {
action = function(player) action = function(player)
local player_name = player:get_player_name() local player_name = player:get_player_name()
if minetest.check_player_privs(player_name, {settime=true}) then if minetest.check_player_privs(player_name, {settime=true}) then
minetest.sound_play("owl", minetest.sound_play("ui_owl",
{to_player=player_name, gain = 1.0}) {to_player=player_name, gain = 1.0})
minetest.set_timeofday((21000 % 24000) / 24000) minetest.set_timeofday((21000 % 24000) / 24000)
minetest.chat_send_player(player_name, minetest.chat_send_player(player_name,
@ -183,14 +183,14 @@ ui.register_page("craft", {
local n=#formspec+1 local n=#formspec+1
if ui.trash_enabled or ui.is_creative(player_name) or minetest.get_player_privs(player_name).give then if ui.trash_enabled or ui.is_creative(player_name) or minetest.get_player_privs(player_name).give then
formspec[n] = string.format("label[%f,%f;%s]", craftx + 6.45, crafty + 2.4, F(S("Trash:"))) formspec[n] = string.format("label[%f,%f;%s]", craftx + 6.35, crafty + 2.3, F(S("Trash:")))
formspec[n+1] = ui.make_trash_slot(craftx + 6.25, crafty + 2.5) formspec[n+1] = ui.make_trash_slot(craftx + 6.25, crafty + 2.5)
n=n + 2 n=n + 2
end end
if ui.is_creative(player_name) then if ui.is_creative(player_name) then
formspec[n] = ui.single_slot(craftx - 2.5, crafty + 2.5) formspec[n] = ui.single_slot(craftx - 2.5, crafty + 2.5)
formspec[n+1] = string.format("label[%f,%f;%s]", craftx - 2.3, crafty + 2.4,F(S("Refill:"))) formspec[n+1] = string.format("label[%f,%f;%s]", craftx - 2.4, crafty + 2.3, F(S("Refill:")))
formspec[n+2] = string.format("list[detached:%srefill;main;%f,%f;1,1;]", formspec[n+2] = string.format("list[detached:%srefill;main;%f,%f;1,1;]",
F(player_name), craftx - 2.5 + ui.list_img_offset, crafty + 2.5 + ui.list_img_offset) F(player_name), craftx - 2.5 + ui.list_img_offset, crafty + 2.5 + ui.list_img_offset)
end end

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.