built on 11/12/2020 18:07:29

This commit is contained in:
Joachim Stolberg 2020-12-11 18:07:29 +01:00
parent 98d9e1b7cf
commit 67ba0cde5b
87 changed files with 3820 additions and 1580 deletions

View File

@ -92,24 +92,24 @@ minetest.register_on_respawnplayer(function(player)
end
end)
local function control_player(player)
local player_name = player:get_player_name()
local function control_player(player_name)
if Currently_left_the_game[player_name] then
Currently_left_the_game[player_name] = nil
return
end
local player = minetest.get_player_by_name(player_name)
if player then
local pos = player:get_pos()
if pos then
--pos.y = math.floor(pos.y)
local node = minetest.get_node(pos)
if string.sub(node.name,1,13) == "autobahn:node" then
minetest.after(0.5, control_player, player)
minetest.after(0.5, control_player, player_name)
else
pos.y = pos.y - 1
node = minetest.get_node(pos)
if string.sub(node.name,1,13) == "autobahn:node" then
minetest.after(0.5, control_player, player)
minetest.after(0.5, control_player, player_name)
else
reset_player_privs(player)
end
@ -125,11 +125,13 @@ local NodeTbl1 = {
["autobahn:node3"] = true,
["autobahn:node4"] = true,
["autobahn:node5"] = true,
["autobahn:node6"] = true,
["autobahn:node12"] = true,
["autobahn:node22"] = true,
["autobahn:node32"] = true,
["autobahn:node42"] = true,
["autobahn:node52"] = true,
["autobahn:node62"] = true,
}
local NodeTbl2 = {
["autobahn:node11"] = true,
@ -137,6 +139,7 @@ local NodeTbl2 = {
["autobahn:node31"] = true,
["autobahn:node41"] = true,
["autobahn:node51"] = true,
["autobahn:node61"] = true,
}
local NodeTbl3 = {
["autobahn:node1"] = true,
@ -144,6 +147,7 @@ local NodeTbl3 = {
["autobahn:node3"] = true,
["autobahn:node4"] = true,
["autobahn:node5"] = true,
["autobahn:node6"] = true,
}
-- 1) _o_
@ -227,7 +231,9 @@ local function register_node(name, tiles, drawtype, mesh, box, drop)
sunlight_propagates = true,
sounds = default.node_sound_stone_defaults(),
is_ground_content = false,
groups = {cracky=2, crumbly=2, not_in_creative_inventory=(mesh==nil) and 0 or 1},
groups = {cracky=2, crumbly=2,
fall_damage_add_percent = -80,
not_in_creative_inventory=(mesh==nil) and 0 or 1},
drop = "autobahn:"..drop,
after_place_node = function(pos, placer, itemstack, pointed_thing)
@ -239,7 +245,8 @@ local function register_node(name, tiles, drawtype, mesh, box, drop)
reset_player_privs(clicker)
else
set_player_privs(clicker)
minetest.after(0.5, control_player, clicker)
local player_name = clicker:get_player_name()
minetest.after(0.5, control_player, player_name)
end
end,
})
@ -277,12 +284,14 @@ local Nodes = {
{name="node31", tiles={"autobahn3.png","autobahn1.png"}, drawtype="mesh", mesh="autobahn_ramp1.obj", box=sb1, drop="node3"},
{name="node41", tiles={"autobahn2.png^[transformR180]","autobahn1.png"}, drawtype="mesh", mesh="autobahn_ramp1.obj", box=sb1, drop="node4"},
{name="node51", tiles={"autobahn4.png^[transformR90]","autobahn1.png"}, drawtype="mesh", mesh="autobahn_ramp1.obj", box=sb1, drop="node5"},
{name="node61", tiles={"autobahn5.png^[transformR90]","autobahn1.png"}, drawtype="mesh", mesh="autobahn_ramp1.obj", box=sb1, drop="node6"},
{name="node12", tiles={"autobahn1.png"}, drawtype="mesh", mesh="autobahn_ramp2.obj", box=sb2, drop="node1"},
{name="node22", tiles={"autobahn2.png","autobahn1.png"}, drawtype="mesh", mesh="autobahn_ramp2.obj", box=sb2, drop="node2"},
{name="node32", tiles={"autobahn3.png","autobahn1.png"}, drawtype="mesh", mesh="autobahn_ramp2.obj", box=sb2, drop="node3"},
{name="node42", tiles={"autobahn2.png^[transformR180]","autobahn1.png"}, drawtype="mesh", mesh="autobahn_ramp2.obj", box=sb2, drop="node4"},
{name="node52", tiles={"autobahn4.png^[transformR90]","autobahn1.png"}, drawtype="mesh", mesh="autobahn_ramp2.obj", box=sb2, drop="node5"},
{name="node62", tiles={"autobahn5.png^[transformR90]","autobahn1.png"}, drawtype="mesh", mesh="autobahn_ramp2.obj", box=sb2, drop="node6"},
}
for _,item in ipairs(Nodes) do
@ -405,10 +414,14 @@ if minetest.global_exists("minecart") then
minecart.register_protected_node("autobahn:node21")
minecart.register_protected_node("autobahn:node31")
minecart.register_protected_node("autobahn:node41")
minecart.register_protected_node("autobahn:node51")
minecart.register_protected_node("autobahn:node61")
minecart.register_protected_node("autobahn:node12")
minecart.register_protected_node("autobahn:node22")
minecart.register_protected_node("autobahn:node32")
minecart.register_protected_node("autobahn:node42")
minecart.register_protected_node("autobahn:node52")
minecart.register_protected_node("autobahn:node62")
end

View File

@ -1,9 +1,9 @@
# textdomain: basic_materials
Silicon lump=Silikonklumpen
Simple Integrated Circuit=einfacher Integrierter Schaltkreis
Simple Motor=einfacher Motor
Silicon lump=Siliziumklumpen
Simple Integrated Circuit=Einfacher Integrierter Schaltkreis
Simple Motor=Einfacher Motor
Heating element=Heizelement
Simple energy crystal=einfacher Energiekristall
Simple energy crystal=Einfacher Energiekristall
Spool of steel wire=Spule mit Stahldraht
Spool of copper wire=Spule mit Kupferdraht
@ -12,22 +12,22 @@ Spool of gold wire=Spule mit Golddraht
Steel Strip=Stahlstreifen
Copper Strip=Kupferstreifen
Steel Bar=Stahlstab
Chainlinks (brass)=Messing-Kettenglieder
Chainlinks (steel)=Stahl-Kettenglieder
Chainlinks (brass)=Messingkettenglieder
Chainlinks (steel)=Stahlkettenglieder
Brass Ingot=Messingbarren
Steel gear=Stahlzahnrad
Padlock=Vorhängeschloss
Chain (steel, hanging)=Stahlkette
Chain (brass, hanging)=Messingkette
Chain (steel, hanging)=Hängende Stahlkette
Chain (brass, hanging)=Hängende Messingkette
Brass Block=Messingblock
Oil extract=raffiniertes Öl
Unprocessed paraffin=unbearbeitetes Paraffin
Uncooked Terracotta Base=ungebranntes Terrakotta
Wet Cement=nasser Zement
Oil extract=Ölextrakt
Unprocessed paraffin=Unverarbeitetes Paraffin
Uncooked Terracotta Base=Ungebranntes Terrakotta
Wet Cement=Nasser Zement
Cement=Zement
Concrete Block=Betonblock
Plastic sheet=Kunststoffplatte
Plastic strips=Kunststoffstreifen
Empty wire spool=leere Drahtspule
Empty wire spool=Leere Drahtspule

View File

@ -117,4 +117,4 @@ History
2020-06-27 v1.07 Route storage and cart command bugfixes
2020-07-24 V1.08 Adapted to new techage ICTA style
2020-08-14 V1.09 Hopper support for digtron, protector:chest and default:furnace added
2020-11-12 V1.10 Make carts more robust against server lag

View File

@ -53,8 +53,18 @@ local function on_punch(pos, node, puncher)
if not minecart.teleport_enabled then return end
local route = minecart.get_route(P2S(pos))
if route and route.dest_pos and puncher and puncher:is_player() then
-- only teleport if the user is not pressing shift
if not puncher:get_player_control()['sneak'] then
puncher:set_pos(S2P(route.dest_pos))
local playername = puncher:get_player_name()
local pos = S2P(route.dest_pos)
local teleport = function()
-- Make sure the player object still exists
local player = minetest.get_player_by_name(playername)
if player and pos then player:set_pos(pos) end
end
minetest.after(0.25, teleport)
end
end
end

View File

@ -17,6 +17,12 @@
-- 2) Only the owner can start the recording
-- 3) But any player can act as cargo, cart punched by owner or buffer
local SLOPE_ACCELERATION = 3
local MAX_SPEED = 7
local PUNCH_SPEED = 3
local SLOWDOWN = 0.4
local RAILTYPE = minetest.get_item_group("carts:rail", "connect_to_raillike")
local Y_OFFS_ON_SLOPES = 0.5
-- for lazy programmers
local M = minetest.get_meta
@ -24,6 +30,83 @@ local S = minecart.S
local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end
local S2P = minetest.string_to_pos
local MP = minetest.get_modpath("minecart")
local D = function(pos) return minetest.pos_to_string(vector.round(pos)) end
local tRails = {
["carts:rail"] = true,
["carts:powerrail"] = true,
["carts:brakerail"] = true,
}
local lRails = {"carts:rail", "carts:powerrail", "carts:brakerail"}
local function get_rail_node(pos)
local rail_pos = vector.round(pos)
local node = minecart.get_node_lvm(rail_pos)
if tRails[node.name] then
return rail_pos, node
end
end
local function find_rail_node(pos)
local rail_pos = vector.round(pos)
local node = get_rail_node(rail_pos)
if node then
return rail_pos, node
end
local pos1 = {x=rail_pos.x-1, y=rail_pos.y-1, z=rail_pos.z-1}
local pos2 = {x=rail_pos.x+1, y=rail_pos.y+1, z=rail_pos.z+1}
for _,pos3 in ipairs(minetest.find_nodes_in_area(pos1, pos2, lRails)) do
--print("invalid position1", D(pos), D(pos3))
return pos3, minecart.get_node_lvm(pos3)
end
--print("invalid position2", D(pos))
end
local function get_pitch(dir)
local pitch = 0
if dir.y == -1 then
pitch = -math.pi/4
elseif dir.y == 1 then
pitch = math.pi/4
end
return pitch * (dir.z == 0 and -1 or 1)
end
local function get_yaw(dir)
local yaw = 0
if dir.x < 0 then
yaw = math.pi/2*3
elseif dir.x > 0 then
yaw = math.pi/2
elseif dir.z < 0 then
yaw = math.pi
end
return yaw
end
local function push_cart(self, pos, punch_dir, puncher)
local vel = self.object:get_velocity()
punch_dir = punch_dir or carts:velocity_to_dir(puncher:get_look_dir())
punch_dir.y = 0
local cart_dir = carts:get_rail_direction(pos, punch_dir, nil, nil, RAILTYPE)
-- Always start in horizontal direction
cart_dir.y = 0
if vector.equals(cart_dir, {x=0, y=0, z=0}) then return end
local speed = vector.multiply(cart_dir, PUNCH_SPEED)
local new_vel = vector.add(vel, speed)
local yaw = get_yaw(cart_dir)
local pitch = get_pitch(cart_dir)
self.object:set_rotation({x = pitch, y = yaw, z = 0})
self.object:set_velocity(new_vel)
self.old_pos = vector.round(pos)
self.stopped = false
end
local api = {}
@ -94,31 +177,20 @@ function api:on_punch(puncher, time_from_last_punch, tool_capabilities, directio
return
end
-- running carts can't be punched or removed from external
if not stopped then
return
end
-- Punched by non-authorized player
if puncher_name and not puncher_is_owner then
minetest.chat_send_player(puncher_name, S("[minecart] Cart is protected by ")..(self.owner or ""))
return
end
if not self.railtype then
local node = minetest.get_node(pos).name
self.railtype = minetest.get_item_group(node, "connect_to_raillike")
end
-- Punched by non-player
if not puncher_name then
local cart_dir = carts:get_rail_direction(pos, direction, nil, nil, self.railtype)
local cart_dir = carts:get_rail_direction(pos, direction, nil, nil, RAILTYPE)
if vector.equals(cart_dir, {x=0, y=0, z=0}) then
return
end
self.velocity = vector.multiply(cart_dir, 2)
self.punched = true
api.load_cargo(self, pos)
push_cart(self, pos, cart_dir)
minecart.start_cart(pos, self.myID)
return
end
@ -131,7 +203,7 @@ function api:on_punch(puncher, time_from_last_punch, tool_capabilities, directio
end
-- detach driver
if self.driver then
carts:manage_attachment(puncher_name, nil)
carts:manage_attachment(puncher, nil)
end
-- Pick up cart
api.remove_cart(self, pos, puncher)
@ -147,23 +219,7 @@ function api:on_punch(puncher, time_from_last_punch, tool_capabilities, directio
api.load_cargo(self, pos)
-- Normal punch by owner to start the cart
local punch_dir = carts:velocity_to_dir(puncher:get_look_dir())
punch_dir.y = 0
local cart_dir = carts:get_rail_direction(pos, punch_dir, nil, nil, self.railtype)
if vector.equals(cart_dir, {x=0, y=0, z=0}) then
return
end
self.velocity = vector.multiply(cart_dir, 2)
self.old_dir = cart_dir
self.punched = true
end
local function rail_on_step_event(handler, obj, dtime)
if handler then
handler(obj, dtime)
end
push_cart(self, pos, nil, puncher)
end
-- sound refresh interval = 1.0sec
@ -193,224 +249,149 @@ local function rail_sound(self, dtime)
end
end
local function get_railparams(pos)
local node = minetest.get_node(pos)
return carts.railparams[node.name] or {}
end
local v3_len = vector.length
local function rail_on_step(self, dtime)
local vel = self.object:get_velocity()
local function rail_on_step(self)
-- Check if same position as before
local pos = self.object:get_pos()
local rot = self.object:get_rotation()
local stopped = minecart.stopped(vel) and rot.x == 0
local on_slope = rot.x ~= 0
--print("rail_on_step_new", P2S(pos), rot.x)
-- cart position correction on slopes
if on_slope then
pos.y = pos.y - Y_OFFS_ON_SLOPES
end
-- Used as fallback position
self.old_pos = self.old_pos or pos
local pos_rounded = vector.round(pos)
-- Same pos as before
if vector.equals(pos_rounded, self.old_pos) then
return -- nothing todo
end
-- Check if stopped
local vel = self.object:get_velocity()
local stopped = not on_slope and minecart.stopped(vel)
local is_minecart = self.node_name == nil
local recording = is_minecart and self.driver == self.owner
-- cart position correction on slopes
if rot.x ~= 0 then
pos.y = pos.y - 0.5
if stopped then
if not self.stopped then
local param2 = minetest.dir_to_facedir(self.old_dir)
api.stop_cart(pos, self, self.node_name or "minecart:cart", param2)
if recording then
minecart.stop_recording(self, pos_rounded, vel, self.driver)
end
api.unload_cargo(self, pos)
self.stopped = true
end
self.old_pos = pos_rounded
return -- nothing todo
end
if self.punched then
vel = vector.add(vel, self.velocity)
self.object:set_velocity(vel)
self.old_dir.y = 0
self.stopped = false
elseif stopped and not self.stopped then
local param2 = minetest.dir_to_facedir(self.old_dir)
api.stop_cart(pos, self, self.node_name or "minecart:cart", param2)
if recording then
minecart.stop_recording(self, pos, vel, self.driver)
-- Check if invalid position (not on rail anymore)
local rail_pos, node = get_rail_node(pos)
if not node then
rail_pos, node = find_rail_node(self.old_pos)
if rail_pos then
pos_rounded = rail_pos
if on_slope then
self.object:set_pos({x=rail_pos.x, y=rail_pos.y + Y_OFFS_ON_SLOPES, z=rail_pos.z})
else
self.object:set_pos(rail_pos)
end
else
self.object:set_pos(pos)
minetest.log("error", "[minecart] No valid position "..(P2S(pos) or "nil"))
return -- no valid position
end
api.unload_cargo(self, pos)
self.stopped = true
self.object:set_velocity({x=0, y=0, z=0})
self.object:set_acceleration({x=0, y=0, z=0})
return
elseif stopped then
return
end
if recording then
minecart.store_next_waypoint(self, pos, vel)
-- Calc speed (value)
local speed = math.sqrt((vel.x+vel.z)^2 + vel.y^2)
-- Check if slope position
if pos_rounded.y > self.old_pos.y then
speed = speed - SLOPE_ACCELERATION
elseif pos_rounded.y < self.old_pos.y then
speed = speed + SLOPE_ACCELERATION
else
speed = speed - SLOWDOWN
end
local cart_dir = carts:velocity_to_dir(vel)
local same_dir = vector.equals(cart_dir, self.old_dir)
local update = {}
if self.old_pos and not self.punched and same_dir then
local flo_pos = vector.round(pos)
local flo_old = vector.round(self.old_pos)
if vector.equals(flo_pos, flo_old) then
-- Do not check one node multiple times
return
-- Add power/brake rail acceleration
local acc = (carts.railparams[node.name] or {}).acceleration or 0
speed = speed + acc
-- Determine new direction
local dir = carts:velocity_to_dir(vel)
if speed < 0 then
if on_slope then
dir = vector.multiply(dir, -1)
-- start with a value > 0
speed = 0.5
else
speed = 0
end
end
local ctrl, player
-- Get player controls
local ctrl, player
if recording then
player = minetest.get_player_by_name(self.driver)
if player then
ctrl = player:get_player_control()
end
end
local railparams
end
-- new_dir: New moving direction of the cart
-- keys: Currently pressed L/R key, used to ignore the key on the next rail node
local new_dir, keys = carts:get_rail_direction(rail_pos, dir, ctrl, self.old_keys, RAILTYPE)
-- dir: New moving direction of the cart
-- switch_keys: Currently pressed L/R key, used to ignore the key on the next rail node
local dir, switch_keys = carts:get_rail_direction(
pos, cart_dir, ctrl, self.old_switch, self.railtype
)
-- handle junctions
if switch_keys then -- recording
minecart.set_junction(self, pos, dir, switch_keys)
if recording and keys then
minecart.set_junction(self, rail_pos, new_dir, keys)
else -- normal run
dir, switch_keys = minecart.get_junction(self, pos, dir)
new_dir, keys = minecart.get_junction(self, rail_pos, new_dir)
end
self.old_keys = keys
local dir_changed = not vector.equals(dir, self.old_dir)
local new_acc = {x=0, y=0, z=0}
if vector.equals(dir, {x=0, y=0, z=0}) then
vel = {x = 0, y = 0, z = 0}
local pos_r = vector.round(pos)
if not carts:is_rail(pos_r, self.railtype)
and self.old_pos then
pos = self.old_pos
else
pos = pos_r
end
update.pos = true
update.vel = true
else
-- Direction change detected
if dir_changed then
vel = vector.multiply(dir, math.abs(vel.x + vel.z))
update.vel = true
if dir.y ~= self.old_dir.y then
pos = vector.round(pos)
update.pos = true
end
end
-- Center on the rail
if dir.z ~= 0 and math.floor(pos.x + 0.5) ~= pos.x then
pos.x = math.floor(pos.x + 0.5)
update.pos = true
end
if dir.x ~= 0 and math.floor(pos.z + 0.5) ~= pos.z then
pos.z = math.floor(pos.z + 0.5)
update.pos = true
end
-- Slow down or speed up..
local acc = dir.y * -1.5
-- Get rail for corrected position
railparams = get_railparams(pos)
-- no need to check for railparams == nil since we always make it exist.
local speed_mod = railparams.acceleration
if speed_mod and speed_mod ~= 0 then
-- Try to make it similar to the original carts mod
acc = acc + speed_mod
else
acc = acc - 0.4
end
new_acc = vector.multiply(dir, acc)
end
-- Limits
local max_vel = carts.speed_max
for _, v in pairs({"x","y","z"}) do
if math.abs(vel[v]) > max_vel then
vel[v] = carts:get_sign(vel[v]) * max_vel
new_acc[v] = 0
update.vel = true
end
end
self.object:set_acceleration(new_acc)
self.old_pos = vector.round(pos)
if not vector.equals(dir, {x=0, y=0, z=0}) then
self.old_dir = vector.new(dir)
end
self.old_switch = switch_keys
if self.punched then
self.punched = false
update.vel = true
end
railparams = railparams or get_railparams(pos)
if not (update.vel or update.pos) then
rail_on_step_event(railparams.on_step, self, dtime)
-- Detect U-turn
if (dir.x ~= 0 and dir.x == -new_dir.x) or (dir.z ~= 0 and dir.z == -new_dir.z) then
-- Stop the cart
self.object:set_velocity({x=0, y=0, z=0})
self.object:move_to(pos_rounded)
return
end
local yaw = 0
if self.old_dir.x < 0 then
yaw = math.pi/2*3
elseif self.old_dir.x > 0 then
yaw = math.pi/2
elseif self.old_dir.z < 0 then
yaw = math.pi
end
--self.object:set_yaw(yaw * math.pi)
local pitch = 0
if self.old_dir.z ~= 0 then
if dir.y == -1 then
pitch = -math.pi/4
elseif dir.y == 1 then
pitch = math.pi/4
end
else
if dir.y == -1 then
pitch = math.pi/4
elseif dir.y == 1 then
pitch = -math.pi/4
end
end
self.object:set_rotation({x = pitch, y = yaw, z = 0})
-- cart position correction on slopes
if pitch ~= 0 then
pos.y = pos.y + 0.5
update.pos = true
vel = vector.divide(vel, 2)
update.vel = true
elseif self.old_pitch ~= 0 then
vel = vector.multiply(vel, 2)
update.vel = true
end
self.old_pitch = pitch
if update.vel then
self.object:set_velocity(vel)
end
if update.pos then
if dir_changed then
self.object:set_pos(pos)
-- New direction
elseif not vector.equals(dir, new_dir) then
if new_dir.y ~= 0 then
self.object:set_pos({x=pos_rounded.x, y=pos_rounded.y + Y_OFFS_ON_SLOPES, z=pos_rounded.z})
else
self.object:move_to(pos)
self.object:set_pos(pos_rounded)
end
end
-- Set velocity and rotation
local new_vel = vector.multiply(new_dir, math.min(speed, MAX_SPEED))
local yaw = get_yaw(new_dir)
local pitch = get_pitch(new_dir)
-- call event handler
rail_on_step_event(railparams.on_step, self, dtime)
self.object:set_rotation({x = pitch, y = yaw, z = 0})
self.object:set_velocity(new_vel)
if recording then
minecart.store_next_waypoint(self, rail_pos, vel)
end
self.old_pos = pos_rounded
end
function api:on_step(dtime)
rail_on_step(self, dtime)
rail_sound(self, dtime)
self.delay = (self.delay or 0) + dtime
if self.delay > 0.09 then
rail_on_step(self)
rail_sound(self, self.delay)
self.delay = 0
end
end
return api

View File

@ -49,7 +49,7 @@ function api.get_station_name(pos)
end
end
function api.load_cart(pos, vel, item)
function api.load_cart(pos, vel, pitch, yaw, item)
-- Add cart to map
local obj = minetest.add_entity(pos, item.entity_name or "minecart:cart", nil)
-- Determine ID
@ -67,6 +67,7 @@ function api.load_cart(pos, vel, item)
item.cargo = nil
-- Start cart
obj:set_velocity(vel)
obj:set_rotation({x = pitch or 0, y = yaw or 0, z = 0})
return myID
else
print("Entity has no ID")

View File

@ -13,12 +13,10 @@
minecart = {}
-- Version for compatibility checks, see readme.md/history
minecart.version = 1.09
minecart.version = 1.10
minecart.hopper_enabled = minetest.settings:get_bool("minecart_hopper_enabled") ~= false
minecart.teleport_enabled = minetest.settings:get_bool("minecart_teleport_enabled") ~= false
print("minecart_hopper_enabled", dump(minetest.settings:get_bool("minecart_hopper_enabled")))
minecart.teleport_enabled = minetest.settings:get_bool("minecart_teleport_enabled") == true
minecart.S = minetest.get_translator("minecart")
local MP = minetest.get_modpath("minecart")

View File

@ -51,6 +51,24 @@ function minecart.register_cart_names(cart_name_stopped, cart_name_running)
end
end
function minecart.get_node_lvm(pos)
local node = minetest.get_node_or_nil(pos)
if node then
return node
end
local vm = minetest.get_voxel_manip()
local MinEdge, MaxEdge = vm:read_from_map(pos, pos)
local data = vm:get_data()
local param2_data = vm:get_param2_data()
local area = VoxelArea:new({MinEdge = MinEdge, MaxEdge = MaxEdge})
local idx = area:indexp(pos)
node = {
name = minetest.get_name_from_content_id(data[idx]),
param2 = param2_data[idx]
}
return node
end
function minecart.stopped(vel, tolerance)
tolerance = tolerance or 0.05
return math.abs(vel.x) < tolerance and math.abs(vel.z) < tolerance
@ -64,6 +82,13 @@ local function is_air_like(name)
return false
end
function minecart.range(val, min, max)
val = tonumber(val)
if val < min then return min end
if val > max then return max end
return val
end
function minecart.get_next_node(pos, param2)
local pos2 = param2 and vector.add(pos, param2_to_dir[param2]) or pos
local node = minetest.get_node(pos2)

View File

@ -33,26 +33,26 @@ local NodesAtStation = {}
--
-- Helper functions
--
local function calc_pos_and_vel(item)
local function get_pos_vel_pitch_yaw(item)
if item.start_time and item.start_key then -- cart on recorded route
local run_time = minetest.get_gametime() - item.start_time
local waypoints = get_route(item.start_key).waypoints
local waypoint = waypoints[run_time]
if waypoint then
return S2P(waypoint[1]), S2P(waypoint[2])
return S2P(waypoint[1]), S2P(waypoint[2]), 0, 0
end
end
if item.last_pos then
item.last_pos = vector.round(item.last_pos)
if carts:is_rail(item.last_pos, minetest.raillike_group("rail")) then
return item.last_pos, item.last_vel
return item.last_pos, item.last_vel, item.last_pitch or 0, item.last_yaw or 0
end
item.last_pos.y = item.last_pos.y - 1
if carts:is_rail(item.last_pos, minetest.raillike_group("rail")) then
return item.last_pos, item.last_vel
return item.last_pos, item.last_vel, item.last_pitch or 0, item.last_yaw or 0
end
end
return item.start_pos, {x=0, y=0, z=0}
return item.start_pos, {x=0, y=0, z=0}, 0, 0
end
--
@ -132,24 +132,29 @@ local function monitoring()
if entity then -- cart entity running
local pos = entity.object:get_pos()
local vel = entity.object:get_velocity()
local rot = entity.object:get_rotation()
if not minetest.get_node_or_nil(pos) then -- unloaded area
lib.unload_cart(pos, vel, entity, item)
item.stopped = minecart.stopped(vel)
end
-- store last pos from cart
item.last_pos, item.last_vel = pos, vel
item.last_pos, item.last_vel, item.last_pitch, item.last_yaw = pos, vel, rot.x, rot.y
else -- no cart running
local pos, vel = calc_pos_and_vel(item)
local pos, vel, pitch, yaw = get_pos_vel_pitch_yaw(item)
if pos and vel then
if minetest.get_node_or_nil(pos) then -- loaded area
local myID = lib.load_cart(pos, vel, item)
if pitch > 0 then
pos.y = pos.y + 0.5
end
local myID = lib.load_cart(pos, vel, pitch, yaw, item)
if myID then
item.stopped = minecart.stopped(vel)
to_be_added[myID] = table.copy(item)
CartsOnRail[key] = nil -- invalid old ID
end
end
item.last_pos, item.last_vel = pos, vel
item.last_pos, item.last_vel, item.last_pitch, item.last_yaw = pos, vel, pitch, yaw
else
-- should never happen
minetest.log("error", "[minecart] Cart of owner "..(item.owner or "nil").." got lost")

View File

@ -19,7 +19,7 @@ Instructions:
Important to know:
- 12 units of hydrogen are sufficient for a flight of 6 minutes
- Maximum 5 items stacks in your inventory are allowed including the controller.
- Maximum of 99 items in your inventory are allowed including the controller.
Otherwise you would be too heavy :-)
- The Jetpack also wears out and can be used for approximately 10 flights
- Always hold the controller tight during the flight, otherwise it will switch off :)

View File

@ -13,13 +13,16 @@
-- Load support for I18n.
local S = minetest.get_translator("ta4_jetpack")
local ta4_jetpack = {}
local Players = {}
local Jetpacks = {}
local ItemsBlocklist = {}
local MAX_HEIGHT = tonumber(minetest.settings:get("ta4_jetpack_max_height")) or 500
local MAX_VSPEED = tonumber(minetest.settings:get("ta4_jetpack_max_vertical_speed")) or 20
local MAX_HSPEED = (tonumber(minetest.settings:get("ta4_jetpack_max_horizontal_speed")) or 12) / 4
local MAX_NUM_INV_ITEMS = tonumber(minetest.settings:get("ta4_jetpack_max_num_inv_items")) or 5
local MAX_NUM_INV_ITEMS = tonumber(minetest.settings:get("ta4_jetpack_max_num_inv_items")) or 99
-- Flight time maximum 6 min or 360 s or 3600 steps.
-- 12 units hydrogen for 3600 steps means 0.0033 units hydrogen / step.
@ -31,6 +34,11 @@ local STEPS_TO_FUEL = 0.0033
local WEAR_VALUE = 180 -- roughly 10 flys, 6 min each
-- API function to register items that are forbidden in inventory during flight.
ta4_jetpack.register_forbidden_item = function(itemname)
ItemsBlocklist[itemname] = true
end
local function store_player_physics(player)
local meta = player:get_meta()
-- Check access conflicts with other mods
@ -143,19 +151,22 @@ local function check_player_load(player)
local bags_meta = meta:get_string("unified_inventory:bags")
if bags_meta then
if next(minetest.deserialize(bags_meta) or {}) then
return S("check your bags!")
return S("You are too heavy: Check your bags!")
end
end
for _, stack in ipairs(inv:get_list("craft") or {}) do
if not stack:is_empty() then
return S("check your carfting menu!")
return S("You are too heavy: Check your crafting menu!")
end
end
local count = 0
for _, stack in ipairs(inv:get_list("main") or {}) do
count = count + (stack:is_empty() and 0 or 1)
count = count + stack:get_count()
if count > MAX_NUM_INV_ITEMS then
return S("check your inventory!")
return S("You are too heavy: Check your inventory!")
end
if ItemsBlocklist[stack:get_name()] then
return S("You may not transport @1 with a jetpack!", stack:get_description())
end
end
end
@ -349,7 +360,7 @@ local function turn_controller_on_off(itemstack, user)
-- check inventory load
local res = check_player_load(user)
if res then
minetest.chat_send_player(name, S("[Jetpack] You are too heavy: ")..res)
minetest.chat_send_player(name, S("[Jetpack]").." "..res)
return itemstack
end
-- check fuel
@ -533,3 +544,6 @@ techage.add_manual_items({
ta4_jetpack = "ta4_jetpack.png",
ta4_jetpack_controller = 'ta4_jetpack_controller_inv.png'})
ta4_jetpack.register_forbidden_item("techage:cylinder_large_hydrogen")
ta4_jetpack.register_forbidden_item("techage:cylinder_small_hydrogen")
ta4_jetpack.register_forbidden_item("techage:hydrogen")

View File

@ -5,10 +5,11 @@ TA4 Jetpack=TA4 Jetpac
TA4 Jetpack Controller Off=TA4 Jetpack Controller Aus
TA4 Jetpack Controller On=TA4 Jetpack Controller An
Use the controller (left click) to fill the tank with hydrogen=Benutze den Controller (linksklick) um den Tank mit Wasserstoff zu füllen
[Jetpack] You are too heavy: =[Jetpack] Du bist zu schwer:
You are too heavy: Check your bags!=Du bist zu schwer: Prüfe deine Rucksäcke!
You are too heavy: Check your crafting menu!=Du bist zu schwer: Prüfe dein Crafting Menü!
You are too heavy: Check your inventory!=Du bist zu schwer: Prüfe dein Inventar!
You may not transport @1 with a jetpack!=Du darfst @1 nicht mit dem Jetpack transportieren!
[Jetpack]=[Jetpack]
[Jetpack] You don't have your jetpack on your back!=[Jetpack] Du hast dein Jetpack nicht auf dem Rücken!
[Jetpack] Your tank is empty!=[Jetpack] Dein Tank ist leer!
check your bags!=Prüfe deine Rucksäcke!
check your carfting menu!=Prüfe dein Crafting Menü!
check your inventory!=Prüfe dein Inventar!
##### not used anymore #####

View File

@ -3,9 +3,11 @@ TA4 Jetpack=
TA4 Jetpack Controller Off=
TA4 Jetpack Controller On=
Use the controller (left click) to fill the tank with hydrogen=
[Jetpack] You are too heavy: =
You are too heavy: Check your bags!=
You are too heavy: Check your crafting menu!=
You are too heavy: Check your inventory!=
You may not transport @1 with a jetpack!=
[Jetpack]=
[Jetpack] You don't have your jetpack on your back!=
[Jetpack] Your tank is empty!=
check your bags!=
check your carfting menu!=
check your inventory!=
##### not used anymore #####

View File

@ -17,7 +17,7 @@ techage.add_to_manual('DE', {
"\n"..
"\n",
" - 12 Einheiten Wasserstoff reichen für einen Flug von 6 Minuten\n"..
" - Maximal 5 Stapel von Gegenständen im Spieler-Inventar sind zulässig\\, einschließlich des Controllers\n(Sonst wärst du zu schwer :-)\n"..
" - Maximal 99 Gegenstände im Spieler-Inventar sind zulässig\\, einschließlich des Controllers\n(Sonst wärst du zu schwer :-)\n"..
" - Das Jetpack nutzt sich ab und kann für ca. 10 Flüge verwendet werden\n"..
" - Halte den Controller während des Fluges immer fest\\, sonst schaltet er sich aus :)\n"..
"\n"..

View File

@ -21,7 +21,7 @@ Das Jetpack ist inspiriert vom Jetpack von spirit689 (https://github.com/spirit6
## Was du wissen solltest
- 12 Einheiten Wasserstoff reichen für einen Flug von 6 Minuten
- Maximal 5 Stapel von Gegenständen im Spieler-Inventar sind zulässig, einschließlich des Controllers
- Maximal 99 Gegenstände im Spieler-Inventar sind zulässig, einschließlich des Controllers
(Sonst wärst du zu schwer :-)
- Das Jetpack nutzt sich ab und kann für ca. 10 Flüge verwendet werden
- Halte den Controller während des Fluges immer fest, sonst schaltet er sich aus :)

View File

@ -18,7 +18,7 @@ techage.add_to_manual('EN', {
"\n"..
"\n",
" - 12 units of hydrogen are sufficient for a flight of 6 minutes\n"..
" - Maximum 5 items stacks in your inventory are allowed including the controller.\nOtherwise you would be too heavy :-)\n"..
" - Maximum 99 items in your inventory are allowed including the controller.\nOtherwise you would be too heavy :-)\n"..
" - The Jetpack also wears out and can be used for approximately 10 flights\n"..
" - Always hold the controller tight during the flight\\, otherwise it will switch off :)\n"..
"\n"..

View File

@ -22,7 +22,7 @@ and by the historical game Lunar Lander.
## Important to know
- 12 units of hydrogen are sufficient for a flight of 6 minutes
- Maximum 5 items stacks in your inventory are allowed including the controller.
- Maximum 99 items in your inventory are allowed including the controller.
Otherwise you would be too heavy :-)
- The Jetpack also wears out and can be used for approximately 10 flights
- Always hold the controller tight during the flight, otherwise it will switch off :)

View File

@ -8,7 +8,7 @@ ta4_jetpack_max_vertical_speed (maximum vertical speed) int 20
ta4_jetpack_max_horizontal_speed (maximum horizontal speed) int 12
# Maximum number of inventory items a player can take
ta4_jetpack_max_num_inv_items (maximum number of inventory items) int 5
ta4_jetpack_max_num_inv_items (maximum number of inventory items) int 99

View File

@ -1,28 +0,0 @@
The Techage mod for Minetest is
Copyright (C) 2019-2020 Joachim Stolberg
License of source code
----------------------
This mod is free software; you can redistribute and/or
modify it under the terms of the GNU General Public License version 3 or later
published by the Free Software Foundation.
This mod is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public
License along with this mod; if not, write to the
Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
Boston, MA 02110-1301, USA.
License of media (textures, sounds and documentation)
-----------------------------------------------------
All textures, sounds and documentation files are licensed under the
Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0)
http://creativecommons.org/licenses/by-sa/3.0/

View File

@ -77,6 +77,14 @@ Available worlds will be converted to 'lsqlite3', but there is no way back, so:
### History
**2020-11-01 V0.25**
- Pull request #37: Trowel: Add protection support (from Thomas-S)
- Pull request #38: Charcoal Pile: Ignore "ignore" nodes (from Thomas-S)
- Autocrafter: Add register function for uncraftable items
- Fix bug: Tubes do not recognize when TA2 nodes are added/removed
- TA4 chest/tank: Add 'public' checkbox to allow public access
- Add nodes TA2 Power Generator and TA3 Electric Motor
**2020-10-20 V0.24**
- Pull request #27: Liquid Tanks: Add protection support (from Thomas-S)
- Pull request #28: Quarry: Improve digging behaviour (from Thomas-S)

View File

@ -178,7 +178,7 @@ techage.register_node({"techage:chest_ta2", "techage:chest_ta3"}, {
local function formspec4(pos)
return "size[10,9]"..
"tabheader[0,0;tab;"..S("Inventory,Configuration")..";1;;true]"..
"tabheader[0,0;tab;"..S("Inventory,Pre-Assignment,Config")..";1;;true]"..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
@ -189,9 +189,9 @@ local function formspec4(pos)
"listring[current_player;main]"
end
local function formspec4_cfg(pos)
local function formspec4_pre(pos)
return "size[10,9]"..
"tabheader[0,0;tab;"..S("Inventory,Configuration")..";2;;true]"..
"tabheader[0,0;tab;"..S("Inventory,Pre-Assignment,Config")..";2;;true]"..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
@ -201,8 +201,23 @@ local function formspec4_cfg(pos)
"listring[current_player;main]"
end
local function formspec4_cfg(pos)
local meta = minetest.get_meta(pos)
local label = meta:get_string("label") or ""
local public = dump((meta:get_int("public") or 0) == 1)
return "size[10,5]"..
"tabheader[0,0;tab;"..S("Inventory,Pre-Assignment,Config")..";3;;true]"..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
"field[0.5,1;9,1;label;"..S("Node label:")..";"..label.."]" ..
"checkbox[1,2;public;"..S("Allow public access to the chest")..";"..public.."]"..
"button_exit[3.5,4;3,1;exit;"..S("Save").."]"
end
local function ta4_allow_metadata_inventory_put(pos, listname, index, stack, player)
if minetest.is_protected(pos, player:get_player_name()) then
local public = M(pos):get_int("public") == 1
if not public and minetest.is_protected(pos, player:get_player_name()) then
return 0
end
@ -214,7 +229,8 @@ local function ta4_allow_metadata_inventory_put(pos, listname, index, stack, pla
end
local function ta4_allow_metadata_inventory_take(pos, listname, index, stack, player)
if minetest.is_protected(pos, player:get_player_name()) then
local public = M(pos):get_int("public") == 1
if not public and minetest.is_protected(pos, player:get_player_name()) then
return 0
end
@ -226,7 +242,8 @@ local function ta4_allow_metadata_inventory_take(pos, listname, index, stack, pl
end
local function ta4_allow_metadata_inventory_move(pos, from_list, from_index, to_list, to_index, count, player)
if minetest.is_protected(pos, player:get_player_name()) then
local public = M(pos):get_int("public") == 1
if not public and minetest.is_protected(pos, player:get_player_name()) then
return 0
end
@ -276,10 +293,25 @@ minetest.register_node("techage:chest_ta4", {
mem.filter = nil
meta:set_string("formspec", formspec4(pos))
elseif fields.tab == "2" then
meta:set_string("formspec", formspec4_pre(pos))
elseif fields.tab == "3" then
meta:set_string("formspec", formspec4_cfg(pos))
elseif fields.quit == "true" then
mem.filter = nil
end
if fields.public then
meta:set_int("public", fields.public == "true" and 1 or 0)
end
if fields.exit then
local number = meta:get_string("node_number")
if fields.label ~= "" then
meta:set_string("infotext", minetest.formspec_escape(fields.label).." #"..number)
else
meta:set_string("infotext", S("TA4 Protected Chest").." "..number)
end
meta:set_string("label", fields.label)
meta:set_string("formspec", formspec4_cfg(pos))
end
end,
techage_set_numbers = function(pos, numbers, player_name)

View File

@ -180,10 +180,7 @@ function techage.register_consumer(base_name, inv_name, tiles, tNode, validState
if (meta:contains("node_number")) then
meta:set_string("node_number", "")
end
local number = "-"
if stage > 2 then
number = techage.add_node(pos, name_pas)
end
local number = techage.add_node(pos, name_pas, stage == 2)
if crd.power_netw then
crd.power_netw:after_place_node(pos)
end

View File

@ -35,7 +35,7 @@ local function formspec(self, pos, nvm)
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
power.formspec_label_bar(0, 0.8, S("power"), PWR_CAPA, nvm.provided)..
power.formspec_label_bar(pos, 0, 0.8, S("power"), PWR_CAPA, nvm.provided)..
"image_button[2.8,2;1,1;".. self:get_state_button_image(nvm) ..";state_button;]"..
"tooltip[2.8,2;1,1;"..self:get_state_tooltip(nvm).."]"
end

View File

@ -20,11 +20,16 @@ local S = techage.S
local DESCRIPTION = S("TA4 8x2000 Chest")
local STACK_SIZE = 2000
local function gen_stack(inv, idx)
inv[idx] = {name = "", count = 0}
end
local function gen_inv(nvm)
nvm.inventory = {}
for i = 1,8 do
nvm.inventory[i] = {name = "", count = 0}
gen_stack(nvm.inventory, i)
end
return nvm.inventory
end
local function repair_inv(nvm)
@ -32,33 +37,26 @@ local function repair_inv(nvm)
for i = 1,8 do
local item = nvm.inventory[i]
if not item or type(item) ~= "table"
or not item.name or type(item.name) ~= "string"
or not item.count or type(item.count) ~= "number" then
nvm.inventory[i] = {name = "", count = 0}
or not item.name or type(item.name) ~= "string" or item.name == ""
or not item.count or type(item.count) ~= "number" or item.count < 1
then
gen_stack(nvm.inventory, i)
end
end
end
local function get_stack(nvm, idx)
nvm.inventory = nvm.inventory or {}
if nvm.inventory[idx] then
return nvm.inventory[idx]
end
nvm.inventory[idx] = {name = "", count = 0}
return nvm.inventory[idx]
return nvm.inventory[idx] or gen_stack(nvm.inventory, idx)
end
local function get_count(nvm, idx)
nvm.inventory = nvm.inventory or {}
if idx and idx > 0 then
nvm.inventory = nvm.inventory or {}
if nvm.inventory[idx] then
return nvm.inventory[idx].count or 0
else
return 0
end
return nvm.inventory[idx] and nvm.inventory[idx].count or 0
else
local count = 0
for _,item in ipairs(nvm.inventory or {}) do
for _,item in ipairs(nvm.inventory) do
count = count + item.count or 0
end
return count
@ -68,9 +66,7 @@ end
local function get_itemstring(nvm, idx)
if idx and idx > 0 then
nvm.inventory = nvm.inventory or {}
if nvm.inventory[idx] then
return nvm.inventory[idx].name or ""
end
return nvm.inventory[idx] and nvm.inventory[idx].name or ""
end
return ""
end
@ -97,11 +93,11 @@ local function inv_state(nvm)
end
local function max_stacksize(item_name)
local ndef = minetest.registered_nodes[item_name] or minetest.registered_items[item_name] or minetest.registered_craftitems[item_name]
if ndef then
return ndef.stack_max
end
return 0
-- It is sufficient to use minetest.registered_items as all registration
-- functions (node, craftitems, tools) add the definitions there.
local ndef = minetest.registered_items[item_name]
-- Return 1 as fallback so that slots with unknown items can be emptied.
return ndef and ndef.stack_max or 1
end
local function get_stacksize(pos)
@ -112,102 +108,159 @@ local function get_stacksize(pos)
return size
end
-- Sort the items into the nvm inventory
local function sort_in(pos, nvm, stack)
local old_counts = {}
local orig_count = stack:get_count()
for idx,item in ipairs(nvm.inventory or {}) do
if item.name and (item.name == "" or item.name == stack:get_name()) then
local count = math.min(stack:get_count(), get_stacksize(pos) - item.count)
old_counts[idx] = item.count -- store old value
item.count = item.count + count
item.name = stack:get_name()
stack:set_count(stack:get_count() - count)
if stack:get_count() == 0 then
return true
end
end
-- Returns a boolean that indicates if an itemstack and nvmstack can be combined.
-- The second return value is a string describing the reason.
-- This function guarantees not to modify any of both stacks.
local function doesItemStackMatchNvmStack(itemstack, nvmstack)
if itemstack:get_count() == 0 or nvmstack.count == 0 then
return true, "Empty stack"
end
-- restore old values
for idx,cnt in pairs(old_counts) do
nvm.inventory[idx].count = cnt
if nvmstack.name and nvmstack.name ~= "" and nvmstack.name ~= itemstack:get_name() then
return false, "Mismatching names"
end
stack:set_count(orig_count)
return false
-- The following seems to be the most reliable approach to compare meta.
local nvm_meta = ItemStack():get_meta()
nvm_meta:from_table(minetest.deserialize(nvmstack.meta))
if not nvm_meta:equals(itemstack:get_meta()) then
return false, "Mismatching meta"
end
if (nvmstack.wear or 0) ~= itemstack:get_wear() then
return false, "Mismatching wear"
end
return true, "Stacks match"
end
local function move_items_to_stack(item, stack, num)
item.count = item.count - num
stack.count = stack.count + num
if stack.count > 0 then
stack.name = item.name
end
if item.count == 0 then
item.name = "" -- empty
end
return stack
end
local function get_item(pos, nvm, item_name, count)
local stack = {count = 0}
nvm.inventory = nvm.inventory or {}
if item_name then
-- Take specified items from the chest
for _,item in ipairs(nvm.inventory) do
if item.name == item_name then
local num = math.min(item.count, count - stack.count, max_stacksize(item.name))
if M(pos):get_int("assignment") == 1 and num == item.count then
-- never take the last item
num = num - 1
end
stack = move_items_to_stack(item, stack, num)
if stack.count == count then
return ItemStack(stack)
end
end
end
elseif M(pos):get_int("priority") == 1 then
-- Take any items. The position within the inventory is from right to left
for idx = 8,1,-1 do
local item = nvm.inventory[idx]
if item.name ~= "" and (stack.name == nil or stack.name == item.name) then
local num = math.min(item.count, count - stack.count, max_stacksize(item.name))
if M(pos):get_int("assignment") == 1 and num == item.count then
-- never take the last item
num = num - 1
end
stack = move_items_to_stack(item, stack, num)
if stack.count == count then
return ItemStack(stack)
end
end
end
else
-- Take any items. The position within the inventory
-- is incremented each time so that different item stacks will be considered.
local mem = techage.get_mem(pos)
mem.startpos = mem.startpos or 1
for idx = mem.startpos, mem.startpos + 8 do
idx = (idx % 8) + 1
local item = nvm.inventory[idx]
if item.name ~= "" and (stack.name == nil or stack.name == item.name) then
local num = math.min(item.count, count - stack.count, max_stacksize(item.name))
if M(pos):get_int("assignment") == 1 and num == item.count then
-- never take the last item
num = num - 1
end
stack = move_items_to_stack(item, stack, num)
if stack.count == count then
mem.startpos = idx
return ItemStack(stack)
end
end
mem.startpos = idx
end
-- Generic function for adding items to the 8x2000 Chest
-- This function guarantees not to modify the itemstack.
-- The number of items that were added to the chest is returned.
local function add_to_chest(pos, input_stack, idx)
local nvm = techage.get_nvm(pos)
local nvm_stack = get_stack(nvm, idx)
if input_stack:get_count() == 0 then
return 0
end
if stack.count > 0 then
return ItemStack(stack)
if not doesItemStackMatchNvmStack(input_stack, nvm_stack) then
return 0
end
local count = math.min(input_stack:get_count(), get_stacksize(pos) - (nvm_stack.count or 0))
if nvm_stack.count == 0 then
nvm_stack.name = input_stack:get_name()
nvm_stack.meta = minetest.serialize(input_stack:get_meta():to_table())
nvm_stack.wear = input_stack:get_wear()
end
nvm_stack.count = nvm_stack.count + count
return count
end
local function stackOrNil(stack)
if stack and stack.get_count and stack:get_count() > 0 then
return stack
end
return nil
end
-- Generic function for taking items from the 8x2000 Chest
-- output_stack is directly modified; but nil can also be supplied.
-- The resulting output_stack is returned from the function.
-- keep_assignment indicates if the meta information for this function should be considered (manual vs. tubes).
local function take_from_chest(pos, idx, output_stack, max_total_count, keep_assignment)
local nvm = techage.get_nvm(pos)
local nvm_stack = get_stack(nvm, idx)
output_stack = output_stack or ItemStack()
local assignment_count = keep_assignment and M(pos):get_int("assignment") == 1 and 1 or 0
local count = math.min(nvm_stack.count - assignment_count, max_stacksize(nvm_stack.name))
if max_total_count then
count = math.min(count, max_total_count - output_stack:get_count())
end
if count < 1 then
return stackOrNil(output_stack)
end
if not doesItemStackMatchNvmStack(output_stack, nvm_stack) then
return stackOrNil(output_stack)
end
output_stack:add_item(ItemStack({
name = nvm_stack.name,
count = count,
wear = nvm_stack.wear,
}))
output_stack:get_meta():from_table(minetest.deserialize(nvm_stack.meta))
nvm_stack.count = nvm_stack.count - count
if nvm_stack.count == 0 then
gen_stack(nvm.inventory or {}, idx)
end
return stackOrNil(output_stack)
end
-- Function for adding items to the 8x2000 Chest via automation, e.g. pushers
local function tube_add_to_chest(pos, input_stack)
local nvm = techage.get_nvm(pos)
nvm.inventory = nvm.inventory or {}
-- Backup some values needed for restoring the old
-- state if items can't fully be added to chest.
local orig_count = input_stack:get_count()
local backup = table.copy(nvm.inventory)
for idx = 1,8 do
input_stack:take_item(add_to_chest(pos, input_stack, idx))
end
if input_stack:get_count() > 0 then
nvm.inventory = backup -- Restore old nvm inventory
input_stack:set_count(orig_count) -- Restore input_stack
return false -- No items were added to chest
else
return true -- Items were added successfully
end
end
-- Function for taking items from the 8x2000 Chest via automation, e.g. pushers
local function tube_take_from_chest(pos, item_name, count)
local nvm = techage.get_nvm(pos)
local mem = techage.get_mem(pos)
nvm.inventory = nvm.inventory or {}
mem.startpos = mem.startpos or 1
local prio = M(pos):get_int("priority") == 1
local startpos = prio and 8 or mem.startpos
local endpos = prio and 1 or mem.startpos + 8
local step = prio and -1 or 1
local itemstack = ItemStack()
for idx = startpos,endpos,step do
idx = ((idx - 1) % 8) + 1
local nvmstack = get_stack(nvm, idx)
if not item_name or item_name == nvmstack.name then
take_from_chest(pos, idx, itemstack, count - itemstack:get_count(), true)
if itemstack:get_count() == count then
mem.startpos = idx + 1
return itemstack
end
end
mem.startpos = idx + 1
end
return stackOrNil(itemstack)
end
-- Function for manually adding items to the 8x2000 Chest via the formspec
local function inv_add_to_chest(pos, idx)
local inv = M(pos):get_inventory()
local inv_stack = inv:get_stack("main", idx)
local count = add_to_chest(pos, inv_stack, idx)
inv_stack:set_count(inv_stack:get_count() - count)
inv:set_stack("main", idx, inv_stack)
end
-- Function for manually taking items from the 8x2000 Chest via the formspec
local function inv_take_from_chest(pos, idx)
local inv = M(pos):get_inventory()
local inv_stack = inv:get_stack("main", idx)
if inv_stack:get_count() > 0 then
return
end
local output_stack = take_from_chest(pos, idx)
if output_stack then
inv:set_stack("main", idx, output_stack)
end
end
@ -218,9 +271,15 @@ local function formspec_container(x, y, nvm, inv)
tbl[#tbl+1] = "box["..(xpos - 0.03)..",0;0.86,0.9;#808080]"
local stack = get_stack(nvm, i)
if stack.name ~= "" then
local itemname = stack.name.." "..stack.count
local itemstack = ItemStack({
name = stack.name,
count = stack.count,
wear = stack.wear,
})
itemstack:get_meta():from_table(minetest.deserialize(stack.meta))
local itemname = itemstack:to_string()
--tbl[#tbl+1] = "item_image["..xpos..",1;1,1;"..itemname.."]"
tbl[#tbl+1] = techage.item_image(xpos, 0, itemname)
tbl[#tbl+1] = techage.item_image(xpos, 0, itemname, stack.count)
end
if inv:get_stack("main", i):get_count() == 0 then
tbl[#tbl+1] = "image_button["..xpos..",1;1,1;techage_form_get_arrow.png;get"..i..";]"
@ -366,47 +425,6 @@ local function on_rightclick(pos, node, clicker)
end
end
-- take items from chest
local function move_from_nvm_to_inv(pos, idx)
local nvm = techage.get_nvm(pos)
local inv = M(pos):get_inventory()
local inv_stack = inv:get_stack("main", idx)
local nvm_stack = get_stack(nvm, idx)
if nvm_stack.count > 0 and inv_stack:get_count() == 0 then
local count = math.min(nvm_stack.count, max_stacksize(nvm_stack.name))
nvm_stack.count = nvm_stack.count - count
inv:set_stack("main", idx, {name = nvm_stack.name, count = count})
if nvm_stack.count == 0 then
nvm_stack.name = ""
end
end
end
-- add items to chest
local function move_from_inv_to_nvm(pos, idx)
local nvm = techage.get_nvm(pos)
local inv = M(pos):get_inventory()
local inv_stack = inv:get_stack("main", idx)
local nvm_stack = get_stack(nvm, idx)
if inv_stack:get_count() > 0 then
-- Don't handle items with meta or wear information because it would get lost.
local meta_table = inv_stack:get_meta():to_table()
if meta_table ~= nil and next(meta_table.fields) ~= nil or inv_stack:get_wear() ~= 0 then
return
end
if nvm_stack.count == 0 or nvm_stack.name == inv_stack:get_name() then
local count = math.min(inv_stack:get_count(), get_stacksize(pos) - nvm_stack.count)
nvm_stack.count = nvm_stack.count + count
nvm_stack.name = inv_stack:get_name()
inv_stack:set_count(inv_stack:get_count() - count)
inv:set_stack("main", idx, inv_stack)
end
end
end
local function on_receive_fields(pos, formname, fields, player)
if minetest.is_protected(pos, player:get_player_name()) then
return
@ -414,10 +432,10 @@ local function on_receive_fields(pos, formname, fields, player)
for i = 1,8 do
if fields["get"..i] ~= nil then
move_from_nvm_to_inv(pos, i)
inv_take_from_chest(pos, i)
break
elseif fields["add"..i] ~= nil then
move_from_inv_to_nvm(pos, i)
inv_add_to_chest(pos, i)
break
end
end
@ -534,24 +552,21 @@ minetest.register_node("techage:ta4_chest_dummy", {
techage.register_node({"techage:ta4_chest"}, {
on_pull_item = function(pos, in_dir, num, item_name)
local nvm = techage.get_nvm(pos)
local res = get_item(pos, nvm, item_name, num)
local res = tube_take_from_chest(pos, item_name, num)
if techage.is_activeformspec(pos) then
M(pos):set_string("formspec", formspec(pos))
end
return res
end,
on_push_item = function(pos, in_dir, stack)
local nvm = techage.get_nvm(pos)
local res = sort_in(pos, nvm, stack)
local res = tube_add_to_chest(pos, stack)
if techage.is_activeformspec(pos) then
M(pos):set_string("formspec", formspec(pos))
end
return res
end,
on_unpull_item = function(pos, in_dir, stack)
local nvm = techage.get_nvm(pos)
local res = sort_in(pos, nvm, stack)
local res = tube_add_to_chest(pos, stack)
if techage.is_activeformspec(pos) then
M(pos):set_string("formspec", formspec(pos))
end

View File

@ -207,28 +207,39 @@ end
-- Add node to the techage lists.
-- Function determines and returns the node position number,
-- needed for message communication.
function techage.add_node(pos, name)
-- If TA2 node, return '-' instead of a real number, because
-- TA2 nodes should not support number based commands.
function techage.add_node(pos, name, is_ta2)
if item_handling_node(name) then
Tube:after_place_node(pos)
end
if is_ta2 then
return "-"
end
local key = minetest.hash_node_position(pos)
return NumbersToBeRecycled[key] or get_number(pos, true)
local num = NumbersToBeRecycled[key]
if num then
backend.set_nodepos(num, pos)
NumbersToBeRecycled[key] = nil
return num
end
return get_number(pos, true)
end
-- Function removes the node from the techage lists.
function techage.remove_node(pos, oldnode, oldmetadata)
local number = oldmetadata and oldmetadata.fields and oldmetadata.fields.node_number
local number = oldmetadata and oldmetadata.fields and oldmetadata.fields.node_number or oldmetadata.fields.number
print("number1", dump(oldmetadata))
number = number or get_number(pos)
print("number2", number)
if number and tonumber(number) then
local key = minetest.hash_node_position(pos)
NumbersToBeRecycled[key] = number
local ninfo = NodeInfoCache[number] or update_nodeinfo(number)
if ninfo then
NodeInfoCache[number] = nil
if item_handling_node(ninfo.name) then
Tube:after_dig_node(pos)
end
end
NodeInfoCache[number] = nil
print("number3", number)
end
if oldnode and item_handling_node(oldnode.name) then
Tube:after_dig_node(pos)
end
end

View File

@ -240,19 +240,15 @@ function techage.is_ocean(pos)
return true
end
function techage.item_image(x, y, itemname)
function techage.item_image(x, y, itemname, count)
local name, size = unpack(string.split(itemname, " "))
size = count and count or size
size = tonumber(size) or 1
local label = ""
local tooltip = ""
local ndef = minetest.registered_nodes[name] or minetest.registered_items[name] or minetest.registered_craftitems[name]
if ndef and ndef.description then
local text = minetest.formspec_escape(ndef.description)
tooltip = "tooltip["..x..","..y..";1,1;"..text..";#0C3D32;#FFFFFF]"
end
if ndef and ndef.stack_max == 1 then
size = tonumber(size)
local text = minetest.formspec_escape(ItemStack(itemname):get_description())
local tooltip = "tooltip["..x..","..y..";1,1;"..text..";#0C3D32;#FFFFFF]"
if minetest.registered_tools[name] and size > 1 then
local offs = 0
if size < 10 then
offs = 0.65

View File

@ -36,14 +36,28 @@ function techage.liquid.formspec(pos, nvm)
if nvm.liquid and nvm.liquid.amount and nvm.liquid.amount > 0 and nvm.liquid.name then
itemname = nvm.liquid.name.." "..nvm.liquid.amount
end
return "size[4,2]"..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
"box[0,-0.1;3.8,0.5;#c6e8ff]"..
"label[1,-0.1;"..minetest.colorize("#000000", title).."]"..
help(3.4, -0.1)..
techage.item_image(1.5, 1, itemname)
local name = minetest.get_node(pos).name
if name == "techage:ta4_tank" then
local public = dump((M(pos):get_int("public") or 0) == 1)
return "size[5,3]"..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
"box[0,-0.1;4.8,0.5;#c6e8ff]"..
"label[1.5,-0.1;"..minetest.colorize("#000000", title).."]"..
help(4.4, -0.1)..
techage.item_image(2, 1, itemname)..
"checkbox[0.1,2.5;public;"..S("Allow public access to the tank")..";"..public.."]"
else
return "size[4,2]"..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
"box[0,-0.1;3.8,0.5;#c6e8ff]"..
"label[1,-0.1;"..minetest.colorize("#000000", title).."]"..
help(3.4, -0.1)..
techage.item_image(1.5, 1, itemname)
end
end
function techage.liquid.is_empty(pos)
@ -205,7 +219,8 @@ local function empty_on_punch(pos, nvm, full_container, item_count)
end
function techage.liquid.on_punch(pos, node, puncher, pointed_thing)
if minetest.is_protected(pos, puncher:get_player_name()) then
local public = M(pos):get_int("public") == 1
if not public and minetest.is_protected(pos, puncher:get_player_name()) then
return
end

View File

@ -26,6 +26,8 @@ end
function techage.mark_region(name, pos1, pos2, owner, secs)
if not name or not pos1 or not pos2 then return end
techage.unmark_region(name)
local thickness = 0.2
@ -82,6 +84,7 @@ minetest.register_entity(":techage:region_cube", {
textures = {"techage_cube_mark.png"},
use_texture_alpha = true,
physical = false,
glow = 12,
},
on_step = function(self, dtime)
if marker_region[self.player_name] == nil then

View File

@ -114,7 +114,7 @@ minetest.register_entity(":techage:position_side", {
physical = false,
visual_size = {x = 1.1, y = 1.1, z = 1.1},
collisionbox = {-0.55,-0.55,-0.55, 0.55,0.55,0.55},
glow = 8,
glow = 12,
},
on_step = function(self, dtime)
if marker_region[self.player_name] == nil then

View File

@ -243,7 +243,7 @@ local function collect_network_nodes(pos, outdir, tlib2)
end
for _,ntype in ipairs(ntypes) do
if not netw[ntype] then netw[ntype] = {} end
netw[ntype][#netw[ntype] + 1] = {pos = pos, indir = indir, nominal = ndef.nominal or 0}
netw[ntype][#netw[ntype] + 1] = {pos = pos, indir = indir, nominal = ndef.nominal}
end
end)
netw.best_before = minetest.get_gametime() + BEST_BEFORE

View File

@ -41,7 +41,7 @@ function techage.valid_place_for_windturbine(pos, player_name, num_turbines)
if data then
local name = minetest.get_biome_name(data.biome)
if not string.find(name, "ocean") then
chat_message(player_name, S("This is a "..name.." biome and no ocean!"))
chat_message(player_name, S("This is a").." "..name.." "..S("biome and no ocean!"))
return false
end
end
@ -60,7 +60,7 @@ function techage.valid_place_for_windturbine(pos, player_name, num_turbines)
pos2 = {x=pos.x+20, y=1, z=pos.z+20}
num = #minetest.find_nodes_in_area(pos1, pos2,
{"default:water_source", "default:water_flowing", "ignore"})
print(num, (41 * 41 * 0.9))
if num < (41*41 * 0.8) then
techage.mark_region(player_name, pos1, pos2, "")
chat_message(player_name, S("Here is not enough water (41x41 m)!"))
@ -69,6 +69,7 @@ function techage.valid_place_for_windturbine(pos, player_name, num_turbines)
-- Check for next wind turbine
pos1 = {x=pos.x-13, y=2, z=pos.z-13}
pos2 = {x=pos.x+13, y=22, z=pos.z+13}
num = #minetest.find_nodes_in_area(pos1, pos2, {"techage:ta4_wind_turbine"})
if num > num_turbines then
techage.mark_region(player_name, pos1, pos2, "")

View File

@ -59,6 +59,15 @@ local function take_liquid(pos, indir, name, amount)
return amount, name
end
local function untake_liquid(pos, indir, name, amount)
local leftover = liquid.srv_put(pos, indir, name, amount)
if techage.is_activeformspec(pos) then
local nvm = techage.get_nvm(pos)
M(pos):set_string("formspec", liquid.formspec(pos, nvm))
end
return leftover
end
local function put_liquid(pos, indir, name, amount)
-- check if it is not powder
local ndef = minetest.registered_craftitems[name] or {}
@ -153,6 +162,7 @@ minetest.register_node("techage:tank_cart", {
peek = liquid.srv_peek,
put = put_liquid,
take = take_liquid,
untake = untake_liquid,
},
networks = networks_def,
on_rightclick = on_rightclick,

View File

@ -157,7 +157,9 @@ local function untake(recipe, pos, liquids)
for _,item in pairs(recipe.input) do
if item.name ~= "" then
local outdir = liquids[item.name] or reload_liquids(pos)[item.name]
liquid.untake(pos, outdir, item.name, item.num)
if outdir then
liquid.untake(pos, outdir, item.name, item.num)
end
end
end
end

View File

@ -32,7 +32,7 @@ local function formspec(self, pos, nvm)
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
power.formspec_label_bar(0, 0.8, S("power"), PWR_CAPA, nvm.provided)..
power.formspec_label_bar(pos, 0, 0.8, S("power"), PWR_CAPA, nvm.provided)..
"image_button[2.8,2;1,1;".. self:get_state_button_image(nvm) ..";state_button;]"..
"tooltip[2.8,2;1,1;"..self:get_state_tooltip(nvm).."]"
end

View File

@ -49,7 +49,7 @@ local function formspec(self, pos, nvm)
default.gui_slots..
"box[0,-0.1;4.8,0.5;#c6e8ff]"..
"label[1,-0.1;"..minetest.colorize("#000000", S("Digtron Battery")).."]"..
power.formspec_label_bar(0, 0.8, S("Load"), TOTAL_MAX, total, S("Coal Equivalents"))..
power.formspec_label_bar(pos, 0, 0.8, S("Load"), TOTAL_MAX, total, S("Coal Equivalents"))..
"image_button[2.6,2;1,1;".. self:get_state_button_image(nvm) ..";state_button;]"..
"tooltip[2.6,2;1,1;"..self:get_state_tooltip(nvm).."]"..
"image[3.75,2;1,1;"..techage.get_power_image(pos, nvm).."]"

View File

@ -48,6 +48,7 @@ techage.Items = {
ta2_chest = "techage:chest_ta2",
ta2_forceload = "techage:forceload",
ta2_driveaxle = "techage:axle",
ta2_generator = "techage:ta2_generator_off",
---------------------
techage_ta3 = "techage_ta3.png",
techage_ta31 = "techage_ta3b.png",
@ -114,6 +115,7 @@ techage.Items = {
ta3_pipe_wall_entry = "techage:ta3_pipe_wall_entry",
ta3_mesecons_converter = "techage:ta3_mesecons_converter",
ta3_valve = "techage:ta3_valve_closed",
ta3_motor = "techage:ta3_motor_off",
----------------------------
techage_ta4 = "techage_ta4.png",
ta4_windturbine = "techage:ta4_wind_turbine",

View File

@ -28,6 +28,7 @@ techage.manual_DE.aTitel = {
"3,TA2 Schwungrad / Flywheel",
"3,TA2 Dampfleitungen / Steam Pipe",
"3,TA2 Antriebsachsen / TA2 Drive Axle",
"3,TA2 Stromgenerator / TA2 Power Generator",
"2,Items schieben und sortieren",
"3,Röhren / TechAge Tube",
"3,TA2 Schieber / Pusher",
@ -66,6 +67,8 @@ techage.manual_DE.aTitel = {
"3,TA3 Kleiner Stromgenerator / Tiny Power Generator",
"3,TA3 Akku Block / Akku Box",
"3,TA3 Strom Terminal / Power Terminal",
"3,TA3 Elektromotor / TA3 Electric Motor",
"3,TA3 Strom Terminal / Power Terminal",
"2,TA3 Industrieofen",
"3,TA3 Ofen-Ölbrenner / Furnace Oil Burner",
"3,TA3 Ofenoberteil / Furnace Top",
@ -399,6 +402,14 @@ techage.manual_DE.aText = {
"\n"..
"\n"..
"\n",
"Um Lampen oder andere Stromverbraucher an einer Dampfmaschine betreiben zu können\\, wird der TA2 Stromgenerator benötigt. Der TA2 Stromgenerator muss auf einer Seite mit Antriebsachsen verbunden werden und liefert dann auf der anderen Seite elektrischen Strom.\n"..
"\n"..
"Wird der Stromgenerator nicht mit ausreichend Kraft versorgt\\, geht er in einen Fehlerzustand und muss über einen Rechtsklick wieder aktiviert werden.\n"..
"\n"..
"Das Stromgenerator nimmt primär max. 25 ku an Achsenkraft auf und gibt sekundär max. 24 ku als Strom wieder ab. Er verbraucht also ein ku für die Umwandlung.\n"..
"\n"..
"\n"..
"\n",
"Um Gegenstände (Items) von einer Verarbeitungsstation zur nächsten weiter zu transportieren\\, werden Schieber und Röhren verwendet. Siehe Plan.\n"..
"\n"..
"\n"..
@ -424,6 +435,8 @@ techage.manual_DE.aText = {
"\n"..
"Der Verteiler besitzt dazu ein Menü mit 4 Filter mit unterschiedlichen Farben\\, entsprechend den 4 Ausgängen. Soll ein Ausgang genutzt werden\\, so muss der entsprechende Filter über die \"on\" Checkbox aktiviert werden. Alle Items\\, die für diesen Filter konfiguriert sind\\, werden über den zugeordneten Ausgang ausgegeben. Wird ein Filter aktiviert\\, ohne das Items konfiguriert werden\\, so sprechen wir hier von einem \"nicht-konfigurierten\"\\, offenen Ausgang.\n"..
"\n"..
"*Achtung: Der Verteiler ist an seinen Ausgängen gleichzeitig ein Schieber. Daher niemals die Gegenstände mit einem Schieber aus dem Verteiler ziehen!*\n"..
"\n"..
"Für einen nicht-konfigurierten Ausgang gibt es zwei Betriebsarten:\n"..
"\n"..
"1) Alle Items ausgeben\\, die an keine anderen Ausgängen ausgegeben werden können\\, auch wenn diese blockiert sind.\n"..
@ -649,6 +662,21 @@ techage.manual_DE.aText = {
"\n"..
"\n"..
"\n",
"Um TA2 Maschinen über das Stromnetz betreiben zu können\\, wird der TA3 Elektromotor benötigt. Dieser wandelt Strom in Achsenkraft um.\n"..
"Wird der Elektromotor nicht mit ausreichend Strom versorgt\\, geht er in einen Fehlerzustand und muss über einen Rechtsklick wieder aktiviert werden.\n"..
"\n"..
"Das Elektromotor nimmt primär max. 40 ku an Strom auf und gibt sekundär max. 39 ku als Achsenkraft wieder ab. Er verbraucht also ein ku für die Umwandlung.\n"..
"\n"..
"\n"..
"\n",
"Das Strom-Terminal muss mit dem Stromnetz verbunden werden. Es zeigt Daten aus dem Stromnetz an.\n"..
"\n"..
"In der oberen Hälfte werden nur die Daten eines ausgewählten Typs ausgegeben. Wird als Typ bspw. \"Kraftwerk\" gewählt\\, so werden nur die Daten von Öl- und Kohlekraftwerken gesammelt und ausgegeben. Links werden die Daten von Generatoren (Stromabgabe) und rechts die Daten von Energiespeichern (Stromaufnahme) ausgegeben. Beim Akkublocks bspw. wird beides ausgegeben\\, da der Akku Strom aufnehmen und abgeben kann.\n"..
"\n"..
"In der unteren Hälfte werden die Daten aller Generatoren und Speichersystemen des ganzen Stromnetzen zusammengefasst ausgegeben.\n"..
"\n"..
"\n"..
"\n",
"Der TA3 Industrieofen dient als Ergänzung zu normalen Ofen (furnace). Damit können alle Waren mit \"Koch\" Rezepten\\, auch im Industrieofen hergestellt werden. Es gibt aber auch spezielle Rezepte\\, die nur im Industrieofen hergestellt werden können.\n"..
"Der Industrieofen hat sein eigenes Menü zur Rezeptauswahl. Abhängig von den Waren im Industrieofen Inventar links kann rechts das Ausgangsprodukt gewählt werden.\n"..
"\n"..
@ -935,7 +963,7 @@ techage.manual_DE.aText = {
" - 'pub' schalte in den öffentlichen Modus um\n"..
" - 'priv' schalte in den privaten Modus um\n"..
"\n"..
"Im privaten Modul kann nur der Besitzer selbst Kommandos eingeben oder Tasten nutzen.\n"..
"Im privaten Modus (private) kann das Terminal nur von Spielern verwendet werden\\, die an diesem Ort bauen können\\, also Protection Rechte besitzen. Im öffentlichen Modus (public) können alle Spieler die vorkonfigurierten Tasten verwenden.\n"..
"\n"..
"\n"..
"\n",
@ -1070,10 +1098,11 @@ techage.manual_DE.aText = {
"\n"..
"\n"..
"\n",
"Eine Windkraftanlagen liefern immer dann Strom\\, wenn Wind vorhanden ist. Im Spiel gibt es keinen Wind\\, aber die Mod simuliert dies dadurch\\, dass sich nur morgens (5:00 - 9:00) und abends (17:00 - 21:00) die Windräder drehen und damit Strom liefern\\, sofern diese an geeigneten Stellen errichtet werden.\n"..
"Eine Windkraftanlage liefern immer dann Strom\\, wenn Wind vorhanden ist. Im Spiel gibt es keinen Wind\\, aber die Mod simuliert dies dadurch\\, dass sich nur morgens (5:00 - 9:00) und abends (17:00 - 21:00) die Windräder drehen. Eine Windkraftanlage liefert nur dann Strom\\, wenn sie an einer geeigneten Stelle aufgestellt ist.\n"..
"\n"..
"Die TA Windkraftanlagen sind reine Offshore Anlagen\\, das heißt\\, die müssen im Meer (Wasser) errichtet werden. Dies bedeutet\\, dass um den Mast herum mit einem Abstand von 20 Blöcken nur Wasser sein darf und das mindestens 2 Blöcke tief.\n"..
"Der Rotor muss in einer Höhe (Y-Koordinate) von 12 bis maximal 20 m platziert werden. Der Abstand zu weiteren Windkraftanlagen muss mindestens 14 m betragen.\n"..
"Die TA Windkraftanlagen sind reine Offshore Anlagen\\, das heißt\\, die müssen im Meer errichtet werden. Dies bedeutet\\, dass Windkraftanlagen nur in einem Meer (occean) Biom errichtet werden können und dass um den Mast herum ausreichend Wasser und freie Sicht vorhanden sein müssen.\n"..
"\n"..
"Um eine geeignete Stelle zu finden\\, musst du mit dem Schraubenschlüssel (TechAge Info Werkzeug) auf das Wasser klicken. Ob diese Stelle für den Mast der Windkraftanlage geeignet ist\\, wird dir als Chat Nachricht angezeigt.\n"..
"\n"..
"Der Strom muss vom Rotor-Block durch den Mast nach unten geführt werden. Dazu zuerst die Stromleitung nach oben ziehen und das Stromkabel dann mit TA4 Säulenblöcke \"verputzen\". Unten kann eine Arbeitsplattform errichtet werden. Der Plan rechts zeigt den Aufbau im oberen Teil.\n"..
"\n"..
@ -1160,7 +1189,7 @@ techage.manual_DE.aText = {
" - Hülle mit 7x7x7 Concrete Blocks\\, gefüllt mit 125 Gravel\\, Speicherkapazität: 2\\,5 Tage bei 60 ku\n"..
" - Hülle mit 9x9x9 Concrete Blocks\\, gefüllt mit 343 Gravel\\, Speicherkapazität: 6\\,5 Tage bei 60 ku\n"..
"\n"..
"In der Betonhülle darf ein Fenster aus einem Obsidian Glas Block sein. Dieses muss ziemlich in der Mitte der Wand platziert werden. Durch dieses Fenster sieht man\\, ob der Speicher mehr als 80 % geladen ist. Im Plan rechts sieht man den Aufbau aus TA4 Wärmetauscher bestehend aus 3 Blöcken\\, der TA4 Turbine und dem TA4 Generator. Beim Wärmetauscher ist auf die Ausrichtung achten (der Pfeil bei Block 1 muss zur Turbine zeigen).\n"..
"In der Betonhülle darf ein Fenster aus einem Obsidian Glas Block sein. Dieses muss ziemlich in der Mitte der Wand platziert werden. Durch dieses Fenster sieht man\\, ob der Speicher mehr als 80 % geladen ist. Im Plan rechts sieht man den Aufbau aus TA4 Wärmetauscher bestehend aus 3 Blöcken\\, der TA4 Turbine und dem TA4 Generator. Beim Wärmetauscher ist auf die Ausrichtung zu achten (der Pfeil bei Block 1 muss zur Turbine zeigen).\n"..
"\n"..
"Entgegen dem Plan rechts müssen die Anschlüsse am Speicherblock auf gleicher Ebene sein (horizontal angeordnet\\, also nicht unten und oben). Die Rohrzuläufe (TA4 Pipe Inlet) müssen genau in der Mitte der Wand sein und stehen sich damit gegenüber. Als Röhren kommen die gelbel TA4 Röhren zum Einsatz. Die TA3 Dampfrohre können hier nicht verwendet werden.\n"..
"Sowohl der Generator als auch der Wärmetauscher haben einen Stromanschluss und müssen mit dem Stromnetz verbunden werden.\n"..
@ -1349,9 +1378,9 @@ techage.manual_DE.aText = {
"Wird etwas in die Kiste gelegt\\, oder entnommen\\, oder eine der Tasten \"F1\"/\"F2\" gedrückt\\, so wird ein Event-Signal an den Lua Controller gesendet.\n"..
"Die Sensor Kiste unterstützt folgende Kommandos:\n"..
"\n"..
" - Über 'state = $read_data(<num>\\, \"state\")' kann der Status der Kiste abgefragt werden. Mögliche Antworten sind: \"empty\"\\, \"loaded\"\\, \"full\"\n"..
" - Über 'name\\, action = $read_data(<num>\\, \"action\")' kann die letzte Spieleraktion abgefragt werden. 'name' ist der Spielername\\, Als 'action' wird zurückgeliefert: \"put\"\\, \"take\"\\, \"f1\"\\, \"f2\".\n"..
" - Über 'stacks = $read_data(<num>\\, \"stacks\")' kann der Inhalt der Kiste ausgelesen werden. Siehe dazu: https://github.com/joe7575/techage/blob/master/manuals/ta4_lua_controller_EN.md#sensor-chest\n"..
" - Über 'state = $send_cmnd(<num>\\, \"state\")' kann der Status der Kiste abgefragt werden. Mögliche Antworten sind: \"empty\"\\, \"loaded\"\\, \"full\"\n"..
" - Über 'name\\, action = $send_cmnd(<num>\\, \"action\")' kann die letzte Spieleraktion abgefragt werden. 'name' ist der Spielername\\, Als 'action' wird zurückgeliefert: \"put\"\\, \"take\"\\, \"f1\"\\, \"f2\".\n"..
" - Über 'stacks = $send_cmnd(<num>\\, \"stacks\")' kann der Inhalt der Kiste ausgelesen werden. Siehe dazu: https://github.com/joe7575/techage/blob/master/manuals/ta4_lua_controller_EN.md#sensor-chest\n"..
" - Über '$send_cmnd(<num>\\, \"text\"\\, \"press both buttons andnput something into the chest\")' kann der Text im Menü der Sensor Kiste gesetzt werden.\n"..
"\n"..
"Über die Checkbox \"Erlaube öffentlichen Zugriff\" kann eingestellt werden\\, ob die Kiste von jedem genutzt werden darf\\, oder nur von Spielern die hier Zugriffsrechte haben.\n"..
@ -1497,7 +1526,7 @@ techage.manual_DE.aText = {
"\n"..
"Der Kiste besitzt ein zusätzliches Kommandos für den Lua Controller:\n"..
"\n"..
" - 'count' dient zur Anfrage\\, wie viele Items in der Kiste sind.\nBeispiel 1: '$read_data(CHEST\\, \"count\")' --> Summe der Items über alle 8 Speicher\nBeispiel 2: '$read_data(CHEST\\, \"count\"\\, 2)' --> Anzahl der Items in Speicher 2 (zweiter von links)\n"..
" - 'count' dient zur Anfrage\\, wie viele Items in der Kiste sind.\nBeispiel 1: '$send_cmnd(CHEST\\, \"count\")' --> Summe der Items über alle 8 Speicher\nBeispiel 2: '$send_cmnd(CHEST\\, \"count\"\\, 2)' --> Anzahl der Items in Speicher 2 (zweiter von links)\n"..
"\n"..
"\n"..
"\n",
@ -1572,6 +1601,7 @@ techage.manual_DE.aItemName = {
"ta2_flywheel",
"ta2_steampipe",
"ta2_driveaxle",
"ta2_generator",
"",
"tube",
"ta2_pusher",
@ -1610,6 +1640,8 @@ techage.manual_DE.aItemName = {
"ta3_tinygenerator",
"ta3_akkublock",
"ta3_powerterminal",
"ta3_motor",
"ta3_powerterminal",
"",
"ta3_furnacefirebox",
"ta3_furnace",
@ -1764,6 +1796,7 @@ techage.manual_DE.aPlanTable = {
"",
"",
"",
"",
"itemtransport",
"",
"",
@ -1802,6 +1835,8 @@ techage.manual_DE.aPlanTable = {
"",
"",
"",
"",
"",
"ta3_furnace",
"",
"",

View File

@ -28,6 +28,7 @@ techage.manual_EN.aTitel = {
"3,TA2 Flywheel",
"3,TA2 Steam Pipes",
"3,TA2 Drive Axle / TA2 Gearbox",
"3,TA2 Power Generator",
"2,Push and sort items",
"3,TechAge Tube",
"3,TA2 Pusher",
@ -66,6 +67,7 @@ techage.manual_EN.aTitel = {
"3,TA3 Small Power Generator",
"3,TA3 Battery Block",
"3,TA3 Power Terminal",
"3,TA3 Electric Motor",
"2,TA3 Industrial Furnace",
"3,TA3 Furnace Oil Burner",
"3,TA3 Furnace Top",
@ -399,6 +401,14 @@ techage.manual_EN.aText = {
"\n"..
"\n"..
"\n",
"The TA2 Power Generator is required to operate lamps or other power consumers on a steam engine. The TA2 Power Generator has to be connected to drive axles on one side and then supplies electricity on the other side.\n"..
"\n"..
"If the Power Generator is not supplied with sufficient power\\, it goes into an error state and must be reactivated with a right-click.\n"..
"\n"..
"The Power Generator takes max. 25 ku of axle power and provides on the other side max. 24 ku as electricity. So he consumes one ku for the conversion.\n"..
"\n"..
"\n"..
"\n",
"In order to transport objects from one processing station to the next\\, pushers and tubes are used. See plan.\n"..
"\n"..
"\n"..
@ -424,6 +434,8 @@ techage.manual_EN.aText = {
"\n"..
"The distributor has a menu with 4 filters with different colors\\, corresponding to the 4 outputs. If an output is to be used\\, the corresponding filter must be activated via the \"on\" checkbox. All items that are configured for this filter are output via the assigned output. If a filter is activated without items being configured\\, we are talking about an \"unconfigured\"\\, open output.\n"..
"\n"..
"*Attention: The distributor is also a pusher at its output sides. Therefore\\, never pull items out of the distributor with a pusher!*\n"..
"\n"..
"There are two operating modes for a non-configured output:\n"..
"\n"..
"1) Output all items that cannot be output to any other exit\\, even if they are blocked.\n"..
@ -649,6 +661,13 @@ techage.manual_EN.aText = {
"\n"..
"\n"..
"\n",
"The TA3 Electric Motor is required in order to be able to operate TA2 machines via the power grid. The TA3 Electric Motor converts electricity into axle power.\n"..
"If the electric motor is not supplied with sufficient power\\, it goes into an fault state and must be reactivated with a right-click.\n"..
"\n"..
"The electric motor takes max. 40 ku of electricity and provides on the other side max. 39 ku as axle power. So he consumes one ku for the conversion.\n"..
"\n"..
"\n"..
"\n",
"The TA3 industrial furnace serves as a supplement to normal furnaces. This means that all goods can be produced with \"cooking\" recipes\\, even in an industrial furnace. But there are also special recipes that can only be made in an industrial furnace.\n"..
"The industrial furnace has its own menu for recipe selection. Depending on the goods in the industrial furnace inventory on the left\\, the output product can be selected on the right.\n"..
"\n"..
@ -933,7 +952,9 @@ techage.manual_EN.aText = {
" - 'pub' switch to public mode\n"..
" - 'priv' switch to private mode\n"..
"\n"..
"In the private mode\\, only the owner can enter commands himself or use keys.\n"..
"In private mode\\, the terminal can only be used by players who can build at this location\\, i.e. who have protection rights.\n"..
"\n"..
"In public mode\\, all players can use the preconfigured keys.\n"..
"\n"..
"\n"..
"\n",
@ -1060,10 +1081,11 @@ techage.manual_EN.aText = {
"\n"..
"\n"..
"\n",
"A wind turbine always delivers electricity when there is wind. There is no wind in the game\\, but the mod simulates this by only turning the wind turbines in the morning (5:00 a.m. - 9:00 a.m.) and in the evening (5:00 p.m. - 9:00 p.m.) and thus supplying electricity\\, provided they are positioned appropriately.\n"..
"A wind turbine always supplies electricity when there is wind. There is no wind in the game\\, but the mod simulates this by turning the wind turbines only in the morning (5:00 - 9:00) and in the evening (17:00 - 21:00). A wind turbine only supplies electricity if it is set up in a suitable location.\n"..
"\n"..
"The TA wind turbines are pure offshore plants\\, which means that they have to be installed in the sea (water). This means that there must be in the minimum 20 blocks of water around the mast and at least 2 blocks deep.\n"..
"The rotor must be placed at a height (Y coordinate) of 12 to a maximum of 20 m. The distance to other wind turbines must be at least 14 m.\n"..
"The TA wind power plants are pure offshore plants\\, which means that they have to be built in the sea. This means that wind turbines can only be build in a sea (occean) biome and that there must be sufficient water and a clear view around the mast.\n"..
"\n"..
"To find a suitable spot\\, click on the water with the wrench (TechAge Info Tool). A chat message will show you whether this position is suitable for the mast of the wind turbine.\n"..
"\n"..
"The current must be led from the rotor block down through the mast. First pull the power line up and then \"plaster\" the power cable with TA4 pillar blocks. A work platform can be built below. The plan on the right shows the structure in the upper part.\n"..
"\n"..
@ -1340,9 +1362,9 @@ techage.manual_EN.aText = {
"If something is put into the box or removed\\, or one of the \"F1\" / \"F2\" keys is pressed\\, an event signal is sent to the Lua controller.\n"..
"The sensor box supports the following commands:\n"..
"\n"..
" - The status of the box can be queried via 'state = $read_data(<num>\\, \"state\")'. Possible answers are: \"empty\"\\, \"loaded\"\\, \"full\"\n"..
" - The last player action can be queried via 'name\\, action = $read_data(<num>\\, \"action\")'. 'name' is the player name. One of the following is returned as 'action': \"put\"\\, \"take\"\\, \"f1\"\\, \"f2\".\n"..
" - The contents of the box can be read out via 'stacks = $read_data(<num>\\, \"stacks\")'. See: https://github.com/joe7575/techage/blob/master/manuals/ta4_lua_controller_EN.md#sensor-chest\n"..
" - The status of the box can be queried via 'state = $send_cmnd(<num>\\, \"state\")'. Possible answers are: \"empty\"\\, \"loaded\"\\, \"full\"\n"..
" - The last player action can be queried via 'name\\, action = $send_cmnd(<num>\\, \"action\")'. 'name' is the player name. One of the following is returned as 'action': \"put\"\\, \"take\"\\, \"f1\"\\, \"f2\".\n"..
" - The contents of the box can be read out via 'stacks = $send_cmnd(<num>\\, \"stacks\")'. See: https://github.com/joe7575/techage/blob/master/manuals/ta4_lua_controller_EN.md#sensor-chest\n"..
" - Via '$send_cmnd(<num>\\, \"text\"\\, \"press both buttons andnput something into the chest\")' the text can be set in the menu of the sensor box.\n"..
"\n"..
"The checkbox \"Allow public chest access\" can be used to set whether the box can be used by everyone or only by players who have access/protection rights here.\n"..
@ -1471,7 +1493,7 @@ techage.manual_EN.aText = {
"\n"..
"If the chest is filled with a pusher\\, all stores fill from left to right. If all 8 stores are full and no further items can be added\\, further items are rejected.\n"..
"\n"..
"* Row function *\n"..
"*Row function*\n"..
"\n"..
"Several TA4 8x2000 chests can be connected to a large chest with more content. To do this\\, the chests must be placed in a row one after the other.\n"..
"\n"..
@ -1488,7 +1510,7 @@ techage.manual_EN.aText = {
"\n"..
"The chest has an additional command for the Lua controller:\n"..
"\n"..
" - 'count' is used to request how many items are in the chest.\nExample 1: '$read_data(CHEST\\, \"count\")' -> Sum of items across all 8 stores\nExample 2: '$read_data(CHEST\\, \"count\"\\, 2)' -> number of items in store 2 (second from left)\n"..
" - 'count' is used to request how many items are in the chest.\nExample 1: '$send_cmnd(CHEST\\, \"count\")' -> Sum of items across all 8 stores\nExample 2: '$send_cmnd(CHEST\\, \"count\"\\, 2)' -> number of items in store 2 (second from left)\n"..
"\n"..
"\n"..
"\n",
@ -1563,6 +1585,7 @@ techage.manual_EN.aItemName = {
"ta2_flywheel",
"ta2_steampipe",
"ta2_driveaxle",
"ta2_generator",
"",
"tube",
"ta2_pusher",
@ -1601,6 +1624,7 @@ techage.manual_EN.aItemName = {
"ta3_tinygenerator",
"ta3_akkublock",
"ta3_powerterminal",
"ta3_motor",
"",
"ta3_furnacefirebox",
"ta3_furnace",
@ -1755,6 +1779,7 @@ techage.manual_EN.aPlanTable = {
"",
"",
"",
"",
"itemtransport",
"",
"",
@ -1793,6 +1818,7 @@ techage.manual_EN.aPlanTable = {
"",
"",
"",
"",
"ta3_furnace",
"",
"",

View File

@ -185,7 +185,7 @@ local PN270 = {"techage_gaspipe_knee.png^[transformR270", "techage:ta4_pipeS"}
techage.ConstructionPlans["ta3_tank"] = {
{false, false, false, false, false, false, false, false, false, false},
{false, Tubes, PushR, Tubes, Fillr, Tubes, PushR, Tubes, false, false},
{false, false, false, false, TANK3, PIPEH, PIPEH, Pump, PIPEH, Tank},
{false, false, false, false, TANK3, PIPEH, PIPEH, Pump, PIPEH, false},
{false, false, false, false, false, false, false, false, false, false},
}

View File

@ -32,7 +32,7 @@ local function formspec(self, pos, nvm)
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
power.formspec_label_bar(0, 0.8, S("Electricity"), PWR_CAPA, nvm.provided)..
power.formspec_label_bar(pos, 0, 0.8, S("Electricity"), PWR_CAPA, nvm.provided)..
"image_button[2.8,2;1,1;".. self:get_state_button_image(nvm) ..";state_button;]"..
"tooltip[2.8,2;1,1;"..self:get_state_tooltip(nvm).."]"
end

View File

@ -44,8 +44,8 @@ local function formspec(self, pos, nvm)
default.gui_slots..
"box[0,-0.1;5.8,0.5;#c6e8ff]"..
"label[2,-0.1;"..minetest.colorize( "#000000", S("Heat Exchanger")).."]"..
power.formspec_label_bar(0, 0.8, S("Electricity"), needed_max, needed)..
power.formspec_label_bar(3.5, 0.8, S("Thermal"), capa_max, capa, "")..
power.formspec_label_bar(pos, 0, 0.8, S("Electricity"), needed_max, needed)..
power.formspec_label_bar(pos, 3.5, 0.8, S("Thermal"), capa_max, capa, "")..
arrow..
"image_button[2.5,3;1,1;".. self:get_state_button_image(nvm) ..";state_button;]"..
"tooltip[2.5,3;1,1;"..self:get_state_tooltip(nvm).."]"

View File

@ -111,7 +111,7 @@ local function process(inv, recipe, output)
if recipe.waste then
local leftover = inv:add_item("dst", ItemStack(recipe.waste))
if leftover:get_count() > 0 then
inv:set_list("src", leftover)
inv:add_item("src", leftover)
return techage.BLOCKED
end
end

View File

@ -242,7 +242,12 @@ local tubing = {
end
end,
on_recv_message = function(pos, src, topic, payload)
return CRD(pos).State:on_receive_message(pos, topic, payload)
if topic == "output" then
local nvm = techage.get_nvm(pos)
return string.split(nvm.output or "unknown", " ")[1]
else
return CRD(pos).State:on_receive_message(pos, topic, payload)
end
end,
}

View File

@ -44,7 +44,7 @@ local function formspec(self, pos, nvm)
default.gui_slots..
"box[0,-0.1;5.8,0.5;#c6e8ff]"..
"label[2.5,-0.1;"..minetest.colorize( "#000000", S("Electrolyzer")).."]"..
techage.power.formspec_label_bar(0.1, 0.8, S("Electricity"), PWR_NEEDED, nvm.taken)..
techage.power.formspec_label_bar(pos, 0.1, 0.8, S("Electricity"), PWR_NEEDED, nvm.taken)..
arrow..
"image_button[3,2.5;1,1;".. self:get_state_button_image(nvm) ..";state_button;]"..
"tooltip[3,2.5;1,1;"..self:get_state_tooltip(nvm).."]"..
@ -156,6 +156,15 @@ local function after_dig_node(pos, oldnode, oldmetadata, digger)
Cable:after_dig_node(pos)
end
local function put(pos, indir, name, amount)
local leftover = liquid.srv_put(pos, indir, name, amount)
if techage.is_activeformspec(pos) then
local nvm = techage.get_nvm(pos)
M(pos):set_string("formspec", formspec(State, pos, nvm))
end
return leftover
end
local function tubelib2_on_update2(pos, outdir, tlib2, node)
if tlib2.tube_type == "pipe2" then
liquid.update_network(pos, outdir, tlib2)
@ -182,14 +191,8 @@ local netw_def = {
local liquid_def = {
capa = CAPACITY,
peek = liquid.srv_peek,
put = function(pos, indir, name, amount)
local leftover = liquid.srv_put(pos, indir, name, amount)
if techage.is_activeformspec(pos) then
local nvm = techage.get_nvm(pos)
M(pos):set_string("formspec", formspec(State, pos, nvm))
end
return leftover
end,
put = put,
untake = put,
take = function(pos, indir, name, amount)
amount, name = liquid.srv_take(pos, indir, name, amount)
if techage.is_activeformspec(pos) then

View File

@ -48,7 +48,7 @@ local function formspec(self, pos, nvm)
arrow..
"image_button[2,2.5;1,1;".. self:get_state_button_image(nvm) ..";state_button;]"..
"tooltip[2,2.5;1,1;"..self:get_state_tooltip(nvm).."]"..
techage.power.formspec_label_bar(3.5, 0.8, S("Electricity"), PWR_CAPA, nvm.given)
techage.power.formspec_label_bar(pos, 3.5, 0.8, S("Electricity"), PWR_CAPA, nvm.given)
end
local function start_node(pos, nvm, state)

View File

@ -1,79 +1,448 @@
#!/usr/bin/env python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Script to generate the template file and update the translation files.
# Copy the script into the mod or modpack root folder and run it there.
#
# Copyright (C) 2019 Joachim Stolberg
# Copyright (C) 2019 Joachim Stolberg, 2020 FaceDeer, 2020 Louis Royer
# LGPLv2.1+
#
# Copy the script into the mod root folder and adapt the last code lines to you needs.
#
# See https://github.com/minetest-tools/update_translations for
# potential future updates to this script.
from __future__ import print_function
import os, fnmatch, re, shutil
import os, fnmatch, re, shutil, errno
from sys import argv as _argv
from sys import stderr as _stderr
pattern_lua = re.compile(r'[ \.=^\t]S\("(.+?)"\)', re.DOTALL)
pattern_tr = re.compile(r'(.+?[^@])=(.+)')
# Running params
params = {"recursive": False,
"help": False,
"mods": False,
"verbose": False,
"folders": [],
"no-old-file": False
}
# Available CLI options
options = {"recursive": ['--recursive', '-r'],
"help": ['--help', '-h'],
"mods": ['--installed-mods'],
"verbose": ['--verbose', '-v'],
"no-old-file": ['--no-old-file']
}
def gen_template(templ_file, lkeyStrings):
lOut = []
lkeyStrings.sort()
for s in lkeyStrings:
lOut.append("%s=" % s)
open(templ_file, "wt").write("\n".join(lOut))
# Strings longer than this will have extra space added between
# them in the translation files to make it easier to distinguish their
# beginnings and endings at a glance
doublespace_threshold = 60
def set_params_folders(tab: list):
'''Initialize params["folders"] from CLI arguments.'''
# Discarding argument 0 (tool name)
for param in tab[1:]:
stop_param = False
for option in options:
if param in options[option]:
stop_param = True
break
if not stop_param:
params["folders"].append(os.path.abspath(param))
def set_params(tab: list):
'''Initialize params from CLI arguments.'''
for option in options:
for option_name in options[option]:
if option_name in tab:
params[option] = True
break
def print_help(name):
'''Prints some help message.'''
print(f'''SYNOPSIS
{name} [OPTIONS] [PATHS...]
DESCRIPTION
{', '.join(options["help"])}
prints this help message
{', '.join(options["recursive"])}
run on all subfolders of paths given
{', '.join(options["mods"])}
run on locally installed modules
{', '.join(options["no-old-file"])}
do not create *.old files
{', '.join(options["verbose"])}
add output information
''')
def main():
'''Main function'''
set_params(_argv)
set_params_folders(_argv)
if params["help"]:
print_help(_argv[0])
elif params["recursive"] and params["mods"]:
print("Option --installed-mods is incompatible with --recursive")
else:
# Add recursivity message
print("Running ", end='')
if params["recursive"]:
print("recursively ", end='')
# Running
if params["mods"]:
print(f"on all locally installed modules in {os.path.abspath('~/.minetest/mods/')}")
run_all_subfolders("~/.minetest/mods")
elif len(params["folders"]) >= 2:
print("on folder list:", params["folders"])
for f in params["folders"]:
if params["recursive"]:
run_all_subfolders(f)
else:
update_folder(f)
elif len(params["folders"]) == 1:
print("on folder", params["folders"][0])
if params["recursive"]:
run_all_subfolders(params["folders"][0])
else:
update_folder(params["folders"][0])
else:
print("on folder", os.path.abspath("./"))
if params["recursive"]:
run_all_subfolders(os.path.abspath("./"))
else:
update_folder(os.path.abspath("./"))
#group 2 will be the string, groups 1 and 3 will be the delimiters (" or ')
#See https://stackoverflow.com/questions/46967465/regex-match-text-in-either-single-or-double-quote
pattern_lua_s = re.compile(r'[\.=^\t,{\(\s]N?S\(\s*(["\'])((?:\\\1|(?:(?!\1)).)*)(\1)[\s,\)]', re.DOTALL)
pattern_lua_fs = re.compile(r'[\.=^\t,{\(\s]N?FS\(\s*(["\'])((?:\\\1|(?:(?!\1)).)*)(\1)[\s,\)]', re.DOTALL)
pattern_lua_bracketed_s = re.compile(r'[\.=^\t,{\(\s]N?S\(\s*\[\[(.*?)\]\][\s,\)]', re.DOTALL)
pattern_lua_bracketed_fs = re.compile(r'[\.=^\t,{\(\s]N?FS\(\s*\[\[(.*?)\]\][\s,\)]', re.DOTALL)
# Handles "concatenation" .. " of strings"
pattern_concat = re.compile(r'["\'][\s]*\.\.[\s]*["\']', re.DOTALL)
pattern_tr = re.compile(r'(.*?[^@])=(.*)')
pattern_name = re.compile(r'^name[ ]*=[ ]*([^ \n]*)')
pattern_tr_filename = re.compile(r'\.tr$')
pattern_po_language_code = re.compile(r'(.*)\.po$')
#attempt to read the mod's name from the mod.conf file. Returns None on failure
def get_modname(folder):
try:
with open(os.path.join(folder, "mod.conf"), "r", encoding='utf-8') as mod_conf:
for line in mod_conf:
match = pattern_name.match(line)
if match:
return match.group(1)
except FileNotFoundError:
pass
return None
#If there are already .tr files in /locale, returns a list of their names
def get_existing_tr_files(folder):
out = []
for root, dirs, files in os.walk(os.path.join(folder, 'locale/')):
for name in files:
if pattern_tr_filename.search(name):
out.append(name)
return out
# A series of search and replaces that massage a .po file's contents into
# a .tr file's equivalent
def process_po_file(text):
# The first three items are for unused matches
text = re.sub(r'#~ msgid "', "", text)
text = re.sub(r'"\n#~ msgstr ""\n"', "=", text)
text = re.sub(r'"\n#~ msgstr "', "=", text)
# comment lines
text = re.sub(r'#.*\n', "", text)
# converting msg pairs into "=" pairs
text = re.sub(r'msgid "', "", text)
text = re.sub(r'"\nmsgstr ""\n"', "=", text)
text = re.sub(r'"\nmsgstr "', "=", text)
# various line breaks and escape codes
text = re.sub(r'"\n"', "", text)
text = re.sub(r'"\n', "\n", text)
text = re.sub(r'\\"', '"', text)
text = re.sub(r'\\n', '@n', text)
# remove header text
text = re.sub(r'=Project-Id-Version:.*\n', "", text)
# remove double-spaced lines
text = re.sub(r'\n\n', '\n', text)
return text
# Go through existing .po files and, if a .tr file for that language
# *doesn't* exist, convert it and create it.
# The .tr file that results will subsequently be reprocessed so
# any "no longer used" strings will be preserved.
# Note that "fuzzy" tags will be lost in this process.
def process_po_files(folder, modname):
for root, dirs, files in os.walk(os.path.join(folder, 'locale/')):
for name in files:
code_match = pattern_po_language_code.match(name)
if code_match == None:
continue
language_code = code_match.group(1)
tr_name = modname + "." + language_code + ".tr"
tr_file = os.path.join(root, tr_name)
if os.path.exists(tr_file):
if params["verbose"]:
print(f"{tr_name} already exists, ignoring {name}")
continue
fname = os.path.join(root, name)
with open(fname, "r", encoding='utf-8') as po_file:
if params["verbose"]:
print(f"Importing translations from {name}")
text = process_po_file(po_file.read())
with open(tr_file, "wt", encoding='utf-8') as tr_out:
tr_out.write(text)
# from https://stackoverflow.com/questions/600268/mkdir-p-functionality-in-python/600612#600612
# Creates a directory if it doesn't exist, silently does
# nothing if it already exists
def mkdir_p(path):
try:
os.makedirs(path)
except OSError as exc: # Python >2.5
if exc.errno == errno.EEXIST and os.path.isdir(path):
pass
else: raise
# Converts the template dictionary to a text to be written as a file
# dKeyStrings is a dictionary of localized string to source file sets
# dOld is a dictionary of existing translations and comments from
# the previous version of this text
def strings_to_text(dkeyStrings, dOld, mod_name, header_comments):
lOut = [f"# textdomain: {mod_name}\n"]
if header_comments is not None:
lOut.append(header_comments)
dGroupedBySource = {}
for key in dkeyStrings:
sourceList = list(dkeyStrings[key])
sourceList.sort()
sourceString = "\n".join(sourceList)
listForSource = dGroupedBySource.get(sourceString, [])
listForSource.append(key)
dGroupedBySource[sourceString] = listForSource
lSourceKeys = list(dGroupedBySource.keys())
lSourceKeys.sort()
for source in lSourceKeys:
localizedStrings = dGroupedBySource[source]
localizedStrings.sort()
lOut.append("")
lOut.append(source)
lOut.append("")
for localizedString in localizedStrings:
val = dOld.get(localizedString, {})
translation = val.get("translation", "")
comment = val.get("comment")
if len(localizedString) > doublespace_threshold and not lOut[-1] == "":
lOut.append("")
if comment != None:
lOut.append(comment)
lOut.append(f"{localizedString}={translation}")
if len(localizedString) > doublespace_threshold:
lOut.append("")
unusedExist = False
for key in dOld:
if key not in dkeyStrings:
val = dOld[key]
translation = val.get("translation")
comment = val.get("comment")
# only keep an unused translation if there was translated
# text or a comment associated with it
if translation != None and (translation != "" or comment):
if not unusedExist:
unusedExist = True
lOut.append("\n\n##### not used anymore #####\n")
if len(key) > doublespace_threshold and not lOut[-1] == "":
lOut.append("")
if comment != None:
lOut.append(comment)
lOut.append(f"{key}={translation}")
if len(key) > doublespace_threshold:
lOut.append("")
return "\n".join(lOut) + '\n'
# Writes a template.txt file
# dkeyStrings is the dictionary returned by generate_template
def write_template(templ_file, dkeyStrings, mod_name):
# read existing template file to preserve comments
existing_template = import_tr_file(templ_file)
text = strings_to_text(dkeyStrings, existing_template[0], mod_name, existing_template[2])
mkdir_p(os.path.dirname(templ_file))
with open(templ_file, "wt", encoding='utf-8') as template_file:
template_file.write(text)
# Gets all translatable strings from a lua file
def read_lua_file_strings(lua_file):
lOut = []
text = open(lua_file).read()
for s in pattern_lua.findall(text):
s = re.sub(r'"\.\.\s+"', "", s)
s = re.sub("@[^@=n]", "@@", s)
s = s.replace("\n", "@n")
s = s.replace("\\n", "@n")
s = s.replace("=", "@=")
lOut.append(s)
with open(lua_file, encoding='utf-8') as text_file:
text = text_file.read()
#TODO remove comments here
text = re.sub(pattern_concat, "", text)
strings = []
for s in pattern_lua_s.findall(text):
strings.append(s[1])
for s in pattern_lua_bracketed_s.findall(text):
strings.append(s)
for s in pattern_lua_fs.findall(text):
strings.append(s[1])
for s in pattern_lua_bracketed_fs.findall(text):
strings.append(s)
for s in strings:
s = re.sub(r'"\.\.\s+"', "", s)
s = re.sub("@[^@=0-9]", "@@", s)
s = s.replace('\\"', '"')
s = s.replace("\\'", "'")
s = s.replace("\n", "@n")
s = s.replace("\\n", "@n")
s = s.replace("=", "@=")
lOut.append(s)
return lOut
def inport_tr_file(tr_file):
# Gets strings from an existing translation file
# returns both a dictionary of translations
# and the full original source text so that the new text
# can be compared to it for changes.
# Returns also header comments in the third return value.
def import_tr_file(tr_file):
dOut = {}
text = None
header_comment = None
if os.path.exists(tr_file):
for line in open(tr_file, "r").readlines():
s = line.strip()
if s == "" or s[0] == "#":
continue
match = pattern_tr.match(s)
if match:
dOut[match.group(1)] = match.group(2)
return dOut
with open(tr_file, "r", encoding='utf-8') as existing_file :
# save the full text to allow for comparison
# of the old version with the new output
text = existing_file.read()
existing_file.seek(0)
# a running record of the current comment block
# we're inside, to allow preceeding multi-line comments
# to be retained for a translation line
latest_comment_block = None
for line in existing_file.readlines():
line = line.rstrip('\n')
if line[:3] == "###":
if header_comment is None:
# Save header comments
header_comment = latest_comment_block or ""
# Stip textdomain line
tmp_h_c = ""
for l in header_comment.split('\n'):
if not l.startswith("# textdomain:"):
tmp_h_c += l + '\n'
header_comment = tmp_h_c
def generate_template(templ_file):
lOut = []
for root, dirs, files in os.walk('./'):
# Reset comment block if we hit a header
latest_comment_block = None
continue
if line[:1] == "#":
# Save the comment we're inside
if not latest_comment_block:
latest_comment_block = line
else:
latest_comment_block = latest_comment_block + "\n" + line
continue
match = pattern_tr.match(line)
if match:
# this line is a translated line
outval = {}
outval["translation"] = match.group(2)
if latest_comment_block:
# if there was a comment, record that.
outval["comment"] = latest_comment_block
latest_comment_block = None
dOut[match.group(1)] = outval
return (dOut, text, header_comment)
# Walks all lua files in the mod folder, collects translatable strings,
# and writes it to a template.txt file
# Returns a dictionary of localized strings to source file sets
# that can be used with the strings_to_text function.
def generate_template(folder, mod_name):
dOut = {}
for root, dirs, files in os.walk(folder):
for name in files:
if fnmatch.fnmatch(name, "*.lua"):
fname = os.path.join(root, name)
found = read_lua_file_strings(fname)
print(fname, len(found))
lOut.extend(found)
lOut = list(set(lOut))
lOut.sort()
gen_template(templ_file, lOut)
return lOut
if params["verbose"]:
print(f"{fname}: {str(len(found))} translatable strings")
def update_tr_file(lNew, mod_name, tr_file):
lOut = ["# textdomain: %s\n" % mod_name]
if os.path.exists(tr_file):
shutil.copyfile(tr_file, tr_file+".old")
dOld = inport_tr_file(tr_file)
for key in lNew:
val = dOld.get(key, "")
lOut.append("%s=%s" % (key, val))
lOut.append("##### not used anymore #####")
for key in dOld:
if key not in lNew:
lOut.append("%s=%s" % (key, dOld[key]))
open(tr_file, "w").write("\n".join(lOut))
data = generate_template("./locale/template.txt")
update_tr_file(data, "techage", "./locale/techage.de.tr")
#update_tr_file(data, "techage", "./locale/techage.fr.tr")
print("Done.\n")
for s in found:
sources = dOut.get(s, set())
sources.add(f"### {os.path.basename(fname)} ###")
dOut[s] = sources
if len(dOut) == 0:
return None
templ_file = os.path.join(folder, "locale/template.txt")
write_template(templ_file, dOut, mod_name)
return dOut
# Updates an existing .tr file, copying the old one to a ".old" file
# if any changes have happened
# dNew is the data used to generate the template, it has all the
# currently-existing localized strings
def update_tr_file(dNew, mod_name, tr_file):
if params["verbose"]:
print(f"updating {tr_file}")
tr_import = import_tr_file(tr_file)
dOld = tr_import[0]
textOld = tr_import[1]
textNew = strings_to_text(dNew, dOld, mod_name, tr_import[2])
if textOld and textOld != textNew:
print(f"{tr_file} has changed.")
if not params["no-old-file"]:
shutil.copyfile(tr_file, f"{tr_file}.old")
with open(tr_file, "w", encoding='utf-8') as new_tr_file:
new_tr_file.write(textNew)
# Updates translation files for the mod in the given folder
def update_mod(folder):
modname = get_modname(folder)
if modname is not None:
process_po_files(folder, modname)
print(f"Updating translations for {modname}")
data = generate_template(folder, modname)
if data == None:
print(f"No translatable strings found in {modname}")
else:
for tr_file in get_existing_tr_files(folder):
update_tr_file(data, modname, os.path.join(folder, "locale/", tr_file))
else:
print(f"\033[31mUnable to find modname in folder {folder}.\033[0m", file=_stderr)
exit(1)
# Determines if the folder being pointed to is a mod or a mod pack
# and then runs update_mod accordingly
def update_folder(folder):
is_modpack = os.path.exists(os.path.join(folder, "modpack.txt")) or os.path.exists(os.path.join(folder, "modpack.conf"))
if is_modpack:
subfolders = [f.path for f in os.scandir(folder) if f.is_dir()]
for subfolder in subfolders:
update_mod(subfolder + "/")
else:
update_mod(folder)
print("Done.")
def run_all_subfolders(folder):
for modfolder in [f.path for f in os.scandir(folder) if f.is_dir()]:
update_folder(modfolder + "/")
main()

View File

@ -385,13 +385,9 @@ minetest.register_node("techage:ta4_icta_controller", {
on_receive_fields = on_receive_fields,
on_dig = function(pos, node, puncher, pointed_thing)
if minetest.is_protected(pos, puncher:get_player_name()) then
return
end
techage.remove_node(pos)
minetest.node_dig(pos, node, puncher, pointed_thing)
after_dig_node = function(pos, oldnode, oldmetadata)
techage.remove_node(pos, oldnode, oldmetadata)
techage.del_mem(pos)
end,
on_timer = on_timer,

View File

@ -14,6 +14,8 @@
local S = techage.S
techage.display = {}
local NUM_ROWS = 5
local RADIUS = 6
local Param2ToFacedir = {[0] = 0, 0, 3, 1, 2, 0}
@ -33,7 +35,7 @@ local function lcdlib_bugfix(text_tbl)
return ""
end
local function display_update(pos, objref)
function techage.display.display_update(pos, objref)
pos = vector.round(pos)
local nvm = techage.get_nvm(pos)
local text = lcdlib_bugfix(nvm.text)
@ -44,7 +46,7 @@ local function display_update(pos, objref)
visual_size = {x=0.94, y=0.94} })
end
local function display_updateXL(pos, objref)
function techage.display.display_updateXL(pos, objref)
pos = vector.round(pos)
local nvm = techage.get_nvm(pos)
local text = lcdlib_bugfix(nvm.text)
@ -55,7 +57,7 @@ local function display_updateXL(pos, objref)
visual_size = {x=0.94*1.9, y=0.94} })
end
local function on_timer(pos)
function techage.display.on_timer(pos)
local mem = techage.get_mem(pos)
mem.ticks = mem.ticks or 0
@ -77,7 +79,7 @@ local function on_timer(pos)
return true
end
local lcd_box = {
techage.display.lcd_box = {
type = "wallmounted",
wall_top = {-8/16, 15/32, -8/16, 8/16, 8/16, 8/16}
}
@ -90,13 +92,13 @@ minetest.register_node("techage:ta4_display", {
paramtype = "light",
sunlight_propagates = true,
paramtype2 = "wallmounted",
node_box = lcd_box,
selection_box = lcd_box,
node_box = techage.display.lcd_box,
selection_box = techage.display.lcd_box,
light_source = 6,
display_entities = {
["techage:display_entity"] = { depth = 0.42,
on_display_update = display_update},
on_display_update = techage.display.display_update},
},
after_place_node = function(pos, placer)
@ -114,7 +116,7 @@ minetest.register_node("techage:ta4_display", {
techage.remove_node(pos, oldnode, oldmetadata)
end,
on_timer = on_timer,
on_timer = techage.display.on_timer,
on_place = lcdlib.on_place,
on_construct = lcdlib.on_construct,
on_destruct = lcdlib.on_destruct,
@ -124,7 +126,7 @@ minetest.register_node("techage:ta4_display", {
sounds = default.node_sound_glass_defaults(),
})
local lcd_boxXL = {
techage.display.lcd_boxXL = {
type = "fixed",
fixed = {-0.9, -8/16, -8/16, 0.9, -15/32, 8/16}
}
@ -137,13 +139,13 @@ minetest.register_node("techage:ta4_displayXL", {
paramtype = "light",
sunlight_propagates = true,
paramtype2 = "wallmounted",
node_box = lcd_boxXL,
selection_box = lcd_boxXL,
node_box = techage.display.lcd_boxXL,
selection_box = techage.display.lcd_boxXL,
light_source = 6,
display_entities = {
["techage:display_entityXL"] = { depth = 0.42,
on_display_update = display_updateXL},
on_display_update = techage.display.display_updateXL},
},
after_place_node = function(pos, placer)
@ -161,7 +163,7 @@ minetest.register_node("techage:ta4_displayXL", {
techage.remove_node(pos, oldnode, oldmetadata)
end,
on_timer = on_timer,
on_timer = techage.display.on_timer,
on_place = lcdlib.on_place,
on_construct = lcdlib.on_construct,
on_destruct = lcdlib.on_destruct,
@ -189,7 +191,7 @@ minetest.register_craft({
},
})
local function add_line(pos, payload, cycle_time)
function techage.display.add_line(pos, payload, cycle_time)
local nvm = techage.get_nvm(pos)
local mem = techage.get_mem(pos)
nvm.text = nvm.text or {}
@ -206,7 +208,7 @@ local function add_line(pos, payload, cycle_time)
table.insert(nvm.text, payload)
end
local function write_row(pos, payload, cycle_time)
function techage.display.write_row(pos, payload, cycle_time)
local nvm = techage.get_nvm(pos)
local mem = techage.get_mem(pos)
nvm.text = nvm.text or {}
@ -227,7 +229,7 @@ local function write_row(pos, payload, cycle_time)
nvm.text[row] = str
end
local function clear_screen(pos, cycle_time)
function techage.display.clear_screen(pos, cycle_time)
local nvm = techage.get_nvm(pos)
local mem = techage.get_mem(pos)
mem.ticks = mem.ticks or 0
@ -242,11 +244,11 @@ end
techage.register_node({"techage:ta4_display"}, {
on_recv_message = function(pos, src, topic, payload)
if topic == "add" then -- add one line and scroll if necessary
add_line(pos, payload, 1)
techage.display.add_line(pos, payload, 1)
elseif topic == "set" then -- overwrite the given row
write_row(pos, payload, 1)
techage.display.write_row(pos, payload, 1)
elseif topic == "clear" then -- clear the screen
clear_screen(pos, 1)
techage.display.clear_screen(pos, 1)
end
end,
})
@ -254,14 +256,14 @@ techage.register_node({"techage:ta4_display"}, {
techage.register_node({"techage:ta4_displayXL"}, {
on_recv_message = function(pos, src, topic, payload)
if topic == "add" then -- add one line and scroll if necessary
add_line(pos, payload, 2)
techage.display.add_line(pos, payload, 2)
elseif topic == "set" then -- overwrite the given row
write_row(pos, payload, 2)
techage.display.write_row(pos, payload, 2)
elseif topic == "clear" then -- clear the screen
clear_screen(pos, 2)
techage.display.clear_screen(pos, 2)
end
end,
})
})
lcdlib.register_display_entity("techage:display_entity")
lcdlib.register_display_entity("techage:display_entityXL")

View File

@ -13,7 +13,7 @@
techage = {}
-- Version for compatibility checks, see readme.md/history
techage.version = 0.23
techage.version = 0.25
if minetest.global_exists("tubelib") then
minetest.log("error", "[techage] Techage can't be used together with the mod tubelib!")
@ -217,6 +217,8 @@ dofile(MP.."/oil/reboiler.lua")
-- TA3 power based
dofile(MP.."/ta3_power/tiny_generator.lua")
dofile(MP.."/ta3_power/akkubox.lua")
dofile(MP.."/ta3_power/axle2power.lua")
dofile(MP.."/ta3_power/power2axle.lua")
-- Digtron
if minetest.global_exists("digtron") then

View File

@ -97,6 +97,7 @@ end
local function flame(pos, height, heat, first_time)
local idx
local playername = minetest.get_meta(pos):get_string("playername")
pos = {x=pos.x, y=pos.y+height, z=pos.z}
for idx=heat,1,-1 do
pos = {x=pos.x, y=pos.y+1, z=pos.z}
@ -113,6 +114,9 @@ local function flame(pos, height, heat, first_time)
end
return
end
if minetest.is_protected(pos, playername) then
return
end
minetest.add_node(pos, {name = "techage:flame"..math.min(idx,7)})
local meta = minetest.get_meta(pos)
meta:set_int("heat", idx)

View File

@ -46,7 +46,7 @@ techage.recipes.add("ta4_doser", {
-- Hydrogenate
techage.recipes.add("ta4_doser", {
output = "techage:bitumen 2",
output = "techage:bitumen 1",
input = {
"techage:fueloil 1",
"techage:hydrogen 1",
@ -55,7 +55,7 @@ techage.recipes.add("ta4_doser", {
})
techage.recipes.add("ta4_doser", {
output = "techage:fueloil 2",
output = "techage:fueloil 1",
input = {
"techage:naphtha 1",
"techage:hydrogen 1",
@ -64,7 +64,7 @@ techage.recipes.add("ta4_doser", {
})
techage.recipes.add("ta4_doser", {
output = "techage:naphtha 2",
output = "techage:naphtha 1",
input = {
"techage:gasoline 1",
"techage:hydrogen 1",

View File

@ -20,6 +20,7 @@ local Cable = techage.ElectricCable
local power = techage.power
local Flowers = {}
local Plants = {}
local Ignore = { ["flowers:waterlily_waving"] = true }
-- 9 plant positions below the light
local Positions = {
{x = 0, y =-1, z = 0},
@ -143,7 +144,9 @@ minetest.after(1, function()
if name and type(name) == "string" then
local mod = string.split(name, ":")[1]
if mod == "flowers" or mod == "bakedclay" then -- Bakedclay also registers flowers as decoration.
techage.register_flower(name)
if not Ignore[name] then
techage.register_flower(name)
end
end
end
end
@ -151,7 +154,9 @@ minetest.after(1, function()
if type(name) == "string" then
local mod = string.split(name, ":")[1]
if mod == "farming" and ndef.on_timer then -- probably a plant that still needs to grow
techage.register_plant(name)
if not Ignore[name] then
techage.register_plant(name)
end
end
end
end

View File

@ -217,6 +217,14 @@ minetest.register_node("techage:ta4_tank", {
tubelib2_on_update2 = function(pos, outdir, tlib2, node)
liquid.update_network(pos, outdir)
end,
on_receive_fields = function(pos, formname, fields, player)
if minetest.is_protected(pos, player:get_player_name()) then
return
end
if fields.public then
M(pos):set_int("public", fields.public == "true" and 1 or 0)
end
end,
on_timer = node_timer,
on_punch = liquid.on_punch,
after_dig_node = function(pos, oldnode, oldmetadata, digger)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -222,16 +222,17 @@ minetest.register_node("techage:ta3_sequencer", {
on_receive_fields = on_receive_fields,
on_dig = function(pos, node, puncher, pointed_thing)
can_dig = function(pos, puncher)
if minetest.is_protected(pos, puncher:get_player_name()) then
return
return false
end
local nvm = techage.get_nvm(pos)
if not nvm.running then
techage.remove_node(pos)
minetest.node_dig(pos, node, puncher, pointed_thing)
techage.del_mem(pos)
end
return not nvm.running
end,
after_dig_node = function(pos, oldnode, oldmetadata)
techage.remove_node(pos, oldnode, oldmetadata)
techage.del_mem(pos)
end,
on_timer = check_rules,

View File

@ -15,41 +15,23 @@
local M = minetest.get_meta
local S = techage.S
local HELP_TA3 = S("#### TA3 Terminal ####@n"..
"@n"..
"Send commands to your machines@n"..
"and output text messages from your@n"..
"machines to the Terminal.@n"..
"@n"..
"Command syntax:@n"..
" cmd <num> <cmnd>@n"..
"@n"..
"example: cmd 181 on@n"..
"<num> is the number of the node to which the command is sent@n"..
"'on' is the command to turn machines/nodes on@n"..
"Further commands can be retrieved by clicking on@n"..
"machines/nodes with the Techage Info Tool.@n"..
"@n"..
"Local commands:@n"..
"- clear = clear screen@n"..
"- help = this message@n"..
"- pub = switch to public use@n"..
"- priv = switch to private use@n"..
"To program a user button with a command:@n"..
" set <button-num> <button-text> <command>@n"..
"e.g. 'set 1 ON cmd 123 on'@n")
local HELP_TA3 = "Syntax:\n"..
" cmd <num> <cmnd>\n"..
"\n"..
"like: cmd 181 on\n"..
"or: cmd 4573 state\n"..
"\n"..
"Local commands:\n"..
"- clear = clear screen\n"..
"- help = this message\n"..
"- pub = switch to public use of buttons\n"..
"- priv = switch to private use of buttons\n"..
"To program a user button with a command:\n"..
" set <button-num> <button-text> <command>\n"..
"e.g.: set 1 ON cmd 123 on"
local CMNDS_TA3 = S("Syntax error, try help")
--local function formspec1()
-- return "size[6,4]"..
-- default.gui_bg..
-- default.gui_bg_img..
-- default.gui_slots..
-- "field[0.5,1;5,1;number;Techage Controller number:;]" ..
-- "button_exit[1.5,2.5;2,1;exit;Save]"
--end
local function get_string(meta, num, default)
local s = meta:get_string("bttn_text"..num)
if not s or s == "" then
@ -106,6 +88,7 @@ end
local function command(pos, command, player)
local meta = minetest.get_meta(pos)
local owner = meta:get_string("owner") or ""
if command then
command = command:sub(1,80)
command = string.trim(command)
@ -117,16 +100,15 @@ local function command(pos, command, player)
local meta = minetest.get_meta(pos)
meta:set_string("output", HELP_TA3)
meta:set_string("formspec", formspec2(meta))
elseif command == "pub" and owner == player then
elseif command == "pub" then
meta:set_int("public", 1)
output(pos, player..":$ "..command)
output(pos, S("Switched to public use!"))
elseif command == "priv" and owner == player then
elseif command == "priv" then
meta:set_int("public", 0)
output(pos, player..":$ "..command)
output(pos, S("Switched to private use!"))
elseif meta:get_int("public") == 1 or owner == player or
minetest.check_player_privs(player, "server") then
else
output(pos, "$ "..command)
local own_num = meta:get_string("node_number")
local num, cmnd, payload = command:match('^cmd%s+([0-9]+)%s+(%w+)%s*(.*)$')
@ -206,24 +188,34 @@ local function register_terminal(num, tiles, node_box, selection_box)
on_receive_fields = function(pos, formname, fields, player)
local meta = minetest.get_meta(pos)
local evt = minetest.explode_table_event(fields.output)
if evt.type == "DCL" then
local s = get_line_text(pos, evt.row)
meta:set_string("command", s)
meta:set_string("formspec", formspec2(meta))
elseif (fields.key_enter == "true" or fields.ok) and fields.cmnd ~= "" then
command(pos, fields.cmnd, player:get_player_name())
meta:set_string("command", "")
meta:set_string("formspec", formspec2(meta))
elseif fields.bttn1 then send_cmnd(pos, meta, 1)
elseif fields.bttn2 then send_cmnd(pos, meta, 2)
elseif fields.bttn3 then send_cmnd(pos, meta, 3)
elseif fields.bttn4 then send_cmnd(pos, meta, 4)
elseif fields.bttn5 then send_cmnd(pos, meta, 5)
elseif fields.bttn6 then send_cmnd(pos, meta, 6)
elseif fields.bttn7 then send_cmnd(pos, meta, 7)
elseif fields.bttn8 then send_cmnd(pos, meta, 8)
elseif fields.bttn9 then send_cmnd(pos, meta, 9)
local public = meta:get_int("public") == 1
local protected = minetest.is_protected(pos, player:get_player_name())
if not protected then
local evt = minetest.explode_table_event(fields.output)
if evt.type == "DCL" then
local s = get_line_text(pos, evt.row)
meta:set_string("command", s)
meta:set_string("formspec", formspec2(meta))
return
elseif (fields.key_enter == "true" or fields.ok) and fields.cmnd ~= "" then
command(pos, fields.cmnd, player:get_player_name())
meta:set_string("command", "")
meta:set_string("formspec", formspec2(meta))
return
end
end
if public or not protected then
if fields.bttn1 then send_cmnd(pos, meta, 1)
elseif fields.bttn2 then send_cmnd(pos, meta, 2)
elseif fields.bttn3 then send_cmnd(pos, meta, 3)
elseif fields.bttn4 then send_cmnd(pos, meta, 4)
elseif fields.bttn5 then send_cmnd(pos, meta, 5)
elseif fields.bttn6 then send_cmnd(pos, meta, 6)
elseif fields.bttn7 then send_cmnd(pos, meta, 7)
elseif fields.bttn8 then send_cmnd(pos, meta, 8)
elseif fields.bttn9 then send_cmnd(pos, meta, 9)
end
end
end,

View File

@ -541,13 +541,9 @@ minetest.register_node("techage:ta4_lua_controller", {
end
end,
on_dig = function(pos, node, puncher, pointed_thing)
if minetest.is_protected(pos, puncher:get_player_name()) then
return
end
techage.remove_node(pos)
minetest.node_dig(pos, node, puncher, pointed_thing)
after_dig_node = function(pos, oldnode, oldmetadata)
techage.remove_node(pos, oldnode, oldmetadata)
techage.del_mem(pos)
end,
on_timer = on_timer,
@ -631,7 +627,7 @@ techage.register_node({"techage:ta4_lua_controller"}, {
elseif topic == "term" then
set_input(pos, number, "term", payload)
elseif topic == "msg" then
set_input(pos, number, "msg", payload)
set_input(pos, number, "msg", {src = src, data = payload})
elseif topic == "state" then
local running = meta:get_int("running") or STATE_STOPPED
return techage.StateStrings[running] or "stopped"

View File

@ -86,11 +86,14 @@ minetest.register_node("techage:ta4_server", {
return
end
techage.del_mem(pos)
techage.remove_node(pos)
minetest.node_dig(pos, node, puncher, pointed_thing)
end,
on_timer = function(pos, elasped)
after_dig_node = function(pos, oldnode, oldmetadata)
techage.remove_node(pos, oldnode, oldmetadata)
end,
on_timer = function(pos, elasped)
local meta = M(pos)
local nvm = techage.get_nvm(pos)
nvm.size = nvm.size or 0

View File

@ -108,7 +108,7 @@ local function command(pos, cmnd, player)
if techage.lua_ctlr.not_protected(owner, num) then
--output(pos, player..":$ msg "..num.." "..text)
output(pos, "> msg "..num.." "..text)
techage.send_single(own_number, num, "msg", {src=own_number, text=text})
techage.send_single(own_number, num, "msg", text)
return
end
end
@ -230,26 +230,31 @@ techage.lua_ctlr.register_action("put_term", {
})
techage.lua_ctlr.register_function("get_msg", {
cmnd = function(self)
cmnd = function(self, raw)
local msg = techage.lua_ctlr.get_msg(self.meta.number)
if msg then
return msg.src, msg.text
local data = msg.data
if not raw then
data = tostring(data or "")
end
return msg.src, data
end
end,
help = ' $get_msg() --> number and text string or nil\n'..
help = ' $get_msg([raw]) --> number and any value or nil\n'..
' If the optional `raw` parameter is not set or false,\n'..
' the second return value is guaranteed to be a string.\n'..
' Read a received messages. Number is the node\n'..
' number of the sender.\n'..
' example: num,msg = $get_msg().'
})
techage.lua_ctlr.register_action("send_msg", {
cmnd = function(self, num, text)
local msg = {src = self.meta.number, text = tostring(text or "")}
cmnd = function(self, num, data)
if techage.lua_ctlr.not_protected(self.meta.owner, num) then
techage.send_single(self.meta.number, num, "msg", msg)
techage.send_single(self.meta.number, num, "msg", data)
end
end,
help = " $send_msg(num, text)\n"..
help = " $send_msg(num, data)\n"..
' Send a message to the controller with number "num".\n'..
' example: $send_msg("0123", "test")'
})

View File

@ -70,6 +70,17 @@ Die Antriebsachsen dienen zur Kraftübertragung von der Dampfmaschine zu anderen
[ta2_driveaxle|image]
### TA2 Stromgenerator / TA2 Power Generator
Um Lampen oder andere Stromverbraucher an einer Dampfmaschine betreiben zu können, wird der TA2 Stromgenerator benötigt. Der TA2 Stromgenerator muss auf einer Seite mit Antriebsachsen verbunden werden und liefert dann auf der anderen Seite elektrischen Strom.
Wird der Stromgenerator nicht mit ausreichend Kraft versorgt, geht er in einen Fehlerzustand und muss über einen Rechtsklick wieder aktiviert werden.
Das Stromgenerator nimmt primär max. 25 ku an Achsenkraft auf und gibt sekundär max. 24 ku als Strom wieder ab. Er verbraucht also ein ku für die Umwandlung.
[ta2_generator|image]
## Items schieben und sortieren
@ -107,6 +118,8 @@ Der Verteiler ist in der Lage, die Items aus seinem Inventar sortiert in bis zu
Der Verteiler besitzt dazu ein Menü mit 4 Filter mit unterschiedlichen Farben, entsprechend den 4 Ausgängen. Soll ein Ausgang genutzt werden, so muss der entsprechende Filter über die "on" Checkbox aktiviert werden. Alle Items, die für diesen Filter konfiguriert sind, werden über den zugeordneten Ausgang ausgegeben. Wird ein Filter aktiviert, ohne das Items konfiguriert werden, so sprechen wir hier von einem "nicht-konfigurierten", offenen Ausgang.
**Achtung: Der Verteiler ist an seinen Ausgängen gleichzeitig ein Schieber. Daher niemals die Gegenstände mit einem Schieber aus dem Verteiler ziehen!**
Für einen nicht-konfigurierten Ausgang gibt es zwei Betriebsarten:
1) Alle Items ausgeben, die an keine anderen Ausgängen ausgegeben werden können, auch wenn diese blockiert sind.

View File

@ -71,6 +71,18 @@ The drive axles are used to transmit power from the steam engine to other machin
[ta2_driveaxle|image]
### TA2 Power Generator
The TA2 Power Generator is required to operate lamps or other power consumers on a steam engine. The TA2 Power Generator has to be connected to drive axles on one side and then supplies electricity on the other side.
If the Power Generator is not supplied with sufficient power, it goes into an error state and must be reactivated with a right-click.
The Power Generator takes max. 25 ku of axle power and provides on the other side max. 24 ku as electricity. So he consumes one ku for the conversion.
[ta2_generator|image]
## Push and sort items
In order to transport objects from one processing station to the next, pushers and tubes are used. See plan.
@ -107,6 +119,8 @@ The distributor is able to transport the items from his inventory sorted in up t
The distributor has a menu with 4 filters with different colors, corresponding to the 4 outputs. If an output is to be used, the corresponding filter must be activated via the "on" checkbox. All items that are configured for this filter are output via the assigned output. If a filter is activated without items being configured, we are talking about an "unconfigured", open output.
**Attention: The distributor is also a pusher at its output sides. Therefore, never pull items out of the distributor with a pusher!**
There are two operating modes for a non-configured output:
1) Output all items that cannot be output to any other exit, even if they are blocked.

View File

@ -198,6 +198,27 @@ In der unteren Hälfte werden die Daten aller Generatoren und Speichersystemen d
[ta3_powerterminal|image]
### TA3 Elektromotor / TA3 Electric Motor
Um TA2 Maschinen über das Stromnetz betreiben zu können, wird der TA3 Elektromotor benötigt. Dieser wandelt Strom in Achsenkraft um.
Wird der Elektromotor nicht mit ausreichend Strom versorgt, geht er in einen Fehlerzustand und muss über einen Rechtsklick wieder aktiviert werden.
Das Elektromotor nimmt primär max. 40 ku an Strom auf und gibt sekundär max. 39 ku als Achsenkraft wieder ab. Er verbraucht also ein ku für die Umwandlung.
[ta3_motor|image]
### TA3 Strom Terminal / Power Terminal
Das Strom-Terminal muss mit dem Stromnetz verbunden werden. Es zeigt Daten aus dem Stromnetz an.
In der oberen Hälfte werden nur die Daten eines ausgewählten Typs ausgegeben. Wird als Typ bspw. "Kraftwerk" gewählt, so werden nur die Daten von Öl- und Kohlekraftwerken gesammelt und ausgegeben. Links werden die Daten von Generatoren (Stromabgabe) und rechts die Daten von Energiespeichern (Stromaufnahme) ausgegeben. Beim Akkublocks bspw. wird beides ausgegeben, da der Akku Strom aufnehmen und abgeben kann.
In der unteren Hälfte werden die Daten aller Generatoren und Speichersystemen des ganzen Stromnetzen zusammengefasst ausgegeben.
[ta3_powerterminal|image]
## TA3 Industrieofen
Der TA3 Industrieofen dient als Ergänzung zu normalen Ofen (furnace). Damit können alle Waren mit "Koch" Rezepten, auch im Industrieofen hergestellt werden. Es gibt aber auch spezielle Rezepte, die nur im Industrieofen hergestellt werden können.
@ -569,7 +590,7 @@ Das Terminal besitzt folgende, lokalen Kommandos:
- `pub` schalte in den öffentlichen Modus um
- `priv` schalte in den privaten Modus um
Im privaten Modul kann nur der Besitzer selbst Kommandos eingeben oder Tasten nutzen.
Im privaten Modus (private) kann das Terminal nur von Spielern verwendet werden, die an diesem Ort bauen können, also Protection Rechte besitzen. Im öffentlichen Modus (public) können alle Spieler die vorkonfigurierten Tasten verwenden.
[ta3_terminal|image]

View File

@ -198,6 +198,18 @@ In the lower half, the data of all generators and storage systems of the entire
[ta3_powerterminal|image]
### TA3 Electric Motor
The TA3 Electric Motor is required in order to be able to operate TA2 machines via the power grid. The TA3 Electric Motor converts electricity into axle power.
If the electric motor is not supplied with sufficient power, it goes into an fault state and must be reactivated with a right-click.
The electric motor takes max. 40 ku of electricity and provides on the other side max. 39 ku as axle power. So he consumes one ku for the conversion.
[ta3_motor|image]
## TA3 Industrial Furnace
The TA3 industrial furnace serves as a supplement to normal furnaces. This means that all goods can be produced with "cooking" recipes, even in an industrial furnace. But there are also special recipes that can only be made in an industrial furnace.
@ -567,7 +579,9 @@ The terminal has the following local commands:
- `pub` switch to public mode
- `priv` switch to private mode
In the private mode, only the owner can enter commands himself or use keys.
In private mode, the terminal can only be used by players who can build at this location, i.e. who have protection rights.
In public mode, all players can use the preconfigured keys.
[ta3_terminal|image]

View File

@ -7,10 +7,11 @@ Regenerative Energiequellen wie Wind, Sonne und Biokraft helfen dir, das Ölzeit
## Windkraftanlage
Eine Windkraftanlagen liefern immer dann Strom, wenn Wind vorhanden ist. Im Spiel gibt es keinen Wind, aber die Mod simuliert dies dadurch, dass sich nur morgens (5:00 - 9:00) und abends (17:00 - 21:00) die Windräder drehen und damit Strom liefern, sofern diese an geeigneten Stellen errichtet werden.
Eine Windkraftanlage liefern immer dann Strom, wenn Wind vorhanden ist. Im Spiel gibt es keinen Wind, aber die Mod simuliert dies dadurch, dass sich nur morgens (5:00 - 9:00) und abends (17:00 - 21:00) die Windräder drehen. Eine Windkraftanlage liefert nur dann Strom, wenn sie an einer geeigneten Stelle aufgestellt ist.
Die TA Windkraftanlagen sind reine Offshore Anlagen, das heißt, die müssen im Meer (Wasser) errichtet werden. Dies bedeutet, dass um den Mast herum mit einem Abstand von 20 Blöcken nur Wasser sein darf und das mindestens 2 Blöcke tief.
Der Rotor muss in einer Höhe (Y-Koordinate) von 12 bis maximal 20 m platziert werden. Der Abstand zu weiteren Windkraftanlagen muss mindestens 14 m betragen.
Die TA Windkraftanlagen sind reine Offshore Anlagen, das heißt, die müssen im Meer errichtet werden. Dies bedeutet, dass Windkraftanlagen nur in einem Meer (occean) Biom errichtet werden können und dass um den Mast herum ausreichend Wasser und freie Sicht vorhanden sein müssen.
Um eine geeignete Stelle zu finden, musst du mit dem Schraubenschlüssel (TechAge Info Werkzeug) auf das Wasser klicken. Ob diese Stelle für den Mast der Windkraftanlage geeignet ist, wird dir als Chat Nachricht angezeigt.
Der Strom muss vom Rotor-Block durch den Mast nach unten geführt werden. Dazu zuerst die Stromleitung nach oben ziehen und das Stromkabel dann mit TA4 Säulenblöcke "verputzen". Unten kann eine Arbeitsplattform errichtet werden. Der Plan rechts zeigt den Aufbau im oberen Teil.
@ -390,9 +391,9 @@ Die TA4 Sensor Kiste dient zum Aufbau von Automatischen Lagern oder Verkaufsauto
Wird etwas in die Kiste gelegt, oder entnommen, oder eine der Tasten "F1"/"F2" gedrückt, so wird ein Event-Signal an den Lua Controller gesendet.
Die Sensor Kiste unterstützt folgende Kommandos:
- Über `state = $read_data(<num>, "state")` kann der Status der Kiste abgefragt werden. Mögliche Antworten sind: "empty", "loaded", "full"
- Über `name, action = $read_data(<num>, "action")` kann die letzte Spieleraktion abgefragt werden. `name` ist der Spielername, Als `action` wird zurückgeliefert: "put", "take", "f1", "f2".
- Über `stacks = $read_data(<num>, "stacks")` kann der Inhalt der Kiste ausgelesen werden. Siehe dazu: https://github.com/joe7575/techage/blob/master/manuals/ta4_lua_controller_EN.md#sensor-chest
- Über `state = $send_cmnd(<num>, "state")` kann der Status der Kiste abgefragt werden. Mögliche Antworten sind: "empty", "loaded", "full"
- Über `name, action = $send_cmnd(<num>, "action")` kann die letzte Spieleraktion abgefragt werden. `name` ist der Spielername, Als `action` wird zurückgeliefert: "put", "take", "f1", "f2".
- Über `stacks = $send_cmnd(<num>, "stacks")` kann der Inhalt der Kiste ausgelesen werden. Siehe dazu: https://github.com/joe7575/techage/blob/master/manuals/ta4_lua_controller_EN.md#sensor-chest
- Über `$send_cmnd(<num>, "text", "press both buttons and\nput something into the chest")` kann der Text im Menü der Sensor Kiste gesetzt werden.
Über die Checkbox "Erlaube öffentlichen Zugriff" kann eingestellt werden, ob die Kiste von jedem genutzt werden darf, oder nur von Spielern die hier Zugriffsrechte haben.
@ -596,8 +597,8 @@ Die Kiste kann nur von den Spielern genutzt werden, die an diesem Ort auch bauen
Der Kiste besitzt ein zusätzliches Kommandos für den Lua Controller:
- `count` dient zur Anfrage, wie viele Items in der Kiste sind.
Beispiel 1: `$read_data(CHEST, "count")` --> Summe der Items über alle 8 Speicher
Beispiel 2: `$read_data(CHEST, "count", 2)` --> Anzahl der Items in Speicher 2 (zweiter von links)
Beispiel 1: `$send_cmnd(CHEST, "count")` --> Summe der Items über alle 8 Speicher
Beispiel 2: `$send_cmnd(CHEST, "count", 2)` --> Anzahl der Items in Speicher 2 (zweiter von links)
[ta4_8x2000_chest|image]

View File

@ -7,10 +7,11 @@ Renewable energy sources such as wind, sun and biofuels help you to leave the oi
## Wind Turbine
A wind turbine always delivers electricity when there is wind. There is no wind in the game, but the mod simulates this by only turning the wind turbines in the morning (5:00 a.m. - 9:00 a.m.) and in the evening (5:00 p.m. - 9:00 p.m.) and thus supplying electricity, provided they are positioned appropriately.
A wind turbine always supplies electricity when there is wind. There is no wind in the game, but the mod simulates this by turning the wind turbines only in the morning (5:00 - 9:00) and in the evening (17:00 - 21:00). A wind turbine only supplies electricity if it is set up in a suitable location.
The TA wind turbines are pure offshore plants, which means that they have to be installed in the sea (water). This means that there must be in the minimum 20 blocks of water around the mast and at least 2 blocks deep.
The rotor must be placed at a height (Y coordinate) of 12 to a maximum of 20 m. The distance to other wind turbines must be at least 14 m.
The TA wind power plants are pure offshore plants, which means that they have to be built in the sea. This means that wind turbines can only be build in a sea (occean) biome and that there must be sufficient water and a clear view around the mast.
To find a suitable spot, click on the water with the wrench (TechAge Info Tool). A chat message will show you whether this position is suitable for the mast of the wind turbine.
The current must be led from the rotor block down through the mast. First pull the power line up and then "plaster" the power cable with TA4 pillar blocks. A work platform can be built below. The plan on the right shows the structure in the upper part.
@ -392,9 +393,9 @@ The TA4 sensor box is used to set up automatic warehouses or vending machines in
If something is put into the box or removed, or one of the "F1" / "F2" keys is pressed, an event signal is sent to the Lua controller.
The sensor box supports the following commands:
- The status of the box can be queried via `state = $read_data(<num>, "state")`. Possible answers are: "empty", "loaded", "full"
- The last player action can be queried via `name, action = $read_data(<num>, "action")`. `name` is the player name. One of the following is returned as `action`: "put", "take", "f1", "f2".
- The contents of the box can be read out via `stacks = $read_data(<num>, "stacks")`. See: https://github.com/joe7575/techage/blob/master/manuals/ta4_lua_controller_EN.md#sensor-chest
- The status of the box can be queried via `state = $send_cmnd(<num>, "state")`. Possible answers are: "empty", "loaded", "full"
- The last player action can be queried via `name, action = $send_cmnd(<num>, "action")`. `name` is the player name. One of the following is returned as `action`: "put", "take", "f1", "f2".
- The contents of the box can be read out via `stacks = $send_cmnd(<num>, "stacks")`. See: https://github.com/joe7575/techage/blob/master/manuals/ta4_lua_controller_EN.md#sensor-chest
- Via `$send_cmnd(<num>, "text", "press both buttons and\nput something into the chest")` the text can be set in the menu of the sensor box.
The checkbox "Allow public chest access" can be used to set whether the box can be used by everyone or only by players who have access/protection rights here.
@ -579,7 +580,7 @@ The TA4 8x2000 chest does not have a normal inventory like other chest, but has
If the chest is filled with a pusher, all stores fill from left to right. If all 8 stores are full and no further items can be added, further items are rejected.
** Row function **
**Row function**
Several TA4 8x2000 chests can be connected to a large chest with more content. To do this, the chests must be placed in a row one after the other.
@ -597,8 +598,8 @@ The chest can only be used by players who can build at this location, i.e. who h
The chest has an additional command for the Lua controller:
- `count` is used to request how many items are in the chest.
Example 1: `$read_data(CHEST, "count")` -> Sum of items across all 8 stores
Example 2: `$read_data(CHEST, "count", 2)` -> number of items in store 2 (second from left)
Example 1: `$send_cmnd(CHEST, "count")` -> Sum of items across all 8 stores
Example 2: `$send_cmnd(CHEST, "count", 2)` -> number of items in store 2 (second from left)
[ta4_8x2000_chest|image]

View File

@ -106,7 +106,7 @@ class MyRenderer(mistune.Renderer):
##
def block_code(self, code, lang):
text = formspec_escape(code.strip())
lines = text.split("\n")
lines = text.split("\\n")
lines = [" " + item for item in lines]
self.TextChunck.extend(lines)
self.TextChunck.append("")

View File

@ -346,10 +346,16 @@ In addition to Lua standard function the Lua Controller provides the following f
### Techage Command Functions
* `$read_data(num, ident, add_data)` - Read any kind of data from another block with the given number _num_.
_ident_ specifies the data to be read.
_add_data_ is for additional data and normally not needed.
The result is block dependent (see table below):
With the `$send_cmnd(num, ident, add_data)` function, you can send commands to and retrieve data from another block with the given number _num_.
The possible commands can be classified in two groups: Commands for reading data and commands for triggering an action.
Please note, that this is not a technical distinction, only a logical.
**Reading data**
- _ident_ specifies the data to be read.
- _add_data_ is for additional data and normally not needed.
- The result is block dependent (see table below)
| ident | returned data | comment |
| ------------ | ------------------------------------------------------------ | ------------------------------------------------------------ |
@ -358,18 +364,22 @@ In addition to Lua standard function the Lua Controller provides the following f
| "state" | one of: "empty", "loaded", "full" | State of a chest or Sensor Chest |
| "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 = $read_data("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") |
| "delivered" | number | Read the current delivered power value of a generator block. A power consuming block (accu) provides a negative value |
| "action" | player-name, action-string | only for Sensor Chests |
| "stacks" | Array with up to 4 Stores with the inventory content (see example) | only for Sensor Chests |
| "action" | player-name, action-string | Only for Sensor Chests |
| "stacks" | Array with up to 4 Stores with the inventory content (see example) | Only for Sensor Chests |
| "count" | number | Read the item counter of the TA4 Item Detector block |
| "count" | number of items | Read the total amount of TA4 chest items. An optional number as `add_data` is used to address only one inventory slot (1..8, from left to right). |
| "itemstring" | item string of the given slot | Specific command for the TA4 8x2000 Chest to read the item type (technical name) of one chest slot, specified via `add_data` (1..8).<br />Example: s = $read_data("223", "itemstring", 1) |
| "itemstring" | item string of the given slot | Specific command for the TA4 8x2000 Chest to read the item type (technical name) of one chest slot, specified via `add_data` (1..8).<br />Example: s = $send_cmnd("223", "itemstring", 1) |
| "output" | recipe output string, <br />e.g.: "default:glass" | Only for the Industrial Furnace. If no recipe is active, the command returns "unknown" |
**Trigger an action**
* `$send_cmnd(num, cmnd, data)` - Send a command to another block. _num_ is the number of the remote block, like "1234". _cmnd_ is the command, _data_ is additional data (see table below):
- _num_ is the number of the remote block, like "1234"
- _cmnd_ is the command
- _data_ is additional data (see table below)
| cmnd | data | comment |
| -------------------------------- | ------------ | ------------------------------------------------------------ |
@ -406,11 +416,10 @@ In contrast the Controller can send text strings to the terminal.
### Further Functions
Messages are used to transport data between Controllers. Messages are text strings or any other data. Incoming messages are stored in order (up to 10) and can be read one after the other.
* `$get_msg()` - Read a received message. The function returns the sender number and the message. (see example "Emails")
Messages are used to transport data between Controllers. Messages can contain arbitrary data. Incoming messages are stored in order (up to 10) and can be read one after the other.
* `$get_msg([raw])` - Read a received message. The function returns the sender number and the message. (see example "Emails"). If the _raw_ parameter is not set or false, the message is guaranteed to be a string.
* `$send_msg(num, msg)` - Send a message to another Controller. _num_ is the destination number. (see example "Emails")
* `$chat(text)` - Send yourself a chat message. _text_ is a text string.
* `$door(pos, text)` - Open/Close a door at position "pos".
@ -419,7 +428,7 @@ Messages are used to transport data between Controllers. Messages are text strin
* `$item_description("default:apple")`
Get the description (item name) for a specified itemstring, e. g. determined via the TA4 8x2000 Chest command `itemstring`:
`str = $read_data("223", "itemstring", 1)`
`str = $send_cmnd("223", "itemstring", 1)`
`descr = $item_description(str)`
@ -504,15 +513,15 @@ if ticks % 60 == 0 then
$display(DISPLAY, 1, min.." min")
-- Cactus chest overrun
sts = $read_data("1034", "state") -- read pusher status
sts = $send_cmnd("1034", "state") -- read pusher status
if sts == "blocked" then $display(DISPLAY, 2, "Cactus full") end
-- Tree chest overrun
sts = $read_data("1065", "state") -- read pusher status
sts = $send_cmnd("1065", "state") -- read pusher status
if sts == "blocked" then $display(DISPLAY, 3, "Tree full") end
-- Furnace fuel empty
sts = $read_data("1544", "state") -- read pusher status
sts = $send_cmnd("1544", "state") -- read pusher status
if sts == "standby" then $display(DISPLAY, 4, "Furnace fuel") end
end
```
@ -570,7 +579,7 @@ loop() code:
```lua
if event then
name = $read_data(SENSOR, "name")
name = $send_cmnd(SENSOR, "name")
if name == "" then -- no player arround
$clear_screen(DISPLAY)
else
@ -602,13 +611,13 @@ loop() code:
```lua
if event and $get_input(SENSOR) == "on" then
-- read inventory state
state = $read_data(SENSOR, "state")
state = $send_cmnd(SENSOR, "state")
$print("state: "..state)
-- read player name and action
name, action = $read_data(SENSOR, "action")
name, action = $send_cmnd(SENSOR, "action")
$print("action"..": "..name.." "..action)
-- read inventory content
stacks = $read_data(SENSOR, "stacks")
stacks = $send_cmnd(SENSOR, "stacks")
for i,stack in stacks.next() do
$print("stack: "..stack.get("name").." "..stack.get("count"))
end

View File

@ -27,6 +27,7 @@
- [TA2 Schwungrad / Flywheel](./manual_ta2_DE.md#ta2-schwungrad--flywheel)
- [TA2 Dampfleitungen / Steam Pipe](./manual_ta2_DE.md#ta2-dampfleitungen--steam-pipe)
- [TA2 Antriebsachsen / TA2 Drive Axle](./manual_ta2_DE.md#ta2-antriebsachsen--ta2-drive-axle)
- [TA2 Stromgenerator / TA2 Power Generator](./manual_ta2_DE.md#ta2-stromgenerator--ta2-power-generator)
- [Items schieben und sortieren](./manual_ta2_DE.md#items-schieben-und-sortieren)
- [Röhren / TechAge Tube](./manual_ta2_DE.md#röhren--techage-tube)
- [TA2 Schieber / Pusher](./manual_ta2_DE.md#ta2-schieber--pusher)
@ -44,10 +45,10 @@
- [TA2 Flüssigkeitensammler / Liquid Sampler](./manual_ta2_DE.md#ta2-flüssigkeitensammler--liquid-sampler)
- [TA2 Gesicherte Kiste / Protected Chest](./manual_ta2_DE.md#ta2-gesicherte-kiste--protected-chest)
- [Techage Forceload Block](./manual_ta2_DE.md#techage-forceload-block)
- [TA3: Ölzeitalter](./manual_ta3_DE.md#ta3:-ölzeitalter)
- [Kohlekraftwerk / Ölkraftwerk](./manual_ta3_DE.md#kohlekraftwerk--ölkraftwerk)
- [TA3: Ölzeitalter](./manual_ta3_DE.md#ta3:-Ölzeitalter)
- [Kohlekraftwerk / Ölkraftwerk](./manual_ta3_DE.md#kohlekraftwerk--Ölkraftwerk)
- [TA3 Kraftwerks-Feuerbox / Power Station Firebox](./manual_ta3_DE.md#ta3-kraftwerks-feuerbox--power-station-firebox)
- [TA3 Kraftwerks-Ölbrenner / TA3 Power Station Oil Burner](./manual_ta3_DE.md#ta3-kraftwerks-ölbrenner--ta3-power-station-oil-burner)
- [TA3 Kraftwerks-Ölbrenner / TA3 Power Station Oil Burner](./manual_ta3_DE.md#ta3-kraftwerks-Ölbrenner--ta3-power-station-oil-burner)
- [TA3 Boiler unten/oben](./manual_ta3_DE.md#ta3-boiler-untenoben)
- [TA3 Turbine](./manual_ta3_DE.md#ta3-turbine)
- [TA3 Generator](./manual_ta3_DE.md#ta3-generator)
@ -65,8 +66,10 @@
- [TA3 Kleiner Stromgenerator / Tiny Power Generator](./manual_ta3_DE.md#ta3-kleiner-stromgenerator--tiny-power-generator)
- [TA3 Akku Block / Akku Box](./manual_ta3_DE.md#ta3-akku-block---akku-box)
- [TA3 Strom Terminal / Power Terminal](./manual_ta3_DE.md#ta3-strom-terminal--power-terminal)
- [TA3 Elektromotor / TA3 Electric Motor](./manual_ta3_DE.md#ta3-elektromotor--ta3-electric-motor)
- [TA3 Strom Terminal / Power Terminal](./manual_ta3_DE.md#ta3-strom-terminal--power-terminal)
- [TA3 Industrieofen](./manual_ta3_DE.md#ta3-industrieofen)
- [TA3 Ofen-Ölbrenner / Furnace Oil Burner](./manual_ta3_DE.md#ta3-ofen-ölbrenner--furnace-oil-burner)
- [TA3 Ofen-Ölbrenner / Furnace Oil Burner](./manual_ta3_DE.md#ta3-ofen-Ölbrenner--furnace-oil-burner)
- [TA3 Ofenoberteil / Furnace Top](./manual_ta3_DE.md#ta3-ofenoberteil--furnace-top)
- [TA3 Gebläse / Booster](./manual_ta3_DE.md#ta3-gebläse--booster)
- [Flüssigkeiten](./manual_ta3_DE.md#flüssigkeiten)
@ -76,18 +79,18 @@
- [TA4 Röhre / Pipe](./manual_ta3_DE.md#ta4-röhre--pipe)
- [TA3 Rohr/Wanddurchbruch / TA3 Pipe Wall Entry Blöcke](./manual_ta3_DE.md#ta3-rohrwanddurchbruch--ta3-pipe-wall-entry-blöcke)
- [TA Ventil / TA Valve](./manual_ta3_DE.md#ta-ventil--ta-valve)
- [Öl-Förderung](./manual_ta3_DE.md#öl-förderung)
- [TA3 Ölexplorer / Oil Explorer](./manual_ta3_DE.md#ta3-ölexplorer--oil-explorer)
- [TA3 Ölbohrkiste / Oil Drill Box](./manual_ta3_DE.md#ta3-ölbohrkiste--oil-drill-box)
- [TA3 Ölpumpe / Oil Pumpjack](./manual_ta3_DE.md#ta3-ölpumpe--oil-pumpjack)
- [Öl-Förderung](./manual_ta3_DE.md#Öl-förderung)
- [TA3 Ölexplorer / Oil Explorer](./manual_ta3_DE.md#ta3-Ölexplorer--oil-explorer)
- [TA3 Ölbohrkiste / Oil Drill Box](./manual_ta3_DE.md#ta3-Ölbohrkiste--oil-drill-box)
- [TA3 Ölpumpe / Oil Pumpjack](./manual_ta3_DE.md#ta3-Ölpumpe--oil-pumpjack)
- [TA3 Bohrgestänge / Drill Pipe](./manual_ta3_DE.md#ta3-bohrgestänge--drill-pipe)
- [Öltank / Oil Tank](./manual_ta3_DE.md#öltank--oil-tank)
- [Öl-Transport](./manual_ta3_DE.md#öl-transport)
- [Öl-Transport mit dem Tankwagen](./manual_ta3_DE.md#öl-transport-mit-dem-tankwagen)
- [Öl-Transport mit Fässern über Minecarts](./manual_ta3_DE.md#öl-transport-mit-fässern-über-minecarts)
- [Öltank / Oil Tank](./manual_ta3_DE.md#Öltank--oil-tank)
- [Öl-Transport](./manual_ta3_DE.md#Öl-transport)
- [Öl-Transport mit dem Tankwagen](./manual_ta3_DE.md#Öl-transport-mit-dem-tankwagen)
- [Öl-Transport mit Fässern über Minecarts](./manual_ta3_DE.md#Öl-transport-mit-fässern-über-minecarts)
- [Tankwagen / Tank Cart](./manual_ta3_DE.md#tankwagen--tank-cart)
- [Kistenwagen / Chest Cart](./manual_ta3_DE.md#kistenwagen--chest-cart)
- [Öl-Verarbeitung](./manual_ta3_DE.md#öl-verarbeitung)
- [Öl-Verarbeitung](./manual_ta3_DE.md#Öl-verarbeitung)
- [Destillationsturm / distiller tower](./manual_ta3_DE.md#destillationsturm--distiller-tower)
- [Aufkocher / reboiler)](./manual_ta3_DE.md#aufkocher--reboiler))
- [Logik-/Schalt-Blöcke](./manual_ta3_DE.md#logik-schalt-blöcke)

View File

@ -27,6 +27,7 @@
- [TA2 Flywheel](./manual_ta2_EN.md#ta2-flywheel)
- [TA2 Steam Pipes](./manual_ta2_EN.md#ta2-steam-pipes)
- [TA2 Drive Axle / TA2 Gearbox](./manual_ta2_EN.md#ta2-drive-axle--ta2-gearbox)
- [TA2 Power Generator](./manual_ta2_EN.md#ta2-power-generator)
- [Push and sort items](./manual_ta2_EN.md#push-and-sort-items)
- [TechAge Tube](./manual_ta2_EN.md#techage-tube)
- [TA2 Pusher](./manual_ta2_EN.md#ta2-pusher)
@ -65,6 +66,7 @@
- [TA3 Small Power Generator](./manual_ta3_EN.md#ta3-small-power-generator)
- [TA3 Battery Block](./manual_ta3_EN.md#ta3-battery-block)
- [TA3 Power Terminal](./manual_ta3_EN.md#ta3-power-terminal)
- [TA3 Electric Motor](./manual_ta3_EN.md#ta3-electric-motor)
- [TA3 Industrial Furnace](./manual_ta3_EN.md#ta3-industrial-furnace)
- [TA3 Furnace Oil Burner](./manual_ta3_EN.md#ta3-furnace-oil-burner)
- [TA3 Furnace Top](./manual_ta3_EN.md#ta3-furnace-top)

View File

@ -27,7 +27,7 @@ local function start_consumer(tbl, tlib_type)
if def and def["cstate"] == NOPOWER and (def["calive"] or 0) > 0 then
local ndef = net_def(v.pos, tlib_type)
def["cstate"] = RUNNING
def["taken"] = v.nominal or 0
def["taken"] = v.nominal or def.curr_power or 0
if ndef.on_power then
ndef.on_power(v.pos, tlib_type)
end
@ -73,10 +73,10 @@ local function get_consumer_sum(tbl, tlib_type, cycle_time)
if def and def["cstate"] ~= STOPPED then
def["calive"] = (def["calive"] or 1) - cycle_time/2
if def["calive"] >= 0 then
sum = sum + v.nominal
sum = sum + (v.nominal or def.curr_power or 0)
end
end
--print(N(v.pos), P2S(v.pos), def["cstate"], def["calive"])
--print(N(v.pos), P2S(v.pos), def["cstate"], def["calive"], sum)
end
return sum
end
@ -155,7 +155,7 @@ function techage.power.get_con1_sum(network, tlib_type)
local nvm = techage.get_nvm(v.pos)
local def = nvm[tlib_type] -- power related network data
if def and def["cstate"] ~= STOPPED then
sum = sum + v.nominal
sum = sum + (v.nominal or def.curr_power or 0)
end
end
return sum

View File

@ -48,11 +48,24 @@ function techage.power.formspec_power_bar(max_power, current_power)
return "techage_form_level_bg.png^[lowpart:"..percent..":techage_form_level_fg.png"
end
function techage.power.formspec_label_bar(x, y, label, max_power, current_power, unit)
function techage.power.formspec_label_bar(pos, x, y, label, max_power, current_power, unit)
local percent, ypos
current_power = current_power or 0
max_power = max_power or 1
unit = unit or "ku"
if current_power == 0 then
-- check if power network is overloaded
if techage.power.network_overloaded(pos, techage.ElectricCable) then
return "container["..x..","..y.."]"..
"box[0,0;2.3,3.3;#395c74]"..
"label[0.2,0;"..label.."]"..
"label[0.7,0.4;"..max_power.." "..unit.."]"..
"image[0,0.5;1,3;techage_form_level_red_fg.png]"..
"container_end[]"
end
end
current_power = current_power or 0
if current_power == 0 then
percent = 0
ypos = 2.8

View File

@ -148,6 +148,17 @@ function techage.power.power_available(pos, Cable)
return netw and netw.on and netw.alive and netw.alive > 0
end
function techage.power.network_overloaded(pos, Cable)
local nvm = techage.get_nvm(pos)
local tlib_type = Cable.tube_type
local netID = nvm[Cable.tube_type] and nvm[Cable.tube_type]["netID"]
local netw = networks.has_network(tlib_type, netID)
if netw then
local sum = (netw.available1 or 0) + (netw.available2 or 0)
return sum > 0 and sum < (netw.needed1 or 0)
end
end
-- this is more a try to start, the start will be performed by on_power()
function techage.power.consumer_start(pos, Cable, cycle_time)
local nvm = techage.get_nvm(pos)
@ -228,5 +239,24 @@ function techage.power.generator_alive(pos, Cable, cycle_time, outdir, curr_powe
return 0
end
-- Calculate the needed power over all con1 consumers
function techage.power.needed_power(pos, Cable, outdir)
local sum = 0
networks.connection_walk(pos, outdir, Cable, function(pos, indir, node)
local net = net_def(pos, Cable.tube_type) -- network definition
if net.ntype == "con1" then
local nvm = techage.get_nvm(pos)
local def = nvm[Cable.tube_type] -- power related data
if def and def["cstate"] ~= STOPPED then
if def["calive"] >= 0 then
sum = sum + (net.nominal or def.curr_power or 0)
end
end
end
end)
return sum
end
-- function delete_netID(pos, outdir, Cable)
techage.power.delete_netID = delete_netID

View File

@ -129,10 +129,10 @@ local function formspec(pos, nvm)
"box[0,-0.1;9.8,0.5;#c6e8ff]"..
"label[4,-0.1;"..minetest.colorize( "#000000", S("Network Data")).."]"..
"label[9.5,-0.1;"..minetest.colorize( "#000000", star).."]"..
power.formspec_label_bar(0, 0.7, S("Genera. 1"), gen1.pow_act, gen1.pow_used)..
power.formspec_label_bar(2.5, 0.7, S("Genera. 2"), gen2.pow_act, gen2.pow_used)..
power.formspec_label_bar(5, 0.7, S("Consum. 2"), con2.pow_act, con2.pow_used)..
power.formspec_label_bar(7.5, 0.7, S("Consum. 1"), con1.pow_act, con1.pow_used)..
power.formspec_label_bar(pos, 0, 0.7, S("Genera. 1"), gen1.pow_act, gen1.pow_used)..
power.formspec_label_bar(pos, 2.5, 0.7, S("Genera. 2"), gen2.pow_act, gen2.pow_used)..
power.formspec_label_bar(pos, 5, 0.7, S("Consum. 2"), con2.pow_act, con2.pow_used)..
power.formspec_label_bar(pos, 7.5, 0.7, S("Consum. 1"), con1.pow_act, con1.pow_used)..
"box[0,4.3;9.8,0.4;#c6e8ff]"..
"box[0,4.85;9.8,0.4;#395c74]"..
"box[0,5.35;9.8,0.4;#395c74]"..

View File

@ -37,6 +37,7 @@ local Generators = {
S("Energy storage"),
S("Fuel cell"),
S("Electrolyzer"),
S("TA2 Generator"),
}
local Storage = {
@ -55,6 +56,7 @@ local GeneratorPerformances = {
60, -- S("Energy storage")
25, -- S("Fuel cell")
30, -- S("Electrolyzer")
24, -- S("TA2 Generator")
}
--

View File

@ -67,8 +67,8 @@ local function formspec(self, pos, nvm)
default.gui_slots..
"box[0,-0.1;5.8,0.5;#c6e8ff]"..
"label[2.5,-0.1;"..minetest.colorize( "#000000", S("Inverter")).."]"..
power.formspec_label_bar(0, 0.8, S("Power DC"), PWR_PERF, max_power)..
power.formspec_label_bar(3.5, 0.8, S("Power AC"), max_power, delivered)..
power.formspec_label_bar(pos, 0, 0.8, S("Power DC"), PWR_PERF, max_power)..
power.formspec_label_bar(pos, 3.5, 0.8, S("Power AC"), max_power, delivered)..
arrow..
"image_button[2.5,3;1,1;".. self:get_state_button_image(nvm) ..";state_button;]"..
"tooltip[2.5,3;1,1;"..self:get_state_tooltip(nvm).."]"

View File

@ -38,7 +38,7 @@ local function formspec(self, pos, nvm)
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
power.formspec_label_bar(0, 0.8, S("power"), PWR_CAPA, nvm.provided)..
power.formspec_label_bar(pos, 0, 0.8, S("power"), PWR_CAPA, nvm.provided)..
"image_button[2.8,2;1,1;".. self:get_state_button_image(nvm) ..";state_button;]"..
"tooltip[2.8,2;1,1;"..self:get_state_tooltip(nvm).."]"
end

View File

@ -36,7 +36,7 @@ local function formspec(self, pos, nvm)
default.gui_slots..
"box[0,-0.1;4.8,0.5;#c6e8ff]"..
"label[1,-0.1;"..minetest.colorize( "#000000", S("TA3 Akku Box")).."]"..
power.formspec_label_bar(0, 0.8, S("Load"), PWR_CAPA, capa)..
power.formspec_label_bar(pos, 0, 0.8, S("Load"), PWR_CAPA, capa)..
"image_button[2.6,2;1,1;".. self:get_state_button_image(nvm) ..";state_button;]"..
"tooltip[3,2;1,1;"..self:get_state_tooltip(nvm).."]"..
"label[3.7,1.2;"..S("Electricity").."]"..

View File

@ -0,0 +1,236 @@
--[[
TechAge
=======
Copyright (C) 2019-2020 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
TA2 Power Generator
]]--
-- for lazy programmers
local M = minetest.get_meta
local S = techage.S
local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end
local S2P = minetest.string_to_pos
local Cable = techage.ElectricCable
local Axle = techage.Axle
local power = techage.power
local networks = techage.networks
local CYCLE_TIME = 2
local PWR_PERF = 24
local function swap_node(pos, name)
local node = techage.get_node_lvm(pos)
if node.name == name then
return
end
node.name = name
minetest.swap_node(pos, node)
end
local function on_power(pos)
local nvm = techage.get_nvm(pos)
nvm.axle = nvm.axle or {}
nvm.consumer_powered = true
M(pos):set_string("infotext", S("TA2 Power Generator"))
swap_node(pos, "techage:ta2_generator_on")
nvm.ticks = 0
local outdir = M(pos):get_int("outdir")
nvm.axle.curr_power = techage.power.needed_power(pos, Cable, outdir)
end
local function on_nopower(pos)
local nvm = techage.get_nvm(pos)
nvm.consumer_powered = false
if (nvm.ticks or 0) < 4 then
M(pos):set_string("infotext", S("TA2 Power Generator: Overload fault?\n(restart with right-click)"))
end
nvm.ticks = 0
end
local function node_timer(pos, elapsed)
local nvm = techage.get_nvm(pos)
nvm.axle = nvm.axle or {}
-- trigger network on consumer side
nvm.ticks = (nvm.ticks or 0) + 1
if nvm.ticks % 2 then
power.consumer_alive(pos, Axle, CYCLE_TIME)
end
-- handle generator side delayed
if nvm.ticks > 3 then
local outdir = M(pos):get_int("outdir")
if nvm.consumer_powered and not nvm.running_as_generator then
nvm.running_as_generator = true
power.generator_start(pos, Cable, CYCLE_TIME, outdir, nvm.max_power)
elseif not nvm.consumer_powered and nvm.running_as_generator then
nvm.running_as_generator = false
power.generator_stop(pos, Cable, outdir)
end
if nvm.running_as_generator then
nvm.axle.curr_power = power.generator_alive(pos, Cable, CYCLE_TIME, outdir, PWR_PERF) + 1
else
swap_node(pos, "techage:ta2_generator_off")
end
end
return true
end
local function tubelib2_on_update2(pos, outdir, tlib2, node)
local nvm = techage.get_nvm(pos)
nvm.axle = nvm.axle or {}
nvm.axle.curr_power = 1
power.update_network(pos, outdir, tlib2)
end
minetest.register_node("techage:ta2_generator_off", {
description = S("TA2 Power Generator"),
tiles = {
-- up, down, right, left, back, front
"techage_filling_ta2.png^techage_frame_ta2_top.png^techage_appl_arrow.png",
"techage_filling_ta2.png^techage_frame_ta2.png",
"techage_filling_ta2.png^techage_frame_ta2.png^techage_appl_hole_electric.png",
"techage_filling_ta2.png^techage_axle_clutch.png^techage_frame_ta2.png",
"techage_filling_ta2.png^techage_frame_ta2.png^techage_appl_generator_red.png",
"techage_filling_ta2.png^techage_frame_ta2.png^techage_appl_generator_red.png^[transformFX]",
},
paramtype2 = "facedir",
groups = {cracky=2, crumbly=2, choppy=2},
on_rotate = screwdriver.disallow,
is_ground_content = false,
after_place_node = function(pos)
local nvm = techage.get_nvm(pos)
nvm.axle = nvm.axle or {}
nvm.axle.curr_power = 1
nvm.consumer_powered = false
nvm.running_as_generator = false
M(pos):set_int("outdir", networks.side_to_outdir(pos, "R"))
M(pos):set_int("leftdir", networks.side_to_outdir(pos, "L"))
Cable:after_place_node(pos)
Axle:after_place_node(pos)
minetest.get_node_timer(pos):start(CYCLE_TIME)
power.consumer_start(pos, Axle, CYCLE_TIME*2)
M(pos):set_string("infotext", S("TA2 Power Generator"))
end,
on_rightclick = function(pos, node, clicker)
local nvm = techage.get_nvm(pos)
nvm.axle = nvm.axle or {}
nvm.axle.curr_power = 1
M(pos):set_string("infotext", S("TA2 Power Generator"))
end,
after_dig_node = function(pos, oldnode)
Cable:after_dig_node(pos)
Axle:after_dig_node(pos)
techage.del_mem(pos)
end,
tubelib2_on_update2 = tubelib2_on_update2,
on_timer = node_timer,
networks = {
ele1 = {
sides = {R = 1},
ntype = "gen1",
nominal = PWR_PERF,
},
axle = {
sides = {L = 1},
ntype = "con1",
on_power = on_power,
on_nopower = on_nopower,
},
}
})
minetest.register_node("techage:ta2_generator_on", {
description = S("TA2 Power Generator"),
tiles = {
-- up, down, right, left, back, front
"techage_filling_ta2.png^techage_frame_ta2_top.png^techage_appl_arrow.png",
"techage_filling_ta2.png^techage_frame_ta2.png",
"techage_filling_ta2.png^techage_frame_ta2.png^techage_appl_hole_electric.png",
{
image = "techage_filling4_ta2.png^techage_axle_clutch4.png^techage_frame4_ta2.png",
backface_culling = false,
animation = {
type = "vertical_frames",
aspect_w = 32,
aspect_h = 32,
length = 0.6,
},
},
{
image = "techage_filling4_ta2.png^techage_appl_generator_red4.png^techage_frame4_ta2.png",
backface_culling = false,
animation = {
type = "vertical_frames",
aspect_w = 32,
aspect_h = 32,
length = 0.3,
},
},
{
image = "techage_filling4_ta2.png^techage_appl_generator_red4.png^[transformFX]^techage_frame4_ta2.png",
backface_culling = false,
animation = {
type = "vertical_frames",
aspect_w = 32,
aspect_h = 32,
length = 0.3,
},
},
},
paramtype2 = "facedir",
on_rotate = screwdriver.disallow,
is_ground_content = false,
drop = "",
groups = {not_in_creative_inventory=1},
diggable = false,
tubelib2_on_update2 = tubelib2_on_update2,
on_timer = node_timer,
networks = {
ele1 = {
sides = {R = 1},
ntype = "gen1",
nominal = PWR_PERF,
},
axle = {
sides = {L = 1},
ntype = "con1",
on_power = on_power,
on_nopower = on_nopower,
},
}
})
techage.register_node({"techage:ta2_generator_off", "techage:ta2_generator_on"}, {
on_node_load = function(pos, node)
minetest.get_node_timer(pos):start(CYCLE_TIME)
end,
})
Cable:add_secondary_node_names({"techage:ta2_generator_off", "techage:ta2_generator_on"})
Axle:add_secondary_node_names({"techage:ta2_generator_off", "techage:ta2_generator_on"})
minetest.register_craft({
output = "techage:ta2_generator_off",
recipe = {
{"basic_materials:steel_bar", "dye:red", "default:wood"},
{'techage:axle', 'basic_materials:gear_steel', 'techage:electric_cableS'},
{"default:wood", "techage:iron_ingot", "basic_materials:steel_bar"},
},
})

View File

@ -0,0 +1,245 @@
--[[
TechAge
=======
Copyright (C) 2019-2020 Joachim Stolberg
AGPL v3
See LICENSE.txt for more information
TA3 Electric Motor
]]--
-- for lazy programmers
local M = minetest.get_meta
local S = techage.S
local P2S = function(pos) if pos then return minetest.pos_to_string(pos) end end
local S2P = minetest.string_to_pos
local Cable = techage.ElectricCable
local Axle = techage.Axle
local power = techage.power
local networks = techage.networks
local CYCLE_TIME = 2
local PWR_PERF = 40
-- Axles texture animation
local function switch_axles(pos, on)
for _,outdir in ipairs(networks.get_node_connections(pos, "axle")) do
Axle:switch_tube_line(pos, outdir, on and "on" or "off")
end
end
local function swap_node(pos, name)
local node = techage.get_node_lvm(pos)
if node.name == name then
return
end
node.name = name
minetest.swap_node(pos, node)
end
local function on_power(pos)
local nvm = techage.get_nvm(pos)
nvm.ele1 = nvm.ele1 or {}
nvm.consumer_powered = true
M(pos):set_string("infotext", S("TA3 Electric Motor"))
swap_node(pos, "techage:ta3_motor_on")
nvm.ticks = 0
local outdir = M(pos):get_int("outdir")
nvm.ele1.curr_power = techage.power.needed_power(pos, Axle, outdir)
end
local function on_nopower(pos)
local nvm = techage.get_nvm(pos)
nvm.consumer_powered = false
if (nvm.ticks or 0) < 4 then
M(pos):set_string("infotext", S("TA3 Electric Motor: Overload fault?\n(restart with right-click)"))
end
nvm.ticks = 0
end
local function node_timer(pos, elapsed)
local nvm = techage.get_nvm(pos)
nvm.ele1 = nvm.ele1 or {}
-- trigger network on consumer side
nvm.ticks = (nvm.ticks or 0) + 1
if nvm.ticks % 2 then
power.consumer_alive(pos, Cable, CYCLE_TIME)
end
-- handle generator side delayed
if nvm.ticks > 3 then
local outdir = M(pos):get_int("outdir")
if nvm.consumer_powered and not nvm.running_as_generator then
nvm.running_as_generator = true
power.generator_start(pos, Axle, CYCLE_TIME, outdir, nvm.max_power)
switch_axles(pos, true)
elseif not nvm.consumer_powered and nvm.running_as_generator then
nvm.running_as_generator = false
power.generator_stop(pos, Axle, outdir)
switch_axles(pos, false)
end
if nvm.running_as_generator then
nvm.ele1.curr_power = power.generator_alive(pos, Axle, CYCLE_TIME, outdir, PWR_PERF) + 1
else
swap_node(pos, "techage:ta3_motor_off")
end
end
return true
end
local function tubelib2_on_update2(pos, outdir, tlib2, node)
local nvm = techage.get_nvm(pos)
nvm.ele1 = nvm.ele1 or {}
nvm.ele1.curr_power = 1
power.update_network(pos, outdir, tlib2)
end
minetest.register_node("techage:ta3_motor_off", {
description = S("TA3 Electric Motor"),
tiles = {
-- up, down, right, left, back, front
"techage_filling_ta3.png^techage_frame_ta3_top.png^techage_appl_arrow.png",
"techage_filling_ta3.png^techage_frame_ta3.png",
"techage_filling_ta3.png^techage_axle_clutch.png^techage_frame_ta3.png",
"techage_filling_ta3.png^techage_appl_hole_electric.png^techage_frame_ta3.png",
"techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_generator_red.png^[transformFX]",
"techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_generator_red.png",
},
paramtype2 = "facedir",
groups = {cracky=2, crumbly=2, choppy=2},
on_rotate = screwdriver.disallow,
is_ground_content = false,
after_place_node = function(pos)
local nvm = techage.get_nvm(pos)
nvm.ele1 = nvm.ele1 or {}
nvm.ele1.curr_power = 1
nvm.consumer_powered = false
nvm.running_as_generator = false
M(pos):set_int("outdir", networks.side_to_outdir(pos, "R"))
M(pos):set_int("leftdir", networks.side_to_outdir(pos, "L"))
Cable:after_place_node(pos)
Axle:after_place_node(pos)
minetest.get_node_timer(pos):start(CYCLE_TIME)
power.consumer_start(pos, Cable, CYCLE_TIME*2)
M(pos):set_string("infotext", S("TA3 Electric Motor"))
end,
on_rightclick = function(pos, node, clicker)
local nvm = techage.get_nvm(pos)
nvm.ele1 = nvm.ele1 or {}
nvm.ele1.curr_power = 1
M(pos):set_string("infotext", S("TA3 Electric Motor"))
end,
after_dig_node = function(pos, oldnode)
Cable:after_dig_node(pos)
Axle:after_dig_node(pos)
techage.del_mem(pos)
end,
tubelib2_on_update2 = tubelib2_on_update2,
on_timer = node_timer,
networks = {
axle = {
sides = {R = 1},
ntype = "gen1",
nominal = PWR_PERF,
},
ele1 = {
sides = {L = 1},
ntype = "con1",
on_power = on_power,
on_nopower = on_nopower,
},
}
})
minetest.register_node("techage:ta3_motor_on", {
description = S("TA3 Electric Motor"),
tiles = {
-- up, down, right, left, back, front
"techage_filling_ta3.png^techage_frame_ta3_top.png^techage_appl_arrow.png",
"techage_filling_ta3.png^techage_frame_ta3.png",
{
image = "techage_filling4_ta3.png^techage_axle_clutch4.png^techage_frame4_ta3.png",
backface_culling = false,
animation = {
type = "vertical_frames",
aspect_w = 32,
aspect_h = 32,
length = 0.6,
},
},
"techage_filling_ta3.png^techage_frame_ta3.png^techage_appl_hole_electric.png",
{
image = "techage_filling4_ta3.png^techage_appl_generator_red4.png^[transformFX]^techage_frame4_ta3.png",
backface_culling = false,
animation = {
type = "vertical_frames",
aspect_w = 32,
aspect_h = 32,
length = 0.3,
},
},
{
image = "techage_filling4_ta3.png^techage_appl_generator_red4.png^techage_frame4_ta3.png",
backface_culling = false,
animation = {
type = "vertical_frames",
aspect_w = 32,
aspect_h = 32,
length = 0.3,
},
},
},
paramtype2 = "facedir",
on_rotate = screwdriver.disallow,
is_ground_content = false,
drop = "",
groups = {not_in_creative_inventory=1},
diggable = false,
tubelib2_on_update2 = tubelib2_on_update2,
on_timer = node_timer,
networks = {
axle = {
sides = {R = 1},
ntype = "gen1",
nominal = PWR_PERF,
},
ele1 = {
sides = {L = 1},
ntype = "con1",
on_power = on_power,
on_nopower = on_nopower,
},
}
})
techage.register_node({"techage:ta3_motor_off", "techage:ta3_motor_on"}, {
on_node_load = function(pos, node)
minetest.get_node_timer(pos):start(CYCLE_TIME)
end,
})
Cable:add_secondary_node_names({"techage:ta3_motor_off", "techage:ta3_motor_on"})
Axle:add_secondary_node_names({"techage:ta3_motor_off", "techage:ta3_motor_on"})
minetest.register_craft({
output = "techage:ta3_motor_off",
recipe = {
{"basic_materials:steel_bar", "dye:red", "default:wood"},
{'techage:electric_cableS', 'basic_materials:gear_steel', 'techage:axle'},
{"default:wood", "techage:iron_ingot", "basic_materials:steel_bar"},
},
})

View File

@ -39,7 +39,7 @@ local function formspec(self, pos, nvm)
"image[1.4,1.6;1,1;techage_form_arrow_bg.png^[transformR270]"..
"image_button[1.4,3.2;1,1;".. self:get_state_button_image(nvm) ..";state_button;]"..
"tooltip[1.5,3;1,1;"..self:get_state_tooltip(nvm).."]"..
power.formspec_label_bar(2.5, 0.8, S("Electricity"), PWR_CAPA, nvm.provided)
power.formspec_label_bar(pos, 2.5, 0.8, S("Electricity"), PWR_CAPA, nvm.provided)
end
local function play_sound(pos)

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

@ -245,6 +245,11 @@ local function read_state(itemstack, user, pointed_thing)
fuel = dump(fuel)
minetest.chat_send_player(user:get_player_name(), ndef.description.." "..number..": fuel = "..fuel.." ")
end
local output = techage.send_single("0", number, "output", nil)
if output and output ~= "" and output ~= "unsupported" then
output = dump(output)
minetest.chat_send_player(user:get_player_name(), ndef.description.." "..number..": output = "..output.." ")
end
local load, abs = techage.send_single("0", number, "load", nil)
if load and load ~= "" and load ~= "unsupported" then
load, abs = dump(load), abs and dump(abs) or "--"

View File

@ -51,46 +51,9 @@ end
local function add_rotor(pos, nvm, player_name)
nvm.error = false
-- Check for next wind turbine
local pos1 = {x=pos.x-13, y=pos.y-9, z=pos.z-13}
local pos2 = {x=pos.x+13, y=pos.y+10, z=pos.z+13}
local num = #minetest.find_nodes_in_area(pos1, pos2, {"techage:ta4_wind_turbine"})
if num > 1 then
if player_name then
techage.mark_region(player_name, pos1, pos2, "")
minetest.chat_send_player(player_name, S("[TA4 Wind Turbine]")..
" "..S("The wind turbines are too close together!"))
end
M(pos):set_string("infotext", S("TA4 Wind Turbine").." "..S("Error"))
nvm.error = true
return
end
-- Check for water surface (occean)
pos1 = {x=pos.x-20, y=0, z=pos.z-20}
pos2 = {x=pos.x+20, y=1, z=pos.z+20}
local num = #minetest.find_nodes_in_area(pos1, pos2, {"default:water_source", "default:water_flowing", "ignore"})
if num < (41*41*2-MAX_NUM_FOREIGN_NODES) then
if player_name then
techage.mark_region(player_name, pos1, pos2, "")
minetest.chat_send_player(player_name, S("[TA4 Wind Turbine]")..
" "..S("More water expected (2 m deep)!"))
end
M(pos):set_string("infotext", S("TA4 Wind Turbine").." "..S("Error"))
nvm.error = true
return
end
if pos.y < 12 or pos.y > 20 then
if player_name then
pos1 = {x=pos.x-13, y=12, z=pos.z-13}
pos2 = {x=pos.x+13, y=20, z=pos.z+13}
techage.mark_region(player_name, pos1, pos2, "")
minetest.chat_send_player(player_name, S("[TA4 Wind Turbine]")..
" "..S("No wind at this altitude!"))
end
M(pos):set_string("infotext", S("TA4 Wind Turbine").." "..S("Error"))
if not techage.valid_place_for_windturbine(pos, nil, 1) then
nvm.error = true
M(pos):set_string("infotext", S("TA4 Wind Turbine")..": "..S("Not suitable position!"))
return
end

View File

@ -58,7 +58,7 @@ function tubelib2.get_node_lvm(pos)
local data = vm:get_data()
local param2_data = vm:get_param2_data()
local area = VoxelArea:new({MinEdge = MinEdge, MaxEdge = MaxEdge})
local idx = area:index(pos.x, pos.y, pos.z)
local idx = area:indexp(pos)
node = {
name = minetest.get_name_from_content_id(data[idx]),
param2 = param2_data[idx]