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,
sunlight_propagates = true,
is_ground_content = false,
----- To be unbreakable -----
on_blast = function() end,
on_destruct = function () end,
can_dig = function() return false end,
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(),
})
@ -255,8 +259,12 @@ minetest.register_node("hyperloop:shaftA2", {
light_source = 2,
sunlight_propagates = true,
is_ground_content = false,
----- To be unbreakable -----
on_blast = function() end,
on_destruct = function () end,
can_dig = function() return false end,
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(),
})

View File

@ -173,8 +173,12 @@ minetest.register_node("hyperloop:tubeS2", {
light_source = 2,
sunlight_propagates = true,
is_ground_content = false,
----- To be unbreakable -----
on_blast = function() end,
on_destruct = function () end,
can_dig = function() return false end,
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(),
})
@ -263,8 +267,12 @@ minetest.register_node("hyperloop:tubeA2", {
light_source = 2,
sunlight_propagates = true,
is_ground_content = false,
----- To be unbreakable -----
on_blast = function() end,
on_destruct = function () end,
can_dig = function() return false end,
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(),
})

View File

@ -44,6 +44,7 @@ The mod features are:
- Ingame documentation (German and English), based on the mod "doc"
- API to register carts from other mods
- chat command '/mycart <num>' to output cart state and location
- Command interface for Techage (Lua and ICTA) and for Beduino Controllers
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
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
---------------
@ -153,3 +193,4 @@ History
Speed limit signs and cart terminal added
2021-09-02 V2.01 Chat command /stopcart added
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 inv = minetest.get_inventory({type="node", pos=npos})
if def and inv and def.put_listname then
return inv:add_item(def.put_listname, stack)
if def and inv and def.take_listname then
return inv:add_item(def.take_listname, stack)
elseif def and def.untake_item then
return def.untake_item(npos, stack)
else

View File

@ -3,7 +3,7 @@
Minecart
========
Copyright (C) 2019-2021 Joachim Stolberg
Copyright (C) 2019-2023 Joachim Stolberg
MIT
See license.txt for more information
@ -13,7 +13,7 @@
minecart = {}
-- 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.teleport_enabled = minetest.settings:get_bool("minecart_teleport_enabled") == true
@ -39,6 +39,7 @@ dofile(MP .. "/protection.lua")
dofile(MP .. "/signs.lua")
dofile(MP .. "/terminal.lua")
dofile(MP .. "/pusher.lua")
dofile(MP .. "/beduino.lua")
if minecart.hopper_enabled then
dofile(MP .. "/hopper.lua")

View File

@ -3,7 +3,7 @@
Minecart
========
Copyright (C) 2019-2021 Joachim Stolberg
Copyright (C) 2019-2023 Joachim Stolberg
MIT
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"
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 state, loc, name = get_cart_state_and_loc(owner, userID, query_pos)
local cart_type = minecart.tCartTypes[name] or "unknown"
@ -318,6 +333,10 @@ function minecart.cmnd_cart_location(name, userID, query_pos)
return loc
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)
local userIDs = {}
local carts = {}

View File

@ -3,7 +3,7 @@
Minecart
========
Copyright (C) 2019-2021 Joachim Stolberg
Copyright (C) 2019-2023 Joachim Stolberg
MIT
See license.txt for more information
@ -59,6 +59,11 @@ minetest.register_node("minecart:terminal", {
local meta = M(pos)
meta:set_string("owner", placer:get_player_name())
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)
end,
@ -88,3 +93,42 @@ minetest.register_craft({
{"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)
if sts == false then
sts, _ = minetest.spawn_falling_node(mem.robot_pos)
mem.stored_node = get_node_lvm(pos3)
minetest.swap_node(pos3, {name="air"})
if sts then
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
end
end

View File

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

View File

@ -89,6 +89,19 @@ Available worlds will be converted to 'lsqlite3', but there is no way back, so:
### 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**
- Change the way items are pushed
- 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",
recipe = {"techage:forceload"},
})
minetest.register_craft({
type = "shapeless",
output = "techage:forceload",
recipe = {"techage:forceloadtile"},
})
end
minetest.register_on_joinplayer(function(player)

View File

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

View File

@ -341,7 +341,7 @@ local tubing = {
CRD(pos).State:stop(pos, nvm)
config_item(pos, payload)
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)
CRD(pos).State:stop(pos, nvm)
set_limit(pos, nvm, payload[1])

View File

@ -196,6 +196,9 @@ local function quarry_task(pos, crd, nvm)
pos1.y = y_curr
pos2.y = y_curr
-- Restarting the server can detach the coroutine data.
-- Therefore, read nvm again.
nvm = techage.get_nvm(pos)
nvm.level = y_first - y_curr
if minetest.is_area_protected(pos1, pos2, owner, 5) then
@ -382,6 +385,11 @@ local tubing = {
end,
on_node_load = function(pos)
CRD(pos).State:on_node_load(pos)
local nvm = techage.get_nvm(pos)
if nvm.techage_state == techage.RUNNING then
stop_sound(pos)
play_sound(pos)
end
end,
}

View File

@ -346,6 +346,20 @@ local function count_number_of_chests(pos)
M(pos):set_int("stacksize", STACK_SIZE * cnt)
end
local function dummy_chest_behind(pos, node)
local dir = techage.side_to_outdir("B", node.param2)
local pos1 = tubelib2.get_pos(pos, dir)
node = techage.get_node_lvm(pos1)
return node.name == "techage:ta4_chest_dummy"
end
local function part_of_a_chain(pos, node)
local dir = techage.side_to_outdir("F", node.param2)
local pos1 = tubelib2.get_pos(pos, dir)
node = techage.get_node_lvm(pos1)
return node.name == "techage:ta4_chest_dummy" or node.name == "techage:ta4_chest"
end
local function search_chest_in_front(pos, node)
local dir = techage.side_to_outdir("F", node.param2)
local pos1 = tubelib2.get_pos(pos, dir)
@ -529,6 +543,10 @@ minetest.register_node("techage:ta4_chest", {
after_place_node = function(pos, placer)
local node = minetest.get_node(pos)
if dummy_chest_behind(pos, node) then
minetest.remove_node(pos)
return true
end
if search_chest_in_front(pos, node) then
node.name = "techage:ta4_chest_dummy"
minetest.swap_node(pos, node)
@ -667,6 +685,18 @@ techage.register_node({"techage:ta4_chest_dummy"}, {
end
})
minetest.register_lbm({
label = "Repair Dummy Chests",
name = "techage:chest_dummy",
nodenames = {"techage:ta4_chest_dummy"},
run_at_every_load = true,
action = function(pos, node)
if not part_of_a_chain(pos, node) then
minetest.swap_node(pos, {name = "techage:ta4_chest", param2 = node.param2})
end
end,
})
minetest.register_craft({
type = "shapeless",
output = "techage:ta4_chest",

View File

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

View File

@ -3,7 +3,7 @@
TechAge
=======
Copyright (C) 2019-2022 Joachim Stolberg
Copyright (C) 2019-2023 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
@ -520,6 +520,20 @@ function NodeStates:on_beduino_request_data(pos, topic, payload)
end
end
function NodeStates.get_beduino_state(pos)
local node = minetest.get_node(pos)
local nvm = techage.get_nvm(pos)
if node.name == "ignore" then -- unloaded node?
return 0, {techage.UNLOADED}
elseif nvm.techage_state == RUNNING then
local ttl = (nvm.last_active or 0) + MAX_CYCLE_TIME
if ttl < minetest.get_gametime() then
return 0, {techage.INACTIVE}
end
end
return 0, {nvm.techage_state or STOPPED}
end
-- restart timer
function NodeStates:on_node_load(pos)
local nvm = techage.get_nvm(pos)

View File

@ -55,6 +55,6 @@ minetest.register_on_mods_loaded(function()
beduino.lib.register_SystemHandler(0x140, ta_kv_init)
beduino.lib.register_SystemHandler(0x141, ta_kv_add)
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)

View File

@ -30,7 +30,7 @@ local TOTAL_MAX = INV_SIZE * FUEL_STACK_MAX
local function count_coal(metadata)
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()
end
return total

View File

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

View File

@ -113,7 +113,7 @@ techage.manual_DE.aTitel = {
"3,TA3 Sequenzer / Sequencer",
"3,TA3 Timer",
"3,TA3 Terminal",
"3,TechAge Signallampe / Signal Lamp",
"3,TechAge Farblampe / Color Lamp",
"3,Tür/Tor Blöcke / Door/Gate Blocks",
"3,TA3 Tür Controller / Door Controller",
"3,TA3 Tür Controller II / Door Controller II",
@ -1133,7 +1133,7 @@ techage.manual_DE.aText = {
"\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"..
"\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_timer",
"ta3_terminal",
"ta3_signallamp",
"ta3_colorlamp",
"ta3_doorblock",
"ta3_doorcontroller",
"ta3_doorcontroller",

View File

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

View File

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

View File

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

View File

@ -3,7 +3,7 @@
TechAge
=======
Copyright (C) 2019-2022 Joachim Stolberg
Copyright (C) 2019-2023 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
@ -13,7 +13,7 @@
techage = {}
-- Version for compatibility checks, see readme.md/history
techage.version = 1.08
techage.version = 1.10
if minetest.global_exists("tubelib") then
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
minetest.log("error", "[techage] Techage requires tubelib2 version 2.2 or newer!")
return
elseif minetest.global_exists("minecart") and minecart.version < 1.08 then
minetest.log("error", "[techage] Techage requires minecart version 1.08 or newer!")
elseif minetest.global_exists("minecart") and minecart.version < 2.03 then
minetest.log("error", "[techage] Techage requires minecart version 2.03 or newer!")
return
elseif minetest.global_exists("lcdlib") and lcdlib.version < 1.01 then
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(),
})
-- 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="bakedclay:white", output="techage:cement_powder"})
end

View File

@ -30,7 +30,7 @@ if minetest.global_exists("mesecon") then
},
time = 6,
})
else
end
techage.furnace.register_recipe({
output = "techage:ta4_silicon_wafer 16",
recipe = {
@ -41,5 +41,5 @@ else
},
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)
if contains(Flowers, name) then
return
end
Flowers[#Flowers+1] = name
end
function techage.register_plant(name)
if contains(Plants, name) then
return
end
Plants[name] = true
end
@ -201,4 +216,5 @@ minetest.after(1, function()
end
end
end
-- print(dump(Flowers))
end)

View File

@ -343,7 +343,7 @@ techage.register_node({"techage:t4_pump", "techage:t4_pump_on"}, {
end
end,
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)
State4:stop(pos, nvm)
if payload[1] > 0 then

View File

@ -3,7 +3,7 @@
TechAge
=======
Copyright (C) 2017-2020 Joachim Stolberg
Copyright (C) 2017-2023 Joachim Stolberg
AGPL v3
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:ta4_button_off", "techage:ta4_button_on",
}, {
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)
return mem.clicker or ""
elseif topic == "time" then
@ -409,7 +414,10 @@ techage.register_node({
end
end,
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)
return 0, mem.clicker
elseif topic == 149 then --time

View File

@ -244,6 +244,8 @@ minetest.register_node("techage:ta4_button_2x", {
sounds = default.node_sound_glass_defaults(),
})
techage.register_node({"techage:ta4_button_2x"}, {})
minetest.register_craft({
output = "techage:ta4_button_2x",
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(),
})
techage.register_node({"techage:ta4_button_4x"}, {})
minetest.register_craft({
output = "techage:ta4_button_4x",
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
=======
Copyright (C) 2017-2022 Joachim Stolberg
Copyright (C) 2017-2023 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
@ -21,7 +21,7 @@ local logic = techage.logic
local BLOCKING_TIME = 8 -- seconds
local ON_TIME = 1
local WRENCH_MENU = {
local WRENCH_MENU3 = {
{
type = "dropdown",
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 mem = techage.get_mem(pos)
local t = minetest.get_gametime()
@ -122,6 +162,17 @@ local function after_dig_node(pos, oldnode, oldmetadata, digger)
techage.del_mem(pos)
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", {
description = S("TA3 Detector"),
@ -139,7 +190,7 @@ minetest.register_node("techage:ta3_detector_off", {
on_receive_fields = on_receive_fields,
techage_set_numbers = techage_set_numbers,
after_dig_node = after_dig_node,
ta3_formspec = WRENCH_MENU,
ta3_formspec = WRENCH_MENU3,
on_rotate = screwdriver.disallow,
paramtype = "light",
@ -167,7 +218,7 @@ minetest.register_node("techage:ta3_detector_on", {
on_rotate = screwdriver.disallow,
techage_set_numbers = techage_set_numbers,
after_dig_node = after_dig_node,
ta3_formspec = WRENCH_MENU,
ta3_formspec = WRENCH_MENU3,
paramtype2 = "facedir",
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,
techage_set_numbers = techage_set_numbers,
after_dig_node = after_dig_node,
ta3_formspec = WRENCH_MENU,
ta4_formspec = WRENCH_MENU4,
ta_after_formspec = ta_after_formspec,
on_rotate = screwdriver.disallow,
paramtype = "light",
@ -220,7 +272,8 @@ minetest.register_node("techage:ta4_detector_on", {
on_rotate = screwdriver.disallow,
techage_set_numbers = techage_set_numbers,
after_dig_node = after_dig_node,
ta3_formspec = WRENCH_MENU,
ta4_formspec = WRENCH_MENU4,
ta_after_formspec = ta_after_formspec,
paramtype2 = "facedir",
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
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
switch_on(pos)
local nvm = techage.get_nvm(pos)
if leftover == true then
nvm.counter = (nvm.counter or 0) + stack:get_count()
else
nvm.counter = (nvm.counter or 0) + stack:get_count() - leftover:get_count()
local num_moved = stack:get_count()
if leftover ~= true then
num_moved = num_moved - leftover:get_count()
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
return leftover
end
@ -286,9 +348,15 @@ techage.register_node({"techage:ta4_detector_off", "techage:ta4_detector_on"}, {
if topic == "count" then
local nvm = techage.get_nvm(pos)
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
local nvm = techage.get_nvm(pos)
nvm.counter = 0
nvm.countdown = nil
return true
else
return "unsupported"
@ -298,6 +366,12 @@ techage.register_node({"techage:ta4_detector_off", "techage:ta4_detector_on"}, {
if topic == 6 then -- Detector Block Reset
local nvm = techage.get_nvm(pos)
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
else
return 2

View File

@ -3,7 +3,7 @@
TechAge
=======
Copyright (C) 2022 Joachim Stolberg
Copyright (C) 2022-2023 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
@ -104,7 +104,6 @@ local function register_signallamp(name, description, tiles_off, tiles_on, node_
paramtype = "light",
paramtype2 = "color",
--palette = "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},
@ -148,7 +147,7 @@ local function register_signallamp(name, description, tiles_off, tiles_on, node_
local node = techage.get_node_lvm(pos)
switch_off(pos, node)
return 0
elseif topic == 70 then
elseif topic == 70 or topic == 22 then
local node = techage.get_node_lvm(pos)
switch_on(pos, node, nil, payload[1])
return 0
@ -196,8 +195,8 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
end)
register_signallamp("techage:signal_lamp",
S("TechAge Signal Lamp"),
register_signallamp("techage:color_lamp",
S("TechAge Color Lamp"),
{"techage_signal_lamp.png^[colorize:#000000:80"},
{"techage_signal_lamp.png"},
{
@ -209,8 +208,8 @@ register_signallamp("techage:signal_lamp",
}
)
register_signallamp("techage:signal_lamp2",
S("TechAge Signal Lamp 2 "),
register_signallamp("techage:color_lamp2",
S("TechAge Color Lamp 2"),
{"techage_signallamp2.png^[colorize:#000000:80"},
{"techage_signallamp2.png"}
)
@ -232,3 +231,8 @@ minetest.register_craft({
{"", "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]
### 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.
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

View File

@ -627,13 +627,13 @@ In public mode, all players can use the preconfigured keys.
[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.
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

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: "red", "amber", "green", "off" | Signal Tower state |
| "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 |
| "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") |
@ -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 |
| "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 |
| "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 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` |
@ -417,7 +419,7 @@ Please note, that this is not a technical distinction, only a logical.
| "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) |
| "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

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:
```c
import "ta_kvstore.c"
import "ta_iom.c"
import "lib/ta_kvstore.c"
import "lib/ta_iom.c"
var s[16];

View File

@ -112,7 +112,7 @@
- [TA3 Sequenzer / Sequencer](./manual_ta3_DE.md#ta3-sequenzer--sequencer)
- [TA3 Timer](./manual_ta3_DE.md#ta3-timer)
- [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)
- [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)

View File

@ -112,7 +112,7 @@
- [TA3 Sequencer](./manual_ta3_EN.md#ta3-sequencer)
- [TA3 Timer](./manual_ta3_EN.md#ta3-timer)
- [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)
- [TA3 Door Controller](./manual_ta3_EN.md#ta3-door-controller)
- [TA3 Door Controller II](./manual_ta3_EN.md#ta3-door-controller-ii)

View File

@ -3,12 +3,12 @@
TechAge
=======
Copyright (C) 2020-2022 Joachim Stolberg
Copyright (C) 2020-2023 Joachim Stolberg
AGPL v3
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"),
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)
@ -165,8 +172,6 @@ minetest.register_node("techage:ta5_flycontroller", {
elseif fields.moveAB then
meta:set_string("status", "")
if fly.move_to_other_pos(pos, false) then
nvm.moveBA = true
nvm.running = true
meta:set_string("formspec", formspec(nvm, meta))
local name = player:get_player_name()
mark.stop(name)
@ -175,8 +180,6 @@ minetest.register_node("techage:ta5_flycontroller", {
elseif fields.moveBA then
meta:set_string("status", "")
if fly.move_to_other_pos(pos, true) then
nvm.moveBA = false
nvm.running = true
meta:set_string("formspec", formspec(nvm, meta))
local name = player:get_player_name()
mark.stop(name)
@ -185,8 +188,6 @@ minetest.register_node("techage:ta5_flycontroller", {
elseif fields.move then
meta:set_string("status", "")
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))
local name = player:get_player_name()
mark.stop(name)
@ -219,17 +220,11 @@ techage.register_node({"techage:ta5_flycontroller"}, {
elseif topic == "state" then
return nvm.running and "running" or "stopped"
elseif topic == "a2b" then
nvm.moveBA = true
nvm.running = true
return fly.move_to_other_pos(pos, false)
elseif topic == "b2a" then
nvm.moveBA = false
nvm.running = true
return fly.move_to_other_pos(pos, true)
elseif topic == "move" then
nvm.moveBA = nvm.moveBA == false
nvm.running = true
return fly.move_to_other_pos(pos, nvm.moveBA == false)
return fly.move_to_other_pos(pos, nvm.moveBA)
end
return false
end,
@ -237,17 +232,11 @@ techage.register_node({"techage:ta5_flycontroller"}, {
local nvm = techage.get_nvm(pos)
if topic == 11 then
if payload[1] == 1 then
nvm.moveBA = true
nvm.running = true
return fly.move_to_other_pos(pos, false) and 0 or 3
elseif payload[1] == 2 then
nvm.moveBA = false
nvm.running = true
return fly.move_to_other_pos(pos, true) and 0 or 3
elseif payload[1] == 3 then
nvm.moveBA = nvm.moveBA == false
nvm.running = true
return fly.move_to_other_pos(pos, nvm.moveBA == false) and 0 or 3
return fly.move_to_other_pos(pos, nvm.moveBA) and 0 or 3
end
else
return 2
@ -260,6 +249,10 @@ techage.register_node({"techage:ta5_flycontroller"}, {
end
return 2, ""
end,
on_node_load = function(pos, node)
local nvm = techage.get_nvm(pos)
nvm.running = false
end,
})
minetest.register_craft({

View File

@ -34,22 +34,6 @@ local WRENCH_MENU = {
tooltip = S("Maximum speed for moving blocks"),
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",
name = "height",

View File

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

View File

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

View File

@ -222,13 +222,29 @@ local function on_place(itemstack, placer, pointed_thing)
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", {
description = S("TechAge Repair Kit"),
inventory_image = "techage_repairkit.png",
wield_image = "techage_repairkit.png^[transformR270",
groups = {cracky=1, book=1},
--on_use = repair,
--on_place = repair,
on_use = repair,
on_place = repair,
node_placement_prediction = "",
stack_max = 1,
})
@ -254,3 +270,12 @@ minetest.register_craft({
{"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)
* 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
-- Step 1: group-indexed lookup table for items
local spec_matcher = {}
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 1: Initialize cache for looking up groups
unified_inventory.init_matching_cache()
-- Step 2: Find all matching items for the given spec (groups)
local function get_matching_spec_items(specname)
if specname:sub(1,6) ~= "group:" then
return { [specname] = true }
end
local get_matching_spec_items = unified_inventory.get_matching_items
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
for _, recipes in pairs(ui.crafts_for.recipe) do
for outputitemname, recipes in pairs(ui.crafts_for.recipe) do
-- 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
local ingredient_items = {}
for _, spec in pairs(recipe.items) do
@ -204,8 +172,15 @@ minetest.after(0.01, function()
end
table.insert(ui.crafts_for.usage[name], recipe)
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
ui.crafts_for.recipe[outputitemname] = new_recipe_list
end
for _, callback in ipairs(ui.initialized_callbacks) do
callback()

View File

@ -10,25 +10,26 @@ local F = minetest.formspec_escape
local ui = unified_inventory
ui.register_page("bags", {
get_formspec = function(player)
get_formspec = function(player, perplayer_formspec)
local player_name = player:get_player_name()
return { formspec = table.concat({
ui.style_full.standard_inv_bg,
ui.single_slot(0.925, 1.5),
ui.single_slot(3.425, 1.5),
ui.single_slot(5.925, 1.5),
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)) .. "]",
local std_inv_x = perplayer_formspec.std_inv_x
local formspec = {
perplayer_formspec.standard_inv_bg,
"label[", perplayer_formspec.form_header_x, ",",
perplayer_formspec.form_header_y, ";", F(S("Bags")), "]",
"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;]",
"list[detached:" .. F(player_name) .. "_bags;bag4;8.575,1.65;1,1;]"
}) }
}
for i = 1, 4 do
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,
})
@ -36,7 +37,6 @@ ui.register_button("bags", {
type = "image",
image = "ui_bags_icon.png",
tooltip = S("Bags"),
hide_lite=true
})
local function get_player_bag_stack(player, i)
@ -48,23 +48,38 @@ end
for bag_i = 1, 4 do
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 image = stack:get_definition().inventory_image
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 = {
ui.style_full.standard_inv_bg,
ui.make_inv_img_grid(0.3, 1.5, 8, slots/8),
"image[9.2,0.4;1,1;" .. image .. "]",
"label[0.3,0.65;" .. F(S("Bag @1", bag_i)) .. "]",
perplayer_formspec.standard_inv_bg,
ui.make_inv_img_grid(std_inv_x, bag_inv_y, 8, slots/8),
"label[", header_x, ",", header_y, ";", F(S("Bag @1", bag_i)), "]",
"listcolors[#00000000;#00000000]",
"listring[current_player;main]",
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),
"listring[current_name;bag" .. bag_i .. "contents]",
bag_i, std_inv_x + ui.list_img_offset, bag_inv_y + ui.list_img_offset),
"listring[current_name;bag", bag_i, "contents]",
}
if lite_mode then
return { formspec = table.concat(formspec) }
end
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.
if ui.trash_enabled

View File

@ -57,30 +57,47 @@ end)
local function apply_new_filter(player, search_text, new_dir)
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.current_searchbox[player_name] = search_text
ui.set_inventory_formspec(player, ui.current_page[player_name])
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 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
return
end
-- always take new search text, even if not searching on it yet
local dirty_search_filter = false
receive_fields_searchbox(player, formname, fields)
if fields.searchbox
and fields.searchbox ~= unified_inventory.current_searchbox[player_name] then
unified_inventory.current_searchbox[player_name] = fields.searchbox
dirty_search_filter = true
end
local player_name = player:get_player_name()
local ui_peruser,draw_lite_mode = unified_inventory.get_per_player_formspec(player_name)
local clicked_category
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
if fields[def.name] then
def.action(player)
minetest.sound_play("click",
minetest.sound_play("ui_click",
{to_player=player_name, gain = 0.1})
return
end
@ -179,7 +196,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
end
end
if clicked_item then
minetest.sound_play("click",
minetest.sound_play("ui_click",
{to_player=player_name, gain = 0.1})
local page = unified_inventory.current_page[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
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
if not (fields.alternate or fields.alternate_prev) then
return
end
minetest.sound_play("click",
minetest.sound_play("ui_click",
{to_player=player_name, gain = 0.1})
local item_name = unified_inventory.current_item[player_name]
if not item_name then

View File

@ -24,7 +24,9 @@ Grouped by use-case, afterwards sorted alphabetically.
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(
function (item_name, options)

View File

@ -1,4 +1,5 @@
local S = minetest.get_translator("unified_inventory")
local ui = unified_inventory
function unified_inventory.extract_groupnames(groupname)
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
-- "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 group_names = group_name_list:split(",")
local candidate_items = {}
@ -84,3 +86,61 @@ function unified_inventory.get_group_item(group_name)
return group_item_cache[group_name]
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
formspec[n] = string.format("label[%f,%f;%s: %s]",
ui_peruser.page_buttons_x + ui_peruser.btn_spc * (ui_peruser.is_lite_mode and 1 or 2),
ui_peruser.page_buttons_y + 0.1 + ui_peruser.btn_spc * 2,
formspec[n] = "style[page_number;content_offset=0]"
formspec[n + 1] = string.format("image_button[%f,%f;%f,0.4;;page_number;%s: %s;false;false;]",
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))
end

View File

@ -126,25 +126,18 @@ Example output:
}
--]]
function unified_inventory.find_usable_items(inv_items, craft_items)
local get_group = minetest.get_item_group
local result = {}
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 = {}
if group ~= nil then
for inv_item in pairs(inv_items) do
if get_group(inv_item, group) > 0 then
found[inv_item] = true
for itemname, _ in pairs(items) do
if inv_items[itemname] then
found[itemname] = true
end
end
else
if inv_items[craft_item] ~= nil then
found[craft_item] = true
end
end
result[craft_item] = found
end

View File

@ -98,7 +98,7 @@ ui.register_button("misc_set_day", {
action = function(player)
local player_name = player:get_player_name()
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})
minetest.set_timeofday((6000 % 24000) / 24000)
minetest.chat_send_player(player_name,
@ -122,7 +122,7 @@ ui.register_button("misc_set_night", {
action = function(player)
local player_name = player:get_player_name()
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})
minetest.set_timeofday((21000 % 24000) / 24000)
minetest.chat_send_player(player_name,
@ -183,14 +183,14 @@ ui.register_page("craft", {
local n=#formspec+1
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)
n=n + 2
end
if ui.is_creative(player_name) then
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;]",
F(player_name), craftx - 2.5 + ui.list_img_offset, crafty + 2.5 + ui.list_img_offset)
end

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.