many worldgen mods and gonna try technic now

This commit is contained in:
tangent committed 2026-09-13 03:30:48 -06:00
1 parent bac6ac94a3
commit 548749cb1a
4811 files changed
+163271 -1

No files matched your search

+38
View File
@@ -0,0 +1,38 @@
Technic
=======
License
-------
Copyright (C) 2012-2014 Maciej Kasatkin (RealBadAngel)
Technic chests code is licensed under the GNU LGPLv2+.
Texture licenses:
BlockMen modified by Zefram (CC BY-SA 3.0):
* technic_chernobylite_block.png
* technic_corium_flowing_animated.png
* technic_corium_source_animated.png
celeron55 (Perttu Ahola) modified by Zefram (CC BY-SA 3.0):
* technic_bucket_corium.png
sdzen (Elise Staudter) (CC BY-SA 3.0):
* most of the older 16x16 textures
leftshift (CC BY-SA 3.0):
* technic_river_water_can.png
RealBadAngel: (WTFPL)
* Everything else.
CC BY-SA 3.0: <http://creativecommons.org/licenses/by-sa/3.0/>
Sound licenses:
veikk0 (Veikko Mäkelä) (CC BY-SA 4.0):
* technic_hv_nuclear_reactor_siren_danger_loop.ogg
* Derived from "Nuclear alarm.wav" by Freesound.org user rene___ from <https://freesound.org/people/rene___/sounds/56778/>. Originally licensed under CC0 1.0 <https://creativecommons.org/publicdomain/zero/1.0/>
CC BY-SA 4.0: <https://creativecommons.org/licenses/by-sa/4.0/>
+32
View File
@@ -0,0 +1,32 @@
technic.config = technic.config or Settings(minetest.get_worldpath().."/technic.conf")
local conf_table = technic.config:to_table()
local defaults = {
enable_mining_drill = "true",
enable_mining_laser = "false",
enable_flashlight = "false",
enable_wind_mill = "false",
enable_frames = "false",
enable_corium_griefing = "true",
enable_radiation_protection = "true",
enable_radiation_throttling = "false",
enable_entity_radiation_damage = "true",
enable_longterm_radiation_damage = "true",
enable_nuclear_reactor_digiline_selfdestruct = "false",
switch_max_range = "256",
switch_off_delay_seconds = "1800",
network_overload_reset_time = "20",
admin_priv = "basic_privs",
quarry_dig_above_nodes = "3",
quarry_max_depth = "100",
quarry_time_limit = "5000",
max_lag_reduction_multiplier = "0.99",
--constant_digit_count = nil,
}
for k, v in pairs(defaults) do
if conf_table[k] == nil then
technic.config:set(k, v)
end
end
+188
View File
@@ -0,0 +1,188 @@
-- check if we have the necessary dependencies to allow actually using these materials in the crafts
local mesecons_materials = minetest.get_modpath("mesecons_materials")
-- Remove some recipes
-- Bronze
minetest.clear_craft({
type = "shapeless",
output = "default:bronze_ingot"
})
-- Restore recipe for bronze block to ingots
minetest.register_craft({
output = "default:bronze_ingot 9",
recipe = {
{"default:bronzeblock"}
}
})
-- Accelerator tube
if pipeworks.enable_accelerator_tube then
minetest.clear_craft({
output = "pipeworks:accelerator_tube_1",
})
minetest.register_craft({
output = 'pipeworks:accelerator_tube_1',
recipe = {
{'technic:copper_coil', 'pipeworks:tube_1', 'technic:copper_coil'},
}
})
end
-- Teleport tube
if pipeworks.enable_teleport_tube then
minetest.clear_craft({
output = "pipeworks:teleport_tube_1",
})
minetest.register_craft({
output = 'pipeworks:teleport_tube_1',
recipe = {
{'default:mese_crystal', 'technic:copper_coil', 'default:mese_crystal'},
{'pipeworks:tube_1', 'technic:control_logic_unit', 'pipeworks:tube_1'},
{'default:mese_crystal', 'technic:copper_coil', 'default:mese_crystal'},
}
})
end
-- basic materials' brass ingot
minetest.clear_craft({
output = "basic_materials:brass_ingot",
})
minetest.register_craft( {
type = "shapeless",
output = "basic_materials:brass_ingot 9",
recipe = { "basic_materials:brass_block" },
})
-- tubes crafting recipes
minetest.register_craft({
output = 'technic:diamond_drill_head',
recipe = {
{'technic:stainless_steel_ingot', 'default:diamond', 'technic:stainless_steel_ingot'},
{'default:diamond', '', 'default:diamond'},
{'technic:stainless_steel_ingot', 'default:diamond', 'technic:stainless_steel_ingot'},
}
})
minetest.register_craft({
output = 'technic:green_energy_crystal',
recipe = {
{'default:gold_ingot', 'technic:battery', 'dye:green'},
{'technic:battery', 'technic:red_energy_crystal', 'technic:battery'},
{'dye:green', 'technic:battery', 'default:gold_ingot'},
}
})
minetest.register_craft({
output = 'technic:blue_energy_crystal',
recipe = {
{'moreores:mithril_ingot', 'technic:battery', 'dye:blue'},
{'technic:battery', 'technic:green_energy_crystal', 'technic:battery'},
{'dye:blue', 'technic:battery', 'moreores:mithril_ingot'},
}
})
minetest.register_craft({
output = 'technic:red_energy_crystal',
recipe = {
{'moreores:silver_ingot', 'technic:battery', 'dye:red'},
{'technic:battery', 'basic_materials:energy_crystal_simple', 'technic:battery'},
{'dye:red', 'technic:battery', 'moreores:silver_ingot'},
}
})
minetest.register_craft({
output = 'technic:copper_coil 1',
recipe = {
{'basic_materials:copper_wire', 'technic:wrought_iron_ingot', 'basic_materials:copper_wire'},
{'technic:wrought_iron_ingot', '', 'technic:wrought_iron_ingot'},
{'basic_materials:copper_wire', 'technic:wrought_iron_ingot', 'basic_materials:copper_wire'},
},
replacements = {
{"basic_materials:copper_wire", "basic_materials:empty_spool"},
{"basic_materials:copper_wire", "basic_materials:empty_spool"},
{"basic_materials:copper_wire", "basic_materials:empty_spool"},
{"basic_materials:copper_wire", "basic_materials:empty_spool"}
},
})
local isolation = mesecons_materials and "mesecons_materials:fiber" or "technic:rubber"
minetest.register_craft({
output = 'technic:lv_transformer',
recipe = {
{isolation, 'technic:wrought_iron_ingot', isolation},
{'technic:copper_coil', 'technic:wrought_iron_ingot', 'technic:copper_coil'},
{'technic:wrought_iron_ingot', 'technic:wrought_iron_ingot', 'technic:wrought_iron_ingot'},
}
})
minetest.register_craft({
output = 'technic:mv_transformer',
recipe = {
{isolation, 'technic:carbon_steel_ingot', isolation},
{'technic:copper_coil', 'technic:carbon_steel_ingot', 'technic:copper_coil'},
{'technic:carbon_steel_ingot', 'technic:carbon_steel_ingot', 'technic:carbon_steel_ingot'},
}
})
minetest.register_craft({
output = 'technic:hv_transformer',
recipe = {
{isolation, 'technic:stainless_steel_ingot', isolation},
{'technic:copper_coil', 'technic:stainless_steel_ingot', 'technic:copper_coil'},
{'technic:stainless_steel_ingot', 'technic:stainless_steel_ingot', 'technic:stainless_steel_ingot'},
}
})
minetest.register_craft({
output = 'technic:control_logic_unit',
recipe = {
{'', 'basic_materials:gold_wire', ''},
{'default:copper_ingot', 'technic:silicon_wafer', 'default:copper_ingot'},
{'', 'technic:chromium_ingot', ''},
},
replacements = { {"basic_materials:gold_wire", "basic_materials:empty_spool"}, },
})
minetest.register_craft({
output = 'technic:mixed_metal_ingot 9',
recipe = {
{'technic:stainless_steel_ingot', 'technic:stainless_steel_ingot', 'technic:stainless_steel_ingot'},
{'default:bronze_ingot', 'default:bronze_ingot', 'default:bronze_ingot'},
{'default:tin_ingot', 'default:tin_ingot', 'default:tin_ingot'},
}
})
minetest.register_craft({
output = 'technic:carbon_cloth',
recipe = {
{'technic:graphite', 'technic:graphite', 'technic:graphite'}
}
})
minetest.register_craft({
output = "technic:machine_casing",
recipe = {
{ "technic:cast_iron_ingot", "technic:cast_iron_ingot", "technic:cast_iron_ingot" },
{ "technic:cast_iron_ingot", "basic_materials:brass_ingot", "technic:cast_iron_ingot" },
{ "technic:cast_iron_ingot", "technic:cast_iron_ingot", "technic:cast_iron_ingot" },
},
})
minetest.register_craft({
output = "default:dirt 2",
type = "shapeless",
replacements = {{"bucket:bucket_water","bucket:bucket_empty"}},
recipe = {
"technic:stone_dust",
"group:leaves",
"bucket:bucket_water",
"group:sand",
},
})
+15
View File
@@ -0,0 +1,15 @@
default
pipeworks
technic_worldgen
basic_materials
bucket?
screwdriver?
mesecons?
mesecons_mvps?
digilines?
digiline_remote?
digistuff?
intllib?
unified_inventory?
vector_extras?
dye?
+57
View File
@@ -0,0 +1,57 @@
administrative world anchor
---------------------------
A world anchor is an object in the Minetest world that causes the server
to keep surrounding parts of the world running even when no players
are nearby. It is mainly used to allow machines to run unattended:
normally machines are suspended when not near a player. The technic
mod supplies a form of world anchor, as a placable block, but it is not
straightforwardly available to players. There is no recipe for it, so it
is only available if explicitly spawned into existence by someone with
administrative privileges. In a single-player world, the single player
normally has administrative privileges, and can obtain a world anchor
by entering the chat command "/give singleplayer technic:admin\_anchor".
The world anchor tries to force a cubical area, centered upon the anchor,
to stay loaded. The distance from the anchor to the most distant map
nodes that it will keep loaded is referred to as the "radius", and can be
set in the world anchor's interaction form. The radius can be set as low
as 0, meaning that the anchor only tries to keep itself loaded, or as high
as 255, meaning that it will operate on a 511&times;511&times;511 cube.
Larger radii are forbidden, to avoid typos causing the server excessive
work; to keep a larger area loaded, use multiple anchors. Also use
multiple anchors if the area to be kept loaded is not well approximated
by a cube.
The world is always kept loaded in units of 16&times;16&times;16 cubes,
confusingly known as "map blocks". The anchor's configured radius takes
no account of map block boundaries, but the anchor's effect is actually to
keep loaded each map block that contains any part of the configured cube.
The anchor's interaction form includes a status note showing how many map
blocks this is, and how many of those it is successfully keeping loaded.
When the anchor is disabled, as it is upon placement, it will always
show that it is keeping no map blocks loaded; this does not indicate
any kind of failure.
The world anchor can optionally be locked. When it is locked, only
the anchor's owner, the player who placed it, can reconfigure it or
remove it. Only the owner can lock it. Locking an anchor is useful
if the use of anchors is being tightly controlled by administrators:
an administrator can set up a locked anchor and be sure that it will
not be set by ordinary players to an unapproved configuration.
The server limits the ability of world anchors to keep parts of the world
loaded, to avoid overloading the server. The total number of map blocks
that can be kept loaded in this way is set by the server configuration
item "max\_forceloaded\_blocks" (in minetest.conf), which defaults to
only 16. For comparison, each player normally keeps 125 map blocks loaded
(a radius of 32). If an enabled world anchor shows that it is failing to
keep all the map blocks loaded that it would like to, this can be fixed
by increasing max\_forceloaded\_blocks by the amount of the shortfall.
The tight limit on force-loading is the reason why the world anchor is
not directly available to players. With the limit so low both by default
and in common practice, the only feasible way to determine where world
anchors should be used is for administrators to decide it directly.
+144
View File
@@ -0,0 +1,144 @@
This file is fairly incomplete. Help is welcome.
Tiers
-----
The tier is a string, currently `"LV"`, `"MV"` and `"HV"` are supported.
Network
-------
The network is the cable with the connected machine nodes. Currently the
switching station handles the network activity.
Helper functions
----------------
* `technic.EU_string(num)`
* Converts num to a human-readable string (see pretty_num)
and adds the `EU` unit
* Use this function when showing players energy values
* `technic.pretty_num(num)`
* Converts the number `num` to a human-readable string with SI prefixes
* `technic.swap_node(pos, nodename)`
* Same as `mintest.swap_node` but it only changes the nodename.
* It uses `minetest.get_node` before swapping to ensure the new nodename
is not the same as the current one.
* `technic.get_or_load_node(pos)`
* If the mapblock is loaded, it returns the node at pos,
else it loads the chunk and returns `nil`.
* `technic.set_RE_wear(itemstack, item_load, max_charge)`
* If the `wear_represents` field in the item's nodedef is
`"technic_RE_charge"`, this function does nothing.
* `technic.refill_RE_charge(itemstack)`
* This function fully recharges an RE chargeable item.
* If `technic.power_tools[itemstack:get_name()]` is `nil` (or `false`), this
function does nothing, else that value is the maximum charge.
* The itemstack metadata is changed to contain the charge.
* `technic.is_tier_cable(nodename, tier)`
* Tells whether the node `nodename` is the cable of the tier `tier`.
* `technic.get_cable_tier(nodename)`
* Returns the tier of the cable `nodename` or `nil`.
* `technic.trace_node_ray(pos, dir, range)`
* Returns an iteration function (usable in the for loop) to iterate over the
node positions along the specified ray.
* The returned positions will not include the starting position `pos`.
* `technic.trace_node_ray_fat(pos, dir, range)`
* Like `technic.trace_node_ray` but includes extra positions near the ray.
* The node ray functions are used for mining lasers.
* `technic.config:get(name)`
* Some configuration function
* `technic.tube_inject_item(pos, start_pos, velocity, item)`
* Same as `pipeworks.tube_inject_item`
* `technic.get_charge(itemstack)` (alias `technic.get_RE_charge`)
* Returns current charge level of tool.
* `technic.set_charge(itemstack, charge)` (alias `technic.set_RE_charge`)
* Sets tool charge level.
* `technic.use_charge(itemstack, charge)` (alias `technic.use_RE_charge`)
* Attempt to use charge and return `true`/`false` indicating success.
* Always succeeds without checking charge level if creative is enabled.
Registration functions
----------------------
* `technic.register_power_tool(itemname, max_charge)`
* Same as `technic.power_tools[itemname] = max_charge`
* This function makes the craftitem `itemname` chargeable.
* `technic.register_machine(tier, nodename, machine_type)`
* Same as `technic.machines[tier][nodename] = machine_type`
* Currently this is requisite to make technic recognize your node.
* See also `Machine types`
* `technic.register_tier(tier)`
* Same as `technic.machines[tier] = {}`
* See also `tiers`
### Specific machines
* `technic.register_solar_array(data)`
* data is a table
* `technic.can_insert_unique_stack(pos, node, stack, direction)`
* `technic.insert_object_unique_stack(pos, node, stack, direction)`
* Functions for the parameters `can_insert` and `insert_object` to avoid
filling multiple inventory slots with same type of item.
Used itemdef fields
-------------------
* groups:
* `technic_<ltier> = 1` ltier is a tier in small letters; this group makes
the node connect to the cable(s) of the right tier.
* `technic_machine = 1` Currently used for
* `connect_sides`
* In addition to the default use (see lua_api.txt), this tells where the
machine can be connected.
#
#
* `technic_run(pos, node)`
* This function is currently used to update the node.
Modders have to manually change the information about supply etc. in the
node metadata.
Machine types
-------------
There are currently following types:
* `technic.receiver = "RE"` e.g. grinder
* `technic.producer = "PR"` e.g. solar panel
* `technic.producer_receiver = "PR_RE"` supply converter
* `technic.battery = "BA"` e.g. LV batbox
Switching Station
-----------------
The switching station is the center of all power distribution on an electric
network.
The station collects power from sources (PR), distributes it to sinks (RE),
and uses the excess/shortfall to charge and discharge batteries (BA).
For now, all supply and demand values are expressed in kW.
It works like this:
All PR,BA,RE nodes are indexed and tagged with the switching station.
The tagging is a workaround to allow more stations to be built without allowing
a cheat with duplicating power.
All the RE nodes are queried for their current EU demand. Those which are off
would require no or a small standby EU demand, while those which are on would
require more.
If the total demand is less than the available power they are all updated with
the demand number.
If any surplus exists from the PR nodes the batteries will be charged evenly
with this.
If the total demand requires draw on the batteries they will be discharged
evenly.
If the total demand is more than the available power all RE nodes will be shut
down. We have a brown-out situation.
Hence for now all the power distribution logic resides in this single node.
### Node meta usage
Nodes connected to the network will have one or more of these parameters as meta
data:
* `<LV|MV|HV>_EU_supply` : Exists for PR and BA node types.
This is the EU value supplied by the node. Output
* `<LV|MV|HV>_EU_demand` : Exists for RE and BA node types.
This is the EU value the node requires to run. Output
* `<LV|MV|HV>_EU_input` : Exists for RE and BA node types.
This is the actual EU value the network can give the node. Input
The reason the LV|MV|HV type is prepended to meta data is because some machine
could require several supplies to work.
This way the supplies are separated per network.
+52
View File
@@ -0,0 +1,52 @@
chests
------
The technic mod replaces the basic Minetest game's single type of
chest with a range of chests that have different sizes and features.
The chest types are identified by the materials from which they are made;
the better chests are made from more exotic materials. The chest types
form a linear sequence, each being (with one exception noted below)
strictly more powerful than the preceding one. The sequence begins with
the wooden chest from the basic game, and each later chest type is built
by upgrading a chest of the preceding type. The chest types are:
1. wooden chest: 8&times;4 (32) slots
2. iron chest: 9&times;5 (45) slots
3. copper chest: 12&times;5 (60) slots
4. silver chest: 12&times;6 (72) slots
5. gold chest: 15&times;6 (90) slots
6. mithril chest: 15&times;6 (90) slots
The iron and later chests have the ability to sort their contents,
when commanded by a button in their interaction forms. Item types are
sorted in the same order used in the unified\_inventory craft guide.
The copper and later chests also have an auto-sorting facility that can
be enabled from the interaction form. An auto-sorting chest automatically
sorts its contents whenever a player closes the chest. The contents will
then usually be in a sorted state when the chest is opened, but may not
be if pneumatic tubes have operated on the chest while it was closed,
or if two players have the chest open simultaneously.
The silver and gold chests, but not the mithril chest, have a built-in
sign-like capability. They can be given a textual label, which will
be visible when hovering over the chest. The gold chest, but again not
the mithril chest, can be further labelled with a colored patch that is
visible from a moderate distance.
The mithril chest is currently an exception to the upgrading system.
It has only as many inventory slots as the preceding (gold) type, and has
fewer of the features. It has no feature that other chests don't have:
it is strictly weaker than the gold chest. It is planned that in the
future it will acquire some unique features, but for now the only reason
to use it is aesthetic.
The size of the largest chests is dictated by the maximum size
of interaction form that the game engine can successfully display.
If in the future the engine becomes capable of handling larger forms,
by scaling them to fit the screen, the sequence of chest sizes will
likely be revised.
As with the chest of the basic Minetest game, each chest type comes
in both locked and unlocked flavors. All of the chests work with the
pneumatic tubes of the pipeworks mod.
+132
View File
@@ -0,0 +1,132 @@
# Technic digilines compatibility
This document describes the interaction of
technic machines with the `digilines` mod (https://github.com/minetest-mods/digilines)
## Switching station
**NOTE**: make sure the channel is set accordingly, "switch" in this case
There are two ways getting information from the switching station:
1. Give it a mesecon signal (eg. with a lever) and it will send always when the supply or demand changes.
2. Send to the switching station `"get"` or `"GET"` and it will send back.
The sent message is always a table containing the supply, demand and lag.
Example luacontroller code:
```lua
if event.type == "program" then
-- start interrupt cycle
interrupt(1)
end
if event.type == "interrupt" then
-- request stats
digiline_send("switch", "GET")
end
if event.type == "digiline" and event.channel == "switch" then
--[[
event.msg = {
demand = 0, -- demand in EU's
supply = 0, -- supply in EU's
lag = 0 -- generated lag in microseconds
}
--]]
end
```
## Power monitor
The commands:
- `"get"`: The power monitor sends back information about the attached network
- `"supply"`, `"demand"` and `"lag"`: Same as switching station
- `"battery_count"`, `"battery_charge"` and `"battery_charge_max"`: Totaled information about the attached batteries.
## Supply Converter
You can send the following to it:
- `"get"`: It will send back a table containing the fields `"enabled"`, `"power"` and `"mesecon_mode"` which are all integers.
- `"off"`: Deactivate the supply converter.
- `"on"`: Activate the supply converter.
- `"toggle"`: Activate or deactivate the supply converter depending on its current state.
- `"power "..power`: Set the amount of the power, it shall convert.
- `"mesecon_mode "..<int>`: Set the mesecon mode.
## Battery Boxes
Send to it `"get"` or `"GET"` and it will send a table back containing:
- `demand`: A number.
- `supply`: A number.
- `input`: A number.
- `charge`: A number.
- `max_charge`: A number.
- `src`: Itemstack made to table.
- `dst`: Itemstack made to table.
- `upgrade1`: Itemstack made to table.
- `upgrade2`: Itemstack made to table.
## Forcefield Emitter
You should send a table to it containing the `command` and for some commands the `value` field.
Some strings will automatically be made to tables:
- `"get"` :arrow_right: `{command = "get"}`
- `"off"` :arrow_right: `{command = "off"}`
- `"on"` :arrow_right: `{command = "on"}`
- `"toggle"` :arrow_right: `{command = "toggle"}`
- `"range "..range` :arrow_right: `{command = "range", value = range}`
- `"shape "..shape` :arrow_right: `{command = "shape", value = shape}`
The commands:
- `"get"`: The forcefield emitter sends back a table containing:
- `"enabled"`: `0` is off, `1` is on.
- `"range"`
- `"shape"`: `0` is spheric, `1` is cubic.
- `"off"`: Deactivate the forcefield emitter.
- `"on"`: Activate the forcefield emitter.
- `"toggle"`: Activate or deactivate the forcefield emitter depending on its current state.
- `"range"`: Set the range to `value`.
- `"shape"`: `value` can be a number (`0` or `1`) or a string (`"sphere"` or `"cube"`).
## Nuclear Reactor
Since the nuclear reactor core can't be accessed by digiline wire because the water layer which mustn't have a hole, you need the HV Digicables to interact with it.
You should send a table to it containing at least the `command` field and for some commands other fields.
The commands:
- `"get"`: The nuclear reactor sends back a table containing:
- `"burn_time"`: The time in seconds how long the reactor already runs. One week after start, when it reaches 7 * 24 * 60 * 60 (=604800), the fuel is completely used.
- `"enabled"`: A bool.
- `"siren"`: A bool.
- `"structure_accumulated_badness"`
- `"rods"`: A table with 6 numbers in it, one for each fuel slot.
- A positive value is the count of fuel rods in the slot.
- `0` means the slot is empty.
- A negative value means some other items are in the slot. The absolute value is the count of these items.
- `"self_destruct"`: A setting has to be enabled to use this. The reactor will melt down after `timer` seconds or instantly.
- `"start"`: Tries to start the reactor, `"Start successful"` is sent back on success, `"Error"` if something is wrong.
If the automatic start is enabled, it will always send `"fuel used"` when it uses fuel.
## Quarry
You should send a table to it containing the `command` and for some commands the `value` field.
Some strings will automatically be converted to tables:
- `"get"` :arrow_right: `{command = "get"}`
- `"on"` :arrow_right: `{command = "on"}`
- `"off"` :arrow_right: `{command = "off"}`
- `"restart` :arrow_right: `{command = "restart"}`
- `"radius "..value` :arrow_right: `{command = "radius", value = value}`
The commands:
- `"get"`: The forcefield emitter sends back a table containing:
- `"enabled"`: `0` is off, `1` is on.
- `"radius"`
- `"finished"`
- `"dug_nodes"`
- `"dig_level"`: A negative value means above the quarry, a positive value means below.
- `"on"`: Activate the quarry.
- `"off"`: Deactivate the quarry.
- `"restart"`: Restart the quarry.
- `"radius"`: Set the radius to `value` and restart the quarry if the radius changed.
+221
View File
@@ -0,0 +1,221 @@
power generators
----------------
### fuel-fired generators ###
The fuel-fired generators are electrical power generators that generate
power by the combustion of fuel. Versions of them are available for
all three voltages (LV, MV, and HV). These are all capable of burning
any type of combustible fuel, such as coal. They are relatively easy
to build, and so tend to be the first kind of generator used to power
electrical machines. In this role they form an intermediate step between
the directly fuel-fired machines and a more mature electrical network
powered by means other than fuel combustion. They are also, by virtue of
simplicity and controllability, a useful fallback or peak load generator
for electrical networks that normally use more sophisticated generators.
The MV and HV fuel-fired generators can accept fuel via pneumatic tube,
from any direction.
Keeping a fuel-fired generator fully fuelled is usually wasteful, because
it will burn fuel as long as it has any, even if there is no demand for
the electrical power that it generates. This is unlike the directly
fuel-fired machines, which only burn fuel when they have work to do.
To satisfy intermittent demand without waste, a fuel-fired generator must
only be given fuel when there is either demand for the energy or at least
sufficient battery capacity on the network to soak up the excess energy.
The higher-tier fuel-fired generators get much more energy out of a
fuel item than the lower-tier ones. The difference is much more than
is needed to overcome the inefficiency of supply converters, so it is
worth operating fuel-fired generators at a higher tier than the machines
being powered.
### solar generators ###
The solar generators are electrical power generators that generate power
from sunlight. Versions of them are available for all three voltages
(LV, MV, and HV). There are four types in total, two LV and one each
of MV and HV, forming a sequence of four tiers. The higher-tier ones
are each built mainly from three solar generators of the next tier down,
and their outputs scale in rough accordance, tripling at each tier.
To operate, an arrayed solar generator must be at elevation +1 or above
and have a transparent block (typically air) immediately above it.
It will generate power only when the block above is well lit during
daylight hours. It will generate more power at higher elevation,
reaching maximum output at elevation +36 or higher when sunlit. The small
solar generator has similar rules with slightly different thresholds.
These rules are an attempt to ensure that the generator will only operate
from sunlight, but it is actually possible to fool them to some extent
with light sources such as meselamps.
### hydro generator ###
The hydro generator is an LV power generator that generates a respectable
amount of power from the natural motion of water. To operate, the
generator must be horizontally adjacent to flowing water. The power
produced is dependent on how much flow there is across any or all four
sides, the most flow of course coming from water that's flowing straight
down.
### geothermal generator ###
The geothermal generator is an LV power generator that generates a small
amount of power from the temperature difference between lava and water.
To operate, the generator must be horizontally adjacent to both lava
and water. It doesn't matter whether the liquids consist of source
blocks or flowing blocks.
Beware that if lava and water blocks are adjacent to each other then the
lava will be solidified into stone or obsidian. If the lava adjacent to
the generator is thus destroyed, the generator will stop producing power.
Currently, in the default Minetest game, lava is destroyed even if
it is only diagonally adjacent to water. Under these circumstances,
the only way to operate the geothermal generator is with it adjacent
to one lava block and one water block, which are on opposite sides of
the generator. If diagonal adjacency doesn't destroy lava, such as with
the gloopblocks mod, then it is possible to have more than one lava or
water block adjacent to the geothermal generator. This increases the
generator's output, with the maximum output achieved with two adjacent
blocks of each liquid.
### wind generator ###
The wind generator is an MV power generator that generates a moderate
amount of energy from wind. To operate, the generator must be placed
atop a column of at least 20 wind mill frame blocks, and must be at
an elevation of +30 or higher. It generates more at higher elevation,
reaching maximum output at elevation +50 or higher. Its surroundings
don't otherwise matter; it doesn't actually need to be in open air.
### nuclear generator ###
The nuclear generator (nuclear reactor) is an HV power generator that
generates a large amount of energy from the controlled fission of
uranium-235. It must be fuelled, with uranium fuel rods, but consumes
the fuel quite slowly in relation to the rate at which it is likely to
be mined. The operation of a nuclear reactor poses radiological hazards
to which some thought must be given. Economically, the use of nuclear
power requires a high capital investment, and a secure infrastructure,
but rewards the investment well.
Nuclear fuel is made from uranium. Natural uranium doesn't have a
sufficiently high proportion of U-235, so it must first be enriched
via centrifuge. Producing one unit of 3.5%-fissile uranium requires
the input of five units of 0.7%-fissile (natural) uranium, and produces
four units of 0.0%-fissile (fully depleted) uranium as a byproduct.
It takes five ingots of 3.5%-fissile uranium to make each fuel rod, and
six rods to fuel a reactor. It thus takes the input of the equivalent
of 150 ingots of natural uranium, which can be obtained from the mining
of 75 blocks of uranium ore, to make a full set of reactor fuel.
The nuclear reactor is a large multi-block structure. Only one block in
the structure, the reactor core, is of a type that is truly specific to
the reactor; the rest of the structure consists of blocks that have mainly
non-nuclear uses. The reactor core is where all the generator-specific
action happens: it is where the fuel rods are inserted, and where the
power cable must connect to draw off the generated power.
The reactor structure consists of concentric layers, each a cubical
shell, around the core. Immediately around the core is a layer of water,
representing the reactor coolant; water blocks may be either source blocks
or flowing blocks. Around that is a layer of stainless steel blocks,
representing the reactor pressure vessel, and around that a layer of
blast-resistant concrete blocks, representing a containment structure.
It is customary, though no longer mandatory, to surround this with a
layer of ordinary concrete blocks. The mandatory reactor structure
makes a 7&times;7&times;7 cube, and the full customary structure a
9&times;9&times;9 cube.
The layers surrounding the core don't have to be absolutely complete.
Indeed, if they were complete, it would be impossible to cable the core to
a power network. The cable makes it necessary to have at least one block
missing from each surrounding layer. The water layer is only permitted
to have one water block missing of the 26 possible. The steel layer may
have up to two blocks missing of the 98 possible, and the blast-resistant
concrete layer may have up to two blocks missing of the 218 possible.
Thus it is possible to have not only a cable duct, but also a separate
inspection hole through the solid layers. The separate inspection hole
is of limited use: the cable duct can serve double duty.
Once running, the reactor core is significantly radioactive. The layers
of reactor structure provide quite a lot of shielding, but not enough
to make the reactor safe to be around, in two respects. Firstly, the
shortest possible path from the core to a player outside the reactor
is sufficiently short, and has sufficiently little shielding material,
that it will damage the player. This only affects a player who is
extremely close to the reactor, and close to a face rather than a vertex.
The customary additional layer of ordinary concrete around the reactor
adds sufficient distance and shielding to negate this risk, but it can
also be addressed by just keeping extra distance (a little over two
meters of air).
The second radiological hazard of a running reactor arises from shine
paths; that is, specific paths from the core that lack sufficient
shielding. The necessary cable duct, if straight, forms a perfect
shine path, because the cable itself has no radiation shielding effect.
Any secondary inspection hole also makes a shine path, along which the
only shielding material is the water of the reactor coolant. The shine
path aspect of the cable duct can be ameliorated by adding a kink in the
cable, but this still yields paths with reduced shielding. Ultimately,
shine paths must be managed either with specific shielding outside the
mandatory structure, or with additional no-go areas.
The radioactivity of an operating reactor core makes starting up a reactor
hazardous, and can come as a surprise because the non-operating core
isn't radioactive at all. The radioactive damage is survivable, but it is
normally preferable to avoid it by some care around the startup sequence.
To start up, the reactor must have a full set of fuel inserted, have all
the mandatory structure around it, and be cabled to a switching station.
Only the fuel insertion requires direct access to the core, so irradiation
of the player can be avoided by making one of the other two criteria be
the last one satisfied. Completing the cabling to a switching station
is the easiest to do from a safe distance.
Once running, the reactor will generate 100 kEU/s for a week (168 hours,
604800 seconds), a total of 6.048 GEU from one set of fuel. After the
week is up, it will stop generating and no longer be radioactive. It can
then be refuelled to run for another week. It is not really intended
to be possible to pause a running reactor, but actually disconnecting
it from a switching station will have the effect of pausing the week.
This will probably change in the future. A paused reactor is still
radioactive, just not generating electrical power.
A running reactor can't be safely dismantled, and not only because
dismantling the reactor implies removing the shielding that makes
it safe to be close to the core. The mandatory parts of the reactor
structure are not just mandatory in order to start the reactor; they're
mandatory in order to keep it intact. If the structure around the core
gets damaged, and remains damaged, the core will eventually melt down.
How long there is before meltdown depends on the extent of the damage;
if only one mandatory block is missing, meltdown will follow in 100
seconds. While the structure of a running reactor is in a damaged state,
heading towards meltdown, a siren built into the reactor core will sound.
If the structure is rectified, the siren will signal all-clear. If the
siren stops sounding without signalling all-clear, then it was stopped
by meltdown.
If meltdown is imminent because of damaged reactor structure, digging the
reactor core is not a way to avert it. Digging the core of a running
reactor causes instant meltdown. The only way to dismantle a reactor
without causing meltdown is to start by waiting for it to finish the
week-long burning of its current set of fuel. Once a reactor is no longer
operating, it can be dismantled by ordinary means, with no special risks.
Meltdown, if it occurs, destroys the reactor and poses a major
environmental hazard. The reactor core melts, becoming a hot, highly
radioactive liquid known as "corium". A single meltdown yields a single
corium source block, where the core used to be. Corium flows, and the
flowing corium is very destructive to whatever it comes into contact with.
Flowing corium also randomly solidifies into a radioactive solid called
"Chernobylite". The random solidification and random destruction of
solid blocks means that the flow of corium is constantly changing.
This combined with the severe radioactivity makes corium much more
challenging to deal with than lava. If a meltdown is left to its own
devices, it gets worse over time, as the corium works its way through
the reactor structure and starts to flow over a variety of paths.
It is best to tackle a meltdown quickly; the priority is to extinguish
the corium source block, normally by dropping gravel into it. Only the
most motivated should attempt to pick up the corium in a bucket.
Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 304 KiB

+331
View File
@@ -0,0 +1,331 @@
powered machines
----------------
## Overview
Many of the important machines in Technic run on electricity, using wires to connect generators with the consuming machines. These electric circuits consist of a generator, a consumer, wiring, and a switching station. Every independent circuit requires all 4 of these elements to function. In general the machines are all connected on the bottom by wire. Machines should be placed first and then the wire placed under and around them. The wiring should automatically adjust itself to connect to each machine and adjacent wires. If the wiring looks incorrect, it's likely that it won't work so be sure to check this!
Circuits are also grouped into 3 different categories based on how much power they transfer and the corresponding voltage: low voltage (LV), medium voltage (MV), and high voltage (HV). The base level for all electronics is low voltage, so if voltage isn't specified for a electrical component, you may safely assume it's low voltage. Most low-voltage components are upgradable to medium- and high-voltage through further crafting. To get started, you won't need to worry about any MV or HV components, and the basic low-voltage components are fine.
## Getting started
The first step to working with the more advanced machines are to get a basic electrical circuit set up for converting coal into power for other machines. This will rely on the following:
<img src="./images/LV_Fuel_Fired_Generator_Crafting.png"/>
1x LV Fuel Fired Generator
<img src="./images/Switching_station_crafting.png"/>
1x Switching station
<img src="./images/LV_cable_Crafting.png"/>
2x LV Cable
The generator and switching station should be placed side-by-side with the wire underneath connecting both of them. Once this is done, additional consumers can be added to the network. A grinder or extractor are both good choices to expand the capabilities of the coal-fired smelter and coal-fired alloy furnace that you already have.
### powered machine tiers ###
Each powered machine takes its power in some specific form, being
either fuel-fired (burning fuel directly) or electrically powered at
some specific voltage. There is a general progression through the
game from using fuel-fired machines to electrical machines, and to
higher electrical voltages. The most important kinds of machine come
in multiple variants that are powered in different ways, so the earlier
ones can be superseded. However, some machines are only available for
a specific power tier, so the tier can't be entirely superseded.
### powered machine upgrades ###
Some machines have inventory slots that are used to upgrade them in
some way. Generally, machines of MV and HV tiers have two upgrade slots,
and machines of lower tiers (fuel-fired and LV) do not. Any item can
be placed in an upgrade slot, but only specific items will have any
upgrading effect. It is possible to have multiple upgrades of the same
type, but this can't be achieved by stacking more than one upgrade item
in one slot: it is necessary to put the same kind of item in more than one
upgrade slot. The ability to upgrade machines is therefore very limited.
Two kinds of upgrade are currently possible: an energy upgrade and a
tube upgrade.
An energy upgrade consists of a battery item, the same kind of battery
that serves as a mobile energy store. The effect of an energy upgrade
is to improve in some way the machine's use of electrical energy, most
often by making it use less energy. The upgrade effect has no relation
to energy stored in the battery: the battery's charge level is irrelevant
and will not be affected.
A tube upgrade consists of a control logic unit item. The effect of a
tube upgrade is to make the machine able, or more able, to eject items
it has finished with into pneumatic tubes. The machines that can take
this kind of upgrade are in any case capable of accepting inputs from
pneumatic tubes. These upgrades are essential in using powered machines
as components in larger automated systems.
### tubes with powered machines ###
Generally, powered machines of MV and HV tiers can work with pneumatic
tubes, and those of lower tiers cannot. (As an exception, the fuel-fired
furnace from the basic Minetest game can accept inputs through tubes,
but can't output into tubes.)
If a machine can accept inputs through tubes at all, then this
is a capability of the basic machine, not requiring any upgrade.
Most item-processing machines take only one kind of input, and in that
case they will accept that input from any direction. This doesn't match
how tubes visually connect to the machines: generally tubes will visually
connect to any face except the front, but an item passing through a tube
in front of the machine will actually be accepted into the machine.
A minority of machines take more than one kind of input, and in that
case the input slot into which an arriving item goes is determined by the
direction from which it arrives. In this case the machine may be picky
about the direction of arriving items, associating each input type with
a single face of the machine and not accepting inputs at all through the
remaining faces. Again, the visual connection of tubes doesn't match:
generally tubes will still visually connect to any face except the front,
thus connecting to faces that neither accept inputs nor emit outputs.
Machines do not accept items from tubes into non-input inventory slots:
the output slots or upgrade slots. Output slots are normally filled
only by the processing operation of the machine, and upgrade slots must
be filled manually.
Powered machines generally do not eject outputs into tubes without
an upgrade. One tube upgrade will make them eject outputs at a slow
rate; a second tube upgrade will increase the rate. Whether the slower
rate is adequate depends on how it compares to the rate at which the
machine produces outputs, and on how the machine is being used as part
of a larger construct. The machine always ejects its outputs through a
particular face, usually a side. Due to a bug, the side through which
outputs are ejected is not consistent: when the machine is rotated one
way, the direction of ejection is rotated the other way. This will
probably be fixed some day, but because a straightforward fix would
break half the machines already in use, the fix may be tied to some
larger change such as free selection of the direction of ejection.
### battery boxes ###
The primary purpose of battery boxes is to temporarily store electrical
energy to let an electrical network cope with mismatched supply and
demand. They have a secondary purpose of charging and discharging
powered tools. They are thus a mixture of electrical infrastructure,
powered machine, and generator. Battery boxes connect to cables only
from the bottom.
MV and HV battery boxes have upgrade slots. Energy upgrades increase
the capacity of a battery box, each by 10% of the un-upgraded capacity.
This increase is far in excess of the capacity of the battery that forms
the upgrade.
For charging and discharging of power tools, rather than having input and
output slots, each battery box has a charging slot and a discharging slot.
A fully charged/discharged item stays in its slot. The rates at which a
battery box can charge and discharge increase with voltage, so it can
be worth building a battery box of higher tier before one has other
infrastructure of that tier, just to get access to faster charging.
MV and HV battery boxes work with pneumatic tubes. An item can be input
to the charging slot through the sides or back of the battery box, or
to the discharging slot through the top. With a tube upgrade, fully
charged/discharged tools (as appropriate for their slot) will be ejected
through a side.
### processing machines ###
The furnace, alloy furnace, grinder, extractor, compressor, and centrifuge
have much in common. Each implements some industrial process that
transforms items into other items, and the manner in which they present
these processes as powered machines is essentially identical.
Most of the processing machines operate on inputs of only a single type
at a time, and correspondingly have only a single input slot. The alloy
furnace is an exception: it operates on inputs of two distinct types at
once, and correspondingly has two input slots. It doesn't matter which
way round the alloy furnace's inputs are placed in the two slots.
The processing machines are mostly available in variants for multiple
tiers. The furnace and alloy furnace are each available in fuel-fired,
LV, and MV forms. The grinder, extractor, and compressor are each
available in LV and MV forms. The centrifuge is the only single-tier
processing machine, being only available in MV form. The higher-tier
machines process items faster than the lower-tier ones, but also have
higher power consumption, usually taking more energy overall to perform
the same amount of processing. The MV machines have upgrade slots,
and energy upgrades reduce their energy consumption.
The MV machines can work with pneumatic tubes. They accept inputs via
tubes from any direction. For most of the machines, having only a single
input slot, this is perfectly simple behavior. The alloy furnace is more
complex: it will put an arriving item in either input slot, preferring to
stack it with existing items of the same type. It doesn't matter which
slot each of the alloy furnace's inputs is in, so it doesn't matter that
there's no direct control over that, but there is a risk that supplying
a lot of one item type through tubes will result in both slots containing
the same type of item, leaving no room for the second input.
The MV machines can be given a tube upgrade to make them automatically
eject output items into pneumatic tubes. The items are always ejected
through a side, though which side it is depends on the machine's
orientation, due to a bug. Output items are always ejected singly.
For some machines, such as the grinder, the ejection rate with a
single tube upgrade doesn't keep up with the rate at which items can
be processed. A second tube upgrade increases the ejection rate.
The LV and fuel-fired machines do not work with pneumatic tubes, except
that the fuel-fired furnace (actually part of the basic Minetest game)
can accept inputs from tubes. Items arriving through the bottom of
the furnace go into the fuel slot, and items arriving from all other
directions go into the input slot.
### music player ###
The music player is an LV powered machine that plays audio recordings.
It offers a selection of up to nine tracks. The technic modpack doesn't
include specific music tracks for this purpose; they have to be installed
separately.
The music player gives the impression that the music is being played in
the Minetest world. The music only plays as long as the music player
is in place and is receiving electrical power, and the choice of music
is controlled by interaction with the machine. The sound also appears
to emanate specifically from the music player: the ability to hear it
depends on the player's distance from the music player. However, the
game engine doesn't currently support any other positional cues for
sound, such as attenuation, panning, or HRTF. The impression of the
sound being located in the Minetest world is also compromised by the
subjective nature of track choice: the specific music that is played to
a player depends on what media the player has installed.
### CNC machine ###
The CNC machine is an LV powered machine that cuts building blocks into a
variety of sub-block shapes that are not covered by the crafting recipes
of the stairs mod and its variants. Most of the target shapes are not
rectilinear, involving diagonal or curved surfaces.
Only certain kinds of building material can be processed in the CNC
machine.
### tool workshop ###
The tool workshop is an MV powered machine that repairs mechanically-worn
tools, such as pickaxes and the other ordinary digging tools. It has
a single slot for a tool to be repaired, and gradually repairs the
tool while it is powered. For any single tool, equal amounts of tool
wear, resulting from equal amounts of tool use, take equal amounts of
repair effort. Also, all repairable tools currently take equal effort
to repair equal percentages of wear. The amount of tool use enabled by
equal amounts of repair therefore depends on the tool type.
The mechanical wear that the tool workshop repairs is always indicated in
inventory displays by a colored bar overlaid on the tool image. The bar
can be seen to fill and change color as the tool workshop operates,
eventually disappearing when the repair is complete. However, not every
item that shows such a wear bar is using it to show mechanical wear.
A wear bar can also be used to indicate charging of a power tool with
stored electrical energy, or filling of a container, or potentially for
all sorts of other uses. The tool workshop won't affect items that use
wear bars to indicate anything other than mechanical wear.
The tool workshop has upgrade slots. Energy upgrades reduce its power
consumption.
It can work with pneumatic tubes. Tools to be repaired are accepted
via tubes from any direction. With a tube upgrade, the tool workshop
will also eject fully-repaired tools via one side, the choice of side
depending on the machine's orientation, as for processing machines. It is
safe to put into the tool workshop a tool that is already fully repaired:
assuming the presence of a tube upgrade, the tool will be quickly ejected.
Furthermore, any item of unrepairable type will also be ejected as if
fully repaired. (Due to a historical limitation of the basic Minetest
game, it is impossible for the tool workshop to distinguish between a
fully-repaired tool and any item type that never displays a wear bar.)
### quarry ###
The quarry is an HV powered machine that automatically digs out a
large area. The region that it digs out is a cuboid with a square
horizontal cross section, located immediately behind the quarry machine.
The quarry's action is slow and energy-intensive, but requires little
player effort.
The size of the quarry's horizontal cross section is configurable through
the machine's interaction form. A setting referred to as "radius"
is an integer number of meters which can vary from 2 to 8 inclusive.
The horizontal cross section is a square with side length of twice the
radius plus one meter, thus varying from 5 to 17 inclusive. Vertically,
the quarry always digs from 3 m above the machine to 100 m below it,
inclusive, a total vertical height of 104 m.
Whatever the quarry digs up is ejected through the top of the machine,
as if from a pneumatic tube. Normally a tube should be placed there
to convey the material into a sorting system, processing machines, or
at least chests. A chest may be placed directly above the machine to
capture the output without sorting, but is liable to overflow.
If the quarry encounters something that cannot be dug, such as a liquid,
a locked chest, or a protected area, it will skip past that and attempt
to continue digging.
The quarry consumes 10 kEU per block dug, which is quite a lot of energy.
With most of what is dug being mere stone, it is usually not economically
favorable to power a quarry from anything other than solar power.
In particular, one cannot expect to power a quarry by burning the coal
that it digs up.
Given sufficient power, the quarry digs at a rate of one block per second.
This is rather tedious to wait for. Unfortunately, leaving the quarry
unattended normally means that the Minetest server won't keep the machine
running: it needs a player nearby. This can be resolved by using a world
anchor. The digging is still quite slow, and independently of whether a
world anchor is used the digging can be speeded up by placing multiple
quarry machines with overlapping digging areas. Four can be placed to
dig identical areas, one on each side of the square cross section.
The quarry can be toggled on and off with a mesecons signal.
### forcefield emitter ###
The forcefield emitter is an HV powered machine that generates a
forcefield reminiscent of those seen in many science-fiction stories.
The emitter can be configured to generate a forcefield of either
spherical or cubical shape, in either case centered on the emitter.
The size of the forcefield is configured using a radius parameter that
is an integer number of meters which can vary from 5 to 20 inclusive.
For a spherical forcefield this is simply the radius of the forcefield;
for a cubical forcefield it is the distance from the emitter to the
center of each square face.
The power drawn by the emitter is proportional to the surface area of
the forcefield being generated. A spherical forcefield is therefore the
cheapest way to enclose a specified volume of space with a forcefield,
if the shape of the space doesn't matter. A cubical forcefield is less
efficient at enclosing volume, but is cheaper than the larger spherical
forcefield that would be required if it is necessary to enclose a
cubical space.
The emitter is normally controlled merely through its interaction form,
which has an enable/disable toggle. However, it can also (via the form)
be placed in a mesecon-controlled mode. If mesecon control is enabled,
the emitter must be receiving a mesecon signal in addition to being
manually enabled, in order for it to generate the forcefield.
The forcefield itself behaves largely as if solid, despite being
immaterial: it cannot be traversed, and prevents access to blocks behind
it. It is transparent, but not totally invisible. It cannot be dug.
Some effects can pass through it, however, such as the beam of a mining
laser, and explosions. In fact, explosions as currently implemented by
the tnt mod actually temporarily destroy the forcefield itself; the tnt
mod assumes too much about the regularity of node types.
The forcefield occupies space that would otherwise have been air, but does
not replace or otherwise interfere with materials that are solid, liquid,
or otherwise not just air. If such an object blocking the forcefield is
removed, the forcefield will quickly extend into the now-available space,
but it does not do so instantly: there is a brief moment when the space
is air and can be traversed.
It is possible to have a doorway in a forcefield, by placing in advance,
in space that the forcefield would otherwise occupy, some non-air blocks
that can be walked through. For example, a door suffices, and can be
opened and closed while the forcefield is in place.
+10
View File
@@ -0,0 +1,10 @@
# Technic mesecons compatibility
This document describes the interaction of
technic machines with the `mesecons` mod (https://github.com/minetest-mods/mesecons)
## HV Quarry
* **On-Action**: Enables the quarry
* **Off-Action**: Disables the quarry
+67
View File
@@ -0,0 +1,67 @@
electrical power
----------------
Most machines in technic are electrically powered. To operate them it is
necessary to construct an electrical power network. The network links
together power generators and power-consuming machines, connecting them
using power cables.
There are three tiers of electrical networking: low voltage (LV),
medium voltage (MV), and high voltage (HV). Each network must operate
at a single voltage, and most electrical items are specific to a single
voltage. Generally, the machines of higher tiers are more powerful,
but consume more energy and are more expensive to build, than machines
of lower tiers. It is normal to build networks of all three tiers,
in ascending order as one progresses through the game, but it is not
strictly necessary to do this. Building HV equipment requires some parts
that can only be manufactured using electrical machines, either LV or MV,
so it is not possible to build an HV network first, but it is possible
to skip either LV or MV on the way to HV.
Each voltage has its own cable type, with distinctive insulation. Cable
segments connect to each other and to compatible machines automatically.
Incompatible electrical items don't connect. All non-cable electrical
items must be connected via cable: they don't connect directly to each
other. Most electrical items can connect to cables in any direction,
but there are a couple of important exceptions noted below.
To be useful, an electrical network must connect at least one power
generator to at least one power-consuming machine. In addition to these
items, the network must have a "switching station" in order to operate:
no energy will flow without one. Unlike most electrical items, the
switching station is not voltage-specific: the same item will manage
a network of any tier. However, also unlike most electrical items,
it is picky about the direction in which it is connected to the cable:
the cable must be directly below the switching station.
Hovering over a network's switching station will show the aggregate energy
supply and demand, which is useful for troubleshooting. Electrical energy
is measured in "EU", and power (energy flow) in EU per second (EU/s).
Energy is shifted around a network instantaneously once per second.
In a simple network with only generators and consumers, if total
demand exceeds total supply then no energy will flow, the machines
will do nothing, and the generators' output will be lost. To handle
this situation, it is recommended to add a battery box to the network.
A battery box will store generated energy, and when enough has been
stored to run the consumers for one second it will deliver it to the
consumers, letting them run part-time. It also stores spare energy
when supply exceeds demand, to let consumers run full-time when their
demand occasionally peaks above the supply. More battery boxes can
be added to cope with larger periods of mismatched supply and demand,
such as those resulting from using solar generators (which only produce
energy in the daytime).
When there are electrical networks of multiple tiers, it can be appealing
to generate energy on one tier and transfer it to another. The most
direct way to do this is with the "supply converter", which can be
directly wired into two networks. It is another tier-independent item,
and also particular about the direction of cable connections: it must
have the cable of one network directly above, and the cable of another
network directly below. The supply converter demands 10000 EU/s from
the network above, and when this network gives it power it supplies 9000
EU/s to the network below. Thus it is only 90% efficient, unlike most of
the electrical system which is 100% efficient in moving energy around.
To transfer more than 10000 EU/s between networks, connect multiple
supply converters in parallel.
+105
View File
@@ -0,0 +1,105 @@
industrial processes
--------------------
### alloying ###
In technic, alloying is a way of combining items to create other items,
distinct from standard crafting. Alloying always uses inputs of exactly
two distinct types, and produces a single output. Like cooking, which
takes a single input, it is performed using a powered machine, known
generically as an "alloy furnace". An alloy furnace always has two
input slots, and it doesn't matter which way round the two ingredients
are placed in the slots. Many alloying recipes require one or both
slots to contain a stack of more than one of the ingredient item: the
quantity required of each ingredient is part of the recipe.
As with the furnaces used for cooking, there are multiple kinds of alloy
furnace, powered in different ways. The most-used alloy furnaces are
electrically powered. There is also an alloy furnace that is powered
by directly burning fuel, just like the basic cooking furnace. Building
almost any electrical machine, including the electrically-powered alloy
furnaces, requires a machine casing component, one ingredient of which
is brass, an alloy. It is therefore necessary to use the fuel-fired
alloy furnace in the early part of the game, on the way to building
electrical machinery.
Alloying recipes are mainly concerned with metals. These recipes
combine a base metal with some other element, most often another metal,
to produce a new metal. This is discussed in the section on metal.
There are also a few alloying recipes in which the base ingredient is
non-metallic, such as the recipe for the silicon wafer.
### grinding, extracting, and compressing ###
Grinding, extracting, and compressing are three distinct, but very
similar, ways of converting one item into another. They are all quite
similar to the cooking found in the basic Minetest game. Each uses
an input consisting of a single item type, and produces a single
output. They are all performed using powered machines, respectively
known generically as a "grinder", "extractor", and "compressor".
Some compressing recipes require the input to be a stack of more than
one of the input item: the quantity required is part of the recipe.
Grinding and extracting recipes never require such a stacked input.
There are multiple kinds of grinder, extractor, and compressor. Unlike
cooking furnaces and alloy furnaces, there are none that directly burn
fuel; they are all electrically powered.
Grinding recipes always produce some kind of dust, loosely speaking,
as output. The most important grinding recipes are concerned with metals:
every metal lump or ingot can be ground into metal dust. Coal can also
be ground into dust, and burning the dust as fuel produces much more
energy than burning the original coal lump. There are a few other
grinding recipes that make block types from the basic Minetest game
more interconvertible: standard stone can be ground to standard sand,
desert stone to desert sand, cobblestone to gravel, and gravel to dirt.
Extracting is a miscellaneous category, used for a small group
of processes that just don't fit nicely anywhere else. (Its name is
notably vaguer than those of the other kinds of processing.) It is used
for recipes that produce dye, mainly from flowers. (However, for those
recipes using flowers, the basic Minetest game provides parallel crafting
recipes that are easier to use and produce more dye, and those recipes
are not suppressed by technic.) Its main use is to generate rubber from
raw latex, which it does three times as efficiently as merely cooking
the latex. Extracting was also formerly used for uranium enrichment for
use as nuclear fuel, but this use has been superseded by a new enrichment
system using the centrifuge.
Compressing recipes are mainly used to produce a few relatively advanced
artificial item types, such as the copper and carbon plates used in
advanced machine recipes. There are also a couple of compressing recipes
making natural block types more interconvertible.
### centrifuging ###
Centrifuging is another way of using a machine to convert items.
Centrifuging takes an input of a single item type, and produces outputs
of two distinct types. The input may be required to be a stack of
more than one of the input item: the quantity required is part of
the recipe. Centrifuging is only performed by a single machine type,
the MV (electrically-powered) centrifuge.
Currently, centrifuging recipes don't appear in the unified\_inventory
craft guide, because unified\_inventory can't yet handle recipes with
multiple outputs.
Generally, centrifuging separates the input item into constituent
substances, but it can only work when the input is reasonably fluid,
and in marginal cases it is quite destructive to item structure.
(In real life, centrifuges require their input to be mainly fluid, that
is either liquid or gas. Few items in the game are described as liquid
or gas, so the concept of the centrifuge is stretched a bit to apply to
finely-divided solids.)
The main use of centrifuging is in uranium enrichment, where it
separates the isotopes of uranium dust that otherwise appears uniform.
Enrichment is a necessary process before uranium can be used as nuclear
fuel, and the radioactivity of uranium blocks is also affected by its
isotopic composition.
A secondary use of centrifuging is to separate the components of
metal alloys. This can only be done using the dust form of the alloy.
It recovers both components of binary metal/metal alloys. It can't
recover the carbon from steel or cast iron.
@@ -0,0 +1,136 @@
radioactivity
-------------
The technic mod adds radioactivity to the game, as a hazard that can
harm player characters. Certain substances in the game are radioactive,
and when placed as blocks in the game world will damage nearby players.
Conversely, some substances attenuate radiation, and so can be used
for shielding. The radioactivity system is based on reality, but is
not an attempt at serious simulation: like the rest of the game, it has
many simplifications and deliberate deviations from reality in the name
of game balance.
In real life radiological hazards can be roughly divided into three
categories based on the time scale over which they act: prompt radiation
damage (such as radiation burns) that takes effect immediately; radiation
poisoning that becomes visible in hours and lasts weeks; and cumulative
effects such as increased cancer risk that operate over decades.
The game's version of radioactivity causes only prompt damage, not
any delayed effects. Damage comes in the abstracted form of removing
the player's hit points, and is immediately visible to the player.
As with all other kinds of damage in the game, the player can restore
the hit points by eating food items. High-nutrition foods, such as the
pie baskets supplied by the bushes\_classic mod, are a useful tool in
dealing with radiological hazards.
Only a small range of items in the game are radioactive. From the technic
mod, the only radioactive items are uranium ore, refined uranium blocks,
nuclear reactor cores (when operating), and the materials released when
a nuclear reactor melts down. Other mods can plug into the technic
system to make their own block types radioactive. Radioactive items
are harmless when held in inventories. They only cause radiation damage
when placed as blocks in the game world.
The rate at which damage is caused by a radioactive block depends on the
distance between the source and the player. Distance matters because the
damaging radiation is emitted equally in all directions by the source,
so with distance it spreads out, so less of it will strike a target
of any specific size. The amount of radiation absorbed by a target
thus varies in proportion to the inverse square of the distance from
the source. The game imitates this aspect of real-life radioactivity,
but with some simplifications. While in real life the inverse square law
is only really valid for sources and targets that are small relative to
the distance between them, in the game it is applied even when the source
and target are large and close together. Specifically, the distance is
measured from the center of the radioactive block to the abdomen of the
player character. For extremely close encounters, such as where the
player swims in a radioactive liquid, there is an enforced lower limit
on the effective distance.
Different types of radioactive block emit different amounts of radiation.
The least radioactive of the radioactive block types is uranium ore,
which causes 0.25 HP/s damage to a player 1 m away. A block of refined
but unenriched uranium, as an example, is nine times as radioactive,
and so will cause 2.25 HP/s damage to a player 1 m away. By the inverse
square law, the damage caused by that uranium block reduces by a factor
of four at twice the distance, that is to 0.5625 HP/s at a distance of 2
m, or by a factor of nine at three times the distance, that is to 0.25
HP/s at a distance of 3 m. Other radioactive block types are far more
radioactive than these: the most radioactive of all, the result of a
nuclear reactor melting down, is 1024 times as radioactive as uranium ore.
Uranium blocks are radioactive to varying degrees depending on their
isotopic composition. An isotope being fissile, and thus good as
reactor fuel, is essentially uncorrelated with it being radioactive.
The fissile U-235 is about six times as radioactive than the non-fissile
U-238 that makes up the bulk of natural uranium, so one might expect that
enriching from 0.7% fissile to 3.5% fissile (or depleting to 0.0%) would
only change the radioactivity of uranium by a few percent. But actually
the radioactivity of enriched uranium is dominated by the non-fissile
U-234, which makes up only about 50 parts per million of natural uranium
but is about 19000 times more radioactive than U-238. The radioactivity
of natural uranium comes just about half from U-238 and half from U-234,
and the uranium gets enriched in U-234 along with the U-235. This makes
3.5%-fissile uranium about three times as radioactive as natural uranium,
and 0.0%-fissile uranium about half as radioactive as natural uranium.
Radiation is attenuated by the shielding effect of material along the
path between the radioactive block and the player. In general, only
blocks of homogeneous material contribute to the shielding effect: for
example, a block of solid metal has a shielding effect, but a machine
does not, even though the machine's ingredients include a metal case.
The shielding effect of each block type is based on the real-life
resistance of the material to ionising radiation, but for game balance
the effectiveness of shielding is scaled down from real life, more so
for stronger shield materials than for weaker ones. Also, whereas in
real life materials have different shielding effects against different
types of radiation, the game only has one type of damaging radiation,
and so only one set of shielding values.
Almost any solid or liquid homogeneous material has some shielding value.
At the low end of the scale, 5 meters of wooden planks nearly halves
radiation, though in that case the planks probably contribute more
to safety by forcing the player to stay 5 m further away from the
source than by actual attenuation. Dirt halves radiation in 2.4 m,
and stone in 1.7 m. When a shield must be deliberately constructed,
the preferred materials are metals, the denser the better. Iron and
steel halve radiation in 1.1 m, copper in 1.0 m, and silver in 0.95 m.
Lead would halve in 0.69 m (its in-game shielding value is 80). Gold halves radiation
in 0.53 m (factor of 3.7 per meter), but is a bit scarce to use for
this purpose. Uranium halves radiation in 0.31 m (factor of 9.4 per
meter), but is itself radioactive. The very best shielding in the game
is nyancat material (nyancats and their rainbow blocks), which halves
radiation in 0.22 m (factor of 24 per meter), but is extremely scarce. See [technic/technic/radiation.lua](https://github.com/minetest-technic/technic/blob/master/technic/radiation.lua) for the in-game shielding values, which are different from real-life values.
If the theoretical radiation damage from a particular source is
sufficiently small, due to distance and shielding, then no damage at all
will actually occur. This means that for any particular radiation source
and shielding arrangement there is a safe distance to which a player can
approach without harm. The safe distance is where the radiation damage
would theoretically be 0.25 HP/s. This damage threshold is applied
separately for each radiation source, so to be safe in a multi-source
situation it is only necessary to be safe from each source individually.
The best way to use uranium as shielding is in a two-layer structure,
of uranium and some non-radioactive material. The uranium layer should
be nearer to the primary radiation source and the non-radioactive layer
nearer to the player. The uranium provides a great deal of shielding
against the primary source, and the other material shields against
the uranium layer. Due to the damage threshold mechanism, a meter of
dirt is sufficient to shield fully against a layer of fully-depleted
(0.0%-fissile) uranium. Obviously this is only worthwhile when the
primary radiation source is more radioactive than a uranium block.
When constructing permanent radiation shielding, it is necessary to
pay attention to the geometry of the structure, and particularly to any
holes that have to be made in the shielding, for example to accommodate
power cables. Any hole that is aligned with the radiation source makes a
"shine path" through which a player may be irradiated when also aligned.
Shine paths can be avoided by using bent paths for cables, passing
through unaligned holes in multiple shield layers. If the desired
shielding effect depends on multiple layers, a hole in one layer still
produces a partial shine path, along which the shielding is reduced,
so the positioning of holes in each layer must still be considered.
Tricky shine paths can also be addressed by just keeping players out of
the dangerous area.
@@ -0,0 +1,7 @@
Technic provides additional resources for building these machines.
## Latex
<img src="../textures/technic_raw_latex.png"/>
Latex is used for manufacturing rubber, which is heavily used for the medium- and high-voltage components within Technic. Latex can be harvested directly from rubber trees (look for the pale green/seafoam leaves) using the tree tap like any other tool. Rubber trees regenerate their latex supplies after about a day.
+490
View File
@@ -0,0 +1,490 @@
substances
----------
### ore ###
The technic mod makes extensive use of not just the default ores but also
some that are added by mods. You will need to mine for all the ore types
in the course of the game. Each ore type is found at a specific range of
elevations, and while the ranges mostly overlap, some have non-overlapping
ranges, so you will ultimately need to mine at more than one elevation
to find all the ores. Also, because one of the best elevations to mine
at is very deep, you will be unable to mine there early in the game.
Elevation is measured in meters, relative to a reference plane that
is not quite sea level. (The standard sea level is at an elevation
of about +1.4.) Positive elevations are above the reference plane and
negative elevations below. Because elevations are always described this
way round, greater numbers when higher, we avoid the word "depth".
The ores that matter in technic are coal, iron, copper, tin, zinc,
chromium, uranium, silver, gold, mithril, mese, and diamond.
Coal is part of the basic Minetest game. It is found from elevation
+64 downwards, so is available right on the surface at the start of
the game, but it is far less abundant above elevation 0 than below.
It is initially used as a fuel, driving important machines in the early
part of the game. It becomes less important as a fuel once most of your
machines are electrically powered, but burning fuel remains a way to
generate electrical power. Coal is also used, usually in dust form, as
an ingredient in alloying recipes, wherever elemental carbon is required.
Iron is part of the basic Minetest game. It is found from elevation
+2 downwards, and its abundance increases in stages as one descends,
reaching its maximum from elevation -64 downwards. It is a common metal,
used frequently as a structural component. In technic, unlike the basic
game, iron is used in multiple forms, mainly alloys based on iron and
including carbon (coal).
Copper is part of the basic Minetest game (having migrated there from
moreores). It is found from elevation -16 downwards, but is more abundant
from elevation -64 downwards. It is a common metal, used either on its
own for its electrical conductivity, or as the base component of alloys.
Although common, it is very heavily used, and most of the time it will
be the material that most limits your activity.
Tin is part of the basic Minetest game (having migrated there from
moreores). It is found from elevation +8 downwards, with no
elevation-dependent variations in abundance beyond that point.
It is a common metal. Its main use in pure form is as a component
of electrical batteries. Apart from that its main purpose is
as the secondary ingredient in bronze (the base being copper), but bronze
is itself little used. Its abundance is well in excess of its usage,
so you will usually have a surplus of it.
Zinc is supplied by technic. It is found from elevation +2 downwards,
with no elevation-dependent variations in abundance beyond that point.
It is a common metal. Its main use is as the secondary ingredient
in brass (the base being copper), but brass is itself little used.
Its abundance is well in excess of its usage, so you will usually have
a surplus of it.
Chromium is supplied by technic. It is found from elevation -100
downwards, with no elevation-dependent variations in abundance beyond
that point. It is a moderately common metal. Its main use is as the
secondary ingredient in stainless steel (the base being iron).
Uranium is supplied by technic. It is found only from elevation -80 down
to -300; using it therefore requires one to mine above elevation -300 even
though deeper mining is otherwise more productive. It is a moderately
common metal, useful only for reasons related to radioactivity: it forms
the fuel for nuclear reactors, and is also one of the best radiation
shielding materials available. It is not difficult to find enough uranium
ore to satisfy these uses. Beware that the ore is slightly radioactive:
it will slightly harm you if you stand as close as possible to it.
It is safe when more than a meter away or when mined.
Silver is supplied by the moreores mod. It is found from elevation -2
downwards, with no elevation-dependent variations in abundance beyond
that point. It is a semi-precious metal. It is little used, being most
notably used in electrical items due to its conductivity, being the best
conductor of all the pure elements.
Gold is part of the basic Minetest game (having migrated there from
moreores). It is found from elevation -64 downwards, but is more
abundant from elevation -256 downwards. It is a precious metal. It is
little used, being most notably used in electrical items due to its
combination of good conductivity (third best of all the pure elements)
and corrosion resistance.
Mithril is supplied by the moreores mod. It is found from elevation
-512 downwards, the deepest ceiling of any minable substance, with
no elevation-dependent variations in abundance beyond that point.
It is a rare precious metal, and unlike all the other metals described
here it is entirely fictional, being derived from J. R. R. Tolkien's
Middle-Earth setting. It is little used.
Mese is part of the basic Minetest game. It is found from elevation
-64 downwards. The ore is more abundant from elevation -256 downwards,
and from elevation -1024 downwards there are also occasional blocks of
solid mese (each yielding as much mese as nine blocks of ore). It is a
precious gemstone, and unlike diamond it is entirely fictional. It is
used in many recipes, though mainly not in large quantities, wherever
some magical quality needs to be imparted.
Diamond is part of the basic Minetest game (having migrated there from
technic). It is found from elevation -128 downwards, but is more abundant
from elevation -256 downwards. It is a precious gemstone. It is used
moderately, mainly for reasons connected to its extreme hardness.
### rock ###
In addition to the ores, there are multiple kinds of rock that need to be
mined in their own right, rather than for minerals. The rock types that
matter in technic are standard stone, desert stone, marble, and granite.
Standard stone is part of the basic Minetest game. It is extremely
common. As in the basic game, when dug it yields cobblestone, which can
be cooked to turn it back into standard stone. Cobblestone is used in
recipes only for some relatively primitive machines. Standard stone is
used in a couple of machine recipes. These rock types gain additional
significance with technic because the grinder can be used to turn them
into dirt and sand. This, especially when combined with an automated
cobblestone generator, can be an easier way to acquire sand than
collecting it where it occurs naturally.
Desert stone is part of the basic Minetest game. It is found specifically
in desert biomes, and only from elevation +2 upwards. Although it is
easily accessible, therefore, its quantity is ultimately quite limited.
It is used in a few recipes.
Marble is supplied by technic. It is found in dense clusters from
elevation -50 downwards. It has mainly decorative use, but also appears
in one machine recipe.
Granite is supplied by technic. It is found in dense clusters from
elevation -150 downwards. It is much harder to dig than standard stone,
so impedes mining when it is encountered. It has mainly decorative use,
but also appears in a couple of machine recipes.
### rubber ###
Rubber is a biologically-derived material that has industrial uses due
to its electrical resistivity and its impermeability. In technic, it
is used in a few recipes, and it must be acquired by tapping rubber trees.
If you have the moretrees mod installed, the rubber trees you need
are those defined by that mod. If not, technic supplies a copy of the
moretrees rubber tree.
Extracting rubber requires a specific tool, a tree tap. Using the tree
tap (by left-clicking) on a rubber tree trunk block extracts a lump of
raw latex from the trunk. Each trunk block can be repeatedly tapped for
latex, at intervals of several minutes; its appearance changes to show
whether it is currently ripe for tapping. Each tree has several trunk
blocks, so several latex lumps can be extracted from a tree in one visit.
Raw latex isn't used directly. It must be vulcanized to produce finished
rubber. This can be performed by alloying the latex with coal dust.
### metal ###
Many of the substances important in technic are metals, and there is
a common pattern in how metals are handled. Generally, each metal can
exist in five forms: ore, lump, dust, ingot, and block. With a couple of
tricky exceptions in mods outside technic, metals are only *used* in dust,
ingot, and block forms. Metals can be readily converted between these
three forms, but can't be converted from them back to ore or lump forms.
As in the basic Minetest game, a "lump" of metal is acquired directly by
digging ore, and will then be processed into some other form for use.
A lump is thus more akin to ore than to refined metal. (In real life,
metal ore rarely yields lumps ("nuggets") of pure metal directly.
More often the desired metal is chemically bound into the rock as an
oxide or some other compound, and the ore must be chemically processed
to yield pure metal.)
Not all metals occur directly as ore. Generally, elemental metals (those
consisting of a single chemical element) occur as ore, and alloys (those
consisting of a mixture of multiple elements) do not. In fact, if the
fictional mithril is taken to be elemental, this pattern is currently
followed perfectly. (It is not clear in the Middle-Earth setting whether
mithril is elemental or an alloy.) This might change in the future:
in real life some alloys do occur as ore, and some elemental metals
rarely occur naturally outside such alloys. Metals that do not occur
as ore also lack the "lump" form.
The basic Minetest game offers a single way to refine metals: cook a lump
in a furnace to produce an ingot. With technic this refinement method
still exists, but is rarely used outside the early part of the game,
because technic offers a more efficient method once some machines have
been built. The grinder, available only in electrically-powered forms,
can grind a metal lump into two piles of metal dust. Each dust pile
can then be cooked into an ingot, yielding two ingots from one lump.
This doubling of material value means that you should only cook a lump
directly when you have no choice, mainly early in the game when you
haven't yet built a grinder.
An ingot can also be ground back to (one pile of) dust. Thus it is always
possible to convert metal between ingot and dust forms, at the expense
of some energy consumption. Nine ingots of a metal can be crafted into
a block, which can be used for building. The block can also be crafted
back to nine ingots. Thus it is possible to freely convert metal between
ingot and block forms, which is convenient to store the metal compactly.
Every metal has dust, ingot, and block forms.
Alloying recipes in which a metal is the base ingredient, to produce a
metal alloy, always come in two forms, using the metal either as dust
or as an ingot. If the secondary ingredient is also a metal, it must
be supplied in the same form as the base ingredient. The output alloy
is also returned in the same form. For example, brass can be produced
by alloying two copper ingots with one zinc ingot to make three brass
ingots, or by alloying two piles of copper dust with one pile of zinc
dust to make three piles of brass dust. The two ways of alloying produce
equivalent results.
### iron and its alloys ###
Iron forms several important alloys. In real-life history, iron was the
second metal to be used as the base component of deliberately-constructed
alloys (the first was copper), and it was the first metal whose working
required processes of any metallurgical sophistication. The game
mechanics around iron broadly imitate the historical progression of
processes around it, rather than the less-varied modern processes.
The two-component alloying system of iron with carbon is of huge
importance, both in the game and in real life. The basic Minetest game
doesn't distinguish between these pure iron and these alloys at all,
but technic introduces a distinction based on the carbon content, and
renames some items of the basic game accordingly.
The iron/carbon spectrum is represented in the game by three metal
substances: wrought iron, carbon steel, and cast iron. Wrought iron
has low carbon content (less than 0.25%), resists shattering, and
is easily welded, but is relatively soft and susceptible to rusting.
In real-life history it was used for rails, gates, chains, wire, pipes,
fasteners, and other purposes. Cast iron has high carbon content
(2.1% to 4%), is especially hard, and resists corrosion, but is
relatively brittle, and difficult to work. Historically it was used
to build large structures such as bridges, and for cannons, cookware,
and engine cylinders. Carbon steel has medium carbon content (0.25%
to 2.1%), and intermediate properties: moderately hard and also tough,
somewhat resistant to corrosion. In real life it is now used for most
of the purposes previously satisfied by wrought iron and many of those
of cast iron, but has historically been especially important for its
use in swords, armor, skyscrapers, large bridges, and machines.
In real-life history, the first form of iron to be refined was
wrought iron, which is nearly pure iron, having low carbon content.
It was produced from ore by a low-temperature furnace process (the
"bloomery") in which the ore/iron remains solid and impurities (slag)
are progressively removed by hammering ("working", hence "wrought").
This began in the middle East, around 1800 BCE.
Historically, the next forms of iron to be refined were those of high
carbon content. This was the result of the development of a more
sophisticated kind of furnace, the blast furnace, capable of reaching
higher temperatures. The real advantage of the blast furnace is that it
melts the metal, allowing it to be cast straight into a shape supplied by
a mould, rather than having to be gradually beaten into the desired shape.
A side effect of the blast furnace is that carbon from the furnace's fuel
is unavoidably incorporated into the metal. Normally iron is processed
twice through the blast furnace: once producing "pig iron", which has
very high carbon content and lots of impurities but lower melting point,
casting it into rough ingots, then remelting the pig iron and casting it
into the final moulds. The result is called "cast iron". Pig iron was
first produced in China around 1200 BCE, and cast iron later in the 5th
century BCE. Incidentally, the Chinese did not have the bloomery process,
so this was their first iron refining process, and, unlike the rest of
the world, their first wrought iron was made from pig iron rather than
directly from ore.
Carbon steel, with intermediate carbon content, was developed much later,
in Europe in the 17th century CE. It required a more sophisticated
process, because the blast furnace made it extremely difficult to achieve
a controlled carbon content. Tweaks of the blast furnace would sometimes
produce an intermediate carbon content by luck, but the first processes to
reliably produce steel were based on removing almost all the carbon from
pig iron and then explicitly mixing a controlled amount of carbon back in.
In the game, the bloomery process is represented by ordinary cooking
or grinding of an iron lump. The lump represents unprocessed ore,
and is identified only as "iron", not specifically as wrought iron.
This standard refining process produces dust or an ingot which is
specifically identified as wrought iron. Thus the standard refining
process produces the (nearly) pure metal.
Cast iron is trickier. You might expect from the real-life notes above
that cooking an iron lump (representing ore) would produce pig iron that
can then be cooked again to produce cast iron. This is kind of the case,
but not exactly, because as already noted cooking an iron lump produces
wrought iron. The game doesn't distinguish between low-temperature
and high-temperature cooking processes: the same furnace is used not
just to cast all kinds of metal but also to cook food. So there is no
distinction between cooking processes to produce distinct wrought iron
and pig iron. But repeated cooking *is* available as a game mechanic,
and is indeed used to produce cast iron: re-cooking a wrought iron ingot
produces a cast iron ingot. So pig iron isn't represented in the game as
a distinct item; instead wrought iron stands in for pig iron in addition
to its realistic uses as wrought iron.
Carbon steel is produced by a more regular in-game process: alloying
wrought iron with coal dust (which is essentially carbon). This bears
a fair resemblance to the historical development of carbon steel.
This alloying recipe is relatively time-consuming for the amount of
material processed, when compared against other alloying recipes, and
carbon steel is heavily used, so it is wise to alloy it in advance,
when you're not waiting for it.
There are additional recipes that permit all three of these types of iron
to be converted into each other. Alloying carbon steel again with coal
dust produces cast iron, with its higher carbon content. Cooking carbon
steel or cast iron produces wrought iron, in an abbreviated form of the
bloomery process.
There's one more iron alloy in the game: stainless steel. It is managed
in a completely regular manner, created by alloying carbon steel with
chromium.
### uranium enrichment ###
When uranium is to be used to fuel a nuclear reactor, it is not
sufficient to merely isolate and refine uranium metal. It is necessary
to control its isotopic composition, because the different isotopes
behave differently in nuclear processes.
The main isotopes of interest are U-235 and U-238. U-235 is good at
sustaining a nuclear chain reaction, because when a U-235 nucleus is
bombarded with a neutron it will usually fission (split) into fragments.
It is therefore described as "fissile". U-238, on the other hand,
is not fissile: if bombarded with a neutron it will usually capture it,
becoming U-239, which is very unstable and quickly decays into semi-stable
(and fissile) plutonium-239.
Inconveniently, the fissile U-235 makes up only about 0.7% of natural
uranium, almost all of the other 99.3% being U-238. Natural uranium
therefore doesn't make a great nuclear fuel. (In real life there are
a small number of reactor types that can use it, but technic doesn't
have such a reactor.) Better nuclear fuel needs to contain a higher
proportion of U-235.
Achieving a higher U-235 content isn't as simple as separating the U-235
from the U-238 and just using the required amount of U-235. Because
U-235 and U-238 are both uranium, and therefore chemically identical,
they cannot be chemically separated, in the way that different elements
are separated from each other when refining metal. They do differ
in atomic mass, so they can be separated by centrifuging, but because
their atomic masses are very close, centrifuging doesn't separate them
very well. They cannot be separated completely, but it is possible to
produce uranium that has the isotopes mixed in different proportions.
Uranium with a significantly larger fissile U-235 fraction than natural
uranium is called "enriched", and that with a significantly lower fissile
fraction is called "depleted".
A single pass through a centrifuge produces two output streams, one with
a fractionally higher fissile proportion than the input, and one with a
fractionally lower fissile proportion. To alter the fissile proportion
by a significant amount, these output streams must be centrifuged again,
repeatedly. The usual arrangement is a "cascade", a linear arrangement
of many centrifuges. Each centrifuge takes as input uranium with some
specific fissile proportion, and passes its two output streams to the
two adjacent centrifuges. Natural uranium is input somewhere in the
middle of the cascade, and the two ends of the cascade produce properly
enriched and depleted uranium.
Fuel for technic's nuclear reactor consists of enriched uranium of which
3.5% is fissile. (This is a typical value for a real-life light water
reactor, a common type for power generation.) To enrich uranium in the
game, it must first be in dust form: the centrifuge will not operate
on ingots. (In real life uranium enrichment is done with the uranium
in the form of a gas.) It is best to grind uranium lumps directly to
dust, rather than cook them to ingots first, because this yields twice
as much metal dust. When uranium is in refined form (dust, ingot, or
block), the name of the inventory item indicates its fissile proportion.
Uranium of any available fissile proportion can be put through all the
usual processes for metal.
A single centrifuge operation takes two uranium dust piles, and produces
as output one dust pile with a fissile proportion 0.1% higher and one with
a fissile proportion 0.1% lower. Uranium can be enriched up to the 3.5%
required for nuclear fuel, and depleted down to 0.0%. Thus a cascade
covering the full range of fissile fractions requires 34 cascade stages.
(In real life, enriching to 3.5% uses thousands of cascade stages.
Also, centrifuging is less effective when the input isotope ratio
is more skewed, so the steps in fissile proportion are smaller for
relatively depleted uranium. Zero fissile content is only asymptotically
approachable, and natural uranium relatively cheap, so uranium is normally
only depleted to around 0.3%. On the other hand, much higher enrichment
than 3.5% isn't much more difficult than enriching that far.)
Although centrifuges can be used manually, it is not feasible to perform
uranium enrichment by hand. It is a practical necessity to set up
an automated cascade, using pneumatic tubes to transfer uranium dust
piles between centrifuges. Because both outputs from a centrifuge are
ejected into the same tube, sorting tubes are needed to send the outputs
in different directions along the cascade. It is possible to send items
into the centrifuges through the same tubes that take the outputs, so the
simplest version of the cascade structure has a line of 34 centrifuges
linked by a line of 34 sorting tube segments.
Assuming that the cascade depletes uranium all the way to 0.0%,
producing one unit of 3.5%-fissile uranium requires the input of five
units of 0.7%-fissile (natural) uranium, takes 490 centrifuge operations,
and produces four units of 0.0%-fissile (fully depleted) uranium as a
byproduct. It is possible to reduce the number of required centrifuge
operations by using more natural uranium input and outputting only
partially depleted uranium, but (unlike in real life) this isn't usually
an economical approach. The 490 operations are not spread equally over
the cascade stages: the busiest stage is the one taking 0.7%-fissile
uranium, which performs 28 of the 490 operations. The least busy is the
one taking 3.4%-fissile uranium, which performs 1 of the 490 operations.
A centrifuge cascade will consume quite a lot of energy. It is
worth putting a battery upgrade in each centrifuge. (Only one can be
accommodated, because a control logic unit upgrade is also required for
tube operation.) An MV centrifuge, the only type presently available,
draws 7 kEU/s in this state, and takes 5 s for each uranium centrifuging
operation. It thus takes 35 kEU per operation, and the cascade requires
17.15 MEU to produce each unit of enriched uranium. It takes five units
of enriched uranium to make each fuel rod, and six rods to fuel a reactor,
so the enrichment cascade requires 514.5 MEU to process a full set of
reactor fuel. This is about 0.85% of the 6.048 GEU that the reactor
will generate from that fuel.
If there is enough power available, and enough natural uranium input,
to keep the cascade running continuously, and exactly one centrifuge
at each stage, then the overall speed of the cascade is determined by
the busiest stage, the 0.7% stage. It can perform its 28 operations
towards the enrichment of a single uranium unit in 140 s, so that is
the overall cycle time of the cascade. It thus takes 70 min to enrich
a full set of reactor fuel. While the cascade is running at this full
speed, its average power consumption is 122.5 kEU/s. The instantaneous
power consumption varies from second to second over the 140 s cycle,
and the maximum possible instantaneous power consumption (with all 34
centrifuges active simultaneously) is 238 kEU/s. It is recommended to
have some battery boxes to smooth out these variations.
If the power supplied to the centrifuge cascade averages less than
122.5 kEU/s, then the cascade can't run continuously. (Also, if the
power supply is intermittent, such as solar, then continuous operation
requires more battery boxes to smooth out the supply variations, even if
the average power is high enough.) Because it's automated and doesn't
require continuous player attention, having the cascade run at less
than full speed shouldn't be a major problem. The enrichment work will
consume the same energy overall regardless of how quickly it's performed,
and the speed will vary in direct proportion to the average power supply
(minus any supply lost because battery boxes filled completely).
If there is insufficient power to run both the centrifuge cascade at
full speed and whatever other machines require power, all machines on
the same power network as the centrifuge will be forced to run at the
same fractional speed. This can be inconvenient, especially if use
of the other machines is less automated than the centrifuge cascade.
It can be avoided by putting the centrifuge cascade on a separate power
network from other machines, and limiting the proportion of the generated
power that goes to it.
If there is sufficient power and it is desired to enrich uranium faster
than a single cascade can, the process can be speeded up more economically
than by building an entire second cascade. Because the stages of the
cascade do different proportions of the work, it is possible to add a
second and subsequent centrifuges to only the busiest stages, and have
the less busy stages still keep up with only a single centrifuge each.
Another possible approach to uranium enrichment is to have no fixed
assignment of fissile proportions to centrifuges, dynamically putting
whatever uranium is available into whichever centrifuges are available.
Theoretically all of the centrifuges can be kept almost totally busy all
the time, making more efficient use of capital resources, and the number
of centrifuges used can be as little (down to one) or as large as desired.
The difficult part is that it is not sufficient to put each uranium dust
pile individually into whatever centrifuge is available: they must be
input in matched pairs. Any odd dust pile in a centrifuge will not be
processed and will prevent that centrifuge from accepting any other input.
### concrete ###
Concrete is a synthetic building material. The technic modpack implements
it in the game.
Two forms of concrete are available as building blocks: ordinary
"concrete" and more advanced "blast-resistant concrete". Despite its
name, the latter has no special resistance to explosions or to any other
means of destruction.
Concrete can also be used to make fences. They act just like wooden
fences, but aren't flammable. Confusingly, the item that corresponds
to a wooden "fence" is called "concrete post". Posts placed adjacently
will implicitly create fence between them. Fencing also appears between
a post and adjacent concrete block.
+69
View File
@@ -0,0 +1,69 @@
Tools in Technic fall into two categories: manual and electric. Both sets of tools are unstackable in the inventory. Manual tools work similarly to the manual tools in `minetest`, they have limited durability that is reduced during use (they disappear after they hit 0 durability). Manual tools can be regenerated by using the Tool Workshop, so that valuable tools, such as mese or diamond tools can be preserved. Electric tools require charging in Battery Boxes (of any voltage) and will not be destroyed when they reach 0 charge. Higher levels of electric tools are more powerful and contain more charge so they work longer.
## Chainsaw
<img src="../textures/technic_chainsaw.png"/>
This electric tool speeds up harvesting of wood by allowing the harvesting of an entire tree in a single click. Just click on any part of a tree, and the wood and leaves of that tree at that point and above will be cut and dropped on the ground as blocks. It has enough charge to cut down about 5 trees before running out of power.
**Note:** If you can't find the drops from using the chainsaw, dig around as they can be buried under neighbouring terrain blocks.
## Flashlight
<img src="../textures/technic_flashlight.png"/>
This electric tool illumates the area in front of the player. While it is electric, and so requires charging, to use it one only needs to equip it. Punching with this tool does nothing.
## Lava can
<img src="../textures/technic_lava_can.png"/>
The lava can works similarly to the bucket tool and the water can, but instead of carrying water, it can carry lava source blocks (the ones that aren't animated), up to 8 at a time. To use, simply equip and click on lava source blocks (they will highlight in black). Then clicking anywhere where there isn't a lava source block will place them again.
**Warning:** Be careful where you place these source blocks! Since you're placing lava source blocks, lava will flow out of them and may trap or burn you depending on where you are.
## Mining drill
<img src="../textures/technic_mining_drill.png"/>
<img src="../textures/technic_mining_drill_mk2.png"/>
<img src="../textures/technic_mining_drill_mk3.png"/>
This electric tool operates similarly to a pickaxe, though every block takes exactly one click to drill out. While the Mining Laser allows for faster collection of bulk minerals, it's cutting path can sometimes be undesirable, and the mining drill is useful in these instances.
The higher levels of the mining drill allow for drilling more blocks at once, substantially reducing mining time. At mark-2, the mining drill can drill 3 blocks at once, either 3-deep, 3-wide, or 3-tall. At mark-3, the mining drill can drill 9-blocks at once in a 3x3 layout around the block selected. Switching between these modes can be done by shift-clicking.
**Advice:** Since these tools require substantially more diamonds than the mining laser, and yet drills slower, it is suggested that a mining laser be built first.
## Mining laser
<img src="../textures/technic_mining_laser_mk1.png"/>
<img src="../textures/technic_mining_laser_mk2.png"/>
<img src="../textures/technic_mining_laser_mk3.png"/>
This electric tool is a substantial upgrade above the pickaxe that you start mining with. The laser works by drilling directly forward 7 blocks making a path that's roughly 1-block in diameter. The laser is electric, requiring charging in a battery box (any voltage). As soon as an LV electrical system is set up, this is a great next step.
## Sonic screwdriver
<img src="../textures/technic_sonic_screwdriver.png"/>
This electric tool is used for rotating nodes, much like the default Screwdriver tool, but loses charge instead of durability. This is useful for any nodes which have a "front" direction, like stairs, machines, furniture, etc. Nodes are rotated around the Y-axes (vertical).
## Tree tap
<img src="../textures/technic_tree_tap.png"/>
This manual tool allows for harvesting latex from rubber trees. Extracting latex can be done by hitting rubber trees with it yields for 1 latex. The rubber tree will regenerate its latex supply over the course of a day.
## Water can
<img src="../textures/technic_water_can.png"/>
The water can works similarly to the bucket tool from minetest_game, but it can hold 16 `water_source` (the water blocks that don't look like they're flowing) blocks instead of 1. To use, simply equip the water can and click on water source blocks (they will highlight in black). Then clicking anywhere where there isn't a `water_source` block with the water can equipped will place them.
**Warning:** Be careful where you place these source blocks! Since you're placing water source blocks, water will flow out of them and may trap or drown you depending on where you are.
## Wrench
<img src="../../wrench/textures/technic_wrench.png"/>
The manual tool allows for moving blocks which contain an inventory, like chests. A shift-right-click on a machine/chest will immediately add it to the inventory.
+25
View File
@@ -0,0 +1,25 @@
minetest.register_abm({
nodenames = {"technic:hv_nuclear_reactor_core_active"},
interval = 10,
chance = 1,
action = function(pos, node)
minetest.add_particlespawner({
amount = 50,
time = 10,
minpos = {x=pos.x-0.5, y=pos.y-0.5, z=pos.z-0.5},
maxpos = {x=pos.x+0.5, y=pos.y+0.5, z=pos.z+0.5},
minvel = {x=-0.8, y=-0.8, z=-0.8},
maxvel = {x=0.8, y=0.8, z=0.8},
minacc = {x=0,y=0,z=0},
maxacc = {x=0,y=0,z=0},
minexptime = 0.5,
maxexptime = 2,
minsize = 1,
maxsize = 2,
texture = "blueparticle.png",
glow = 5
})
end
})
+262
View File
@@ -0,0 +1,262 @@
local constant_digit_count = technic.config:get("constant_digit_count")
-- converts a number to a readable string with SI prefix, e.g. 10000 → "10 k",
-- 15 → "15 ", 0.1501 → "150.1 m"
-- a non-breaking space (U+a0) instead of a usual one is put after number
-- The precision is 4 digits
local prefixes = {[-8] = "y", [-7] = "z", [-6] = "a", [-5] = "f", [-4] = "p",
[-3] = "n", [-2] = "µ", [-1] = "m", [0] = "", [1] = "k", [2] = "M",
[3] = "G", [4] = "T", [5] = "P", [6] = "E", [7] = "Z", [8] = "Y"}
function technic.pretty_num(num)
-- the small number added is due to floating point inaccuracy
local b = math.floor(math.log10(math.abs(num)) +0.000001)
local pref_i
if b ~= 0 then
-- b is decremented by 1 to avoid a single digit with many decimals,
-- e.g. instead of 1.021 MEU, 1021 kEU is shown
pref_i = math.floor((b - 1) / 3)
else
-- as special case, avoid showing e.g. 1100 mEU instead of 1.1 EU
pref_i = 0
end
if not prefixes[pref_i] then
-- This happens for 0, nan, inf, very big values, etc.
if num == 0 then
-- handle 0 explicilty to avoid showing "-0"
if not constant_digit_count then
return "0 "
end
-- gives 0.000
return string.format("%.3f ", 0)
end
return string.format("%.4g ", num)
end
num = num * 10 ^ (-3 * pref_i)
if constant_digit_count then
local comma_digits_cnt = 3 - (b - 3 * pref_i)
return string.format("%." .. comma_digits_cnt .. "f %s",
num, prefixes[pref_i])
end
return string.format("%.4g %s", num, prefixes[pref_i])
end
-- some unittests
assert(technic.pretty_num(-0) == "0 ")
assert(technic.pretty_num(0) == "0 ")
assert(technic.pretty_num(1234) == "1234 ")
assert(technic.pretty_num(123456789) == "123.5 M")
-- used to display power values
function technic.EU_string(num)
return technic.pretty_num(num) .. "EU"
end
--- Same as minetest.swap_node, but only changes name
-- and doesn't re-set if already set.
function technic.swap_node(pos, name)
local node = minetest.get_node(pos)
if node.name ~= name then
node.name = name
minetest.swap_node(pos, node)
end
end
--- Fully charge RE chargeable item.
-- Must be defined early to reference in item definitions.
function technic.refill_RE_charge(stack)
local max_charge = technic.power_tools[stack:get_name()]
if not max_charge then return stack end
technic.set_RE_wear(stack, max_charge, max_charge)
local meta = minetest.deserialize(stack:get_metadata()) or {}
meta.charge = max_charge
stack:set_metadata(minetest.serialize(meta))
return stack
end
function technic.set_RE_charge(stack, charge)
local max_charge = technic.power_tools[stack:get_name()]
if max_charge then
technic.set_RE_wear(stack, charge, max_charge)
local meta = minetest.deserialize(stack:get_metadata()) or {}
meta.charge = math.min(math.max(0, charge), max_charge)
stack:set_metadata(minetest.serialize(meta))
end
end
technic.set_charge = technic.set_RE_charge
function technic.get_RE_charge(stack)
local max_charge = technic.power_tools[stack:get_name()]
if max_charge then
local meta = minetest.deserialize(stack:get_metadata()) or {}
return meta.charge or 0, max_charge
end
return 0, 0
end
technic.get_charge = technic.get_RE_charge
function technic.use_RE_charge(stack, amount)
if technic.creative_mode or amount <= 0 then
-- Do not check charge in creative mode or when trying to use zero amount
return true
end
local charge = technic.get_RE_charge(stack)
if charge < amount then
-- Not enough energy available
return false
end
technic.set_RE_charge(stack, charge - amount)
-- Charge used successfully
return true
end
technic.use_charge = technic.use_RE_charge
-- If the node is loaded, returns it. If it isn't loaded, load it and return nil.
function technic.get_or_load_node(pos)
local node = minetest.get_node_or_nil(pos)
if node then return node end
local vm = VoxelManip()
local _, _ = vm:read_from_map(pos, pos)
return nil
end
technic.tube_inject_item = pipeworks.tube_inject_item or function(pos, start_pos, velocity, item)
local tubed = pipeworks.tube_item(vector.new(pos), item)
tubed:get_luaentity().start_pos = vector.new(start_pos)
tubed:set_velocity(velocity)
tubed:set_acceleration(vector.new(0, 0, 0))
end
--- Iterates over the node positions along the specified ray.
-- The returned positions will not include the starting position.
function technic.trace_node_ray(pos, dir, range)
local x_step = dir.x > 0 and 1 or -1
local y_step = dir.y > 0 and 1 or -1
local z_step = dir.z > 0 and 1 or -1
local i = 1
return function(p)
-- Approximation of where we should be if we weren't rounding
-- to nodes. This moves forward a bit faster then we do.
-- A correction is done below.
local real_x = pos.x + (dir.x * i)
local real_y = pos.y + (dir.y * i)
local real_z = pos.z + (dir.z * i)
-- How far off we've gotten from where we should be.
local dx = math.abs(real_x - p.x)
local dy = math.abs(real_y - p.y)
local dz = math.abs(real_z - p.z)
-- If the real position moves ahead too fast, stop it so we
-- can catch up. If it gets too far ahead it will smooth
-- out our movement too much and we won't turn fast enough.
if dx + dy + dz < 2 then
i = i + 1
end
-- Step in whichever direction we're most off course in.
if dx > dy then
if dx > dz then
p.x = p.x + x_step
else
p.z = p.z + z_step
end
elseif dy > dz then
p.y = p.y + y_step
else
p.z = p.z + z_step
end
if vector.distance(pos, p) > range then
return nil
end
return p
end, vector.round(pos)
end
--- Like trace_node_ray, but includes extra positions close to the ray.
function technic.trace_node_ray_fat(pos, dir, range)
local x_step = dir.x > 0 and 1 or -1
local y_step = dir.y > 0 and 1 or -1
local z_step = dir.z > 0 and 1 or -1
local next_poses = {}
local i = 1
return function(p)
local ni, np = next(next_poses)
if np then
next_poses[ni] = nil
return np
end
-- Approximation of where we should be if we weren't rounding
-- to nodes. This moves forward a bit faster then we do.
-- A correction is done below.
local real_x = pos.x + (dir.x * i)
local real_y = pos.y + (dir.y * i)
local real_z = pos.z + (dir.z * i)
-- How far off we've gotten from where we should be.
local dx = math.abs(real_x - p.x)
local dy = math.abs(real_y - p.y)
local dz = math.abs(real_z - p.z)
-- If the real position moves ahead too fast, stop it so we
-- can catch up. If it gets too far ahead it will smooth
-- out our movement too much and we won't turn fast enough.
if dx + dy + dz < 2 then
i = i + 1
end
-- Step in whichever direction we're most off course in.
local sx, sy, sz -- Whether we've already stepped along each axis
if dx > dy then
if dx > dz then
sx = true
p.x = p.x + x_step
else
sz = true
p.z = p.z + z_step
end
elseif dy > dz then
sy = true
p.y = p.y + y_step
else
sz = true
p.z = p.z + z_step
end
if vector.distance(pos, p) > range then
return nil
end
-- Add other positions that we're significantly off on.
-- We can just use fixed integer keys here because the
-- table will be completely cleared before we reach this
-- code block again.
local dlen = math.sqrt(dx*dx + dy*dy + dz*dz)
-- Normalized axis deltas
local dxn, dyn, dzn = dx / dlen, dy / dlen, dz / dlen
if not sx and dxn > 0.5 then
next_poses[1] = vector.new(p.x + x_step, p.y, p.z)
end
if not sy and dyn > 0.5 then
next_poses[2] = vector.new(p.x, p.y + y_step, p.z)
end
if not sz and dzn > 0.5 then
next_poses[3] = vector.new(p.x, p.y, p.z + z_step)
end
return p
end, vector.round(pos)
end
+76
View File
@@ -0,0 +1,76 @@
-- Minetest 0.4.7 mod: technic
-- namespace: technic
-- (c) 2012-2013 by RealBadAngel <mk@realbadangel.pl>
local load_start = os.clock()
technic = rawget(_G, "technic") or {}
technic.creative_mode = minetest.settings:get_bool("creative_mode")
local modpath = minetest.get_modpath("technic")
technic.modpath = modpath
-- Boilerplate to support intllib
if rawget(_G, "intllib") then
technic.getter = intllib.Getter()
else
technic.getter = function(s, a, ...)
if a == nil then
return s
end
a = {a, ...}
return s:gsub(
"(@?)@(%(?)(%d+)(%)?)",
function(e, o, n, c)
if e == "" then
return a[tonumber(n)] .. (o == "" and c or "")
else
return "@" .. o .. n .. c
end
end
)
end
end
local S = technic.getter
-- Read configuration file
dofile(modpath.."/config.lua")
-- Lag monitoring
dofile(modpath.."/max_lag.lua")
-- Helper functions
dofile(modpath.."/helpers.lua")
-- Items
dofile(modpath.."/items.lua")
-- Craft recipes for items
dofile(modpath.."/crafts.lua")
-- Register functions
dofile(modpath.."/register.lua")
-- Radiation
dofile(modpath.."/radiation.lua")
-- Machines
dofile(modpath.."/machines/init.lua")
-- Tools
dofile(modpath.."/tools/init.lua")
-- Aliases for legacy node/item names
dofile(modpath.."/legacy.lua")
-- visual effects
dofile(modpath.."/effects.lua")
if minetest.settings:get_bool("log_mods") then
print(S("[Technic] Loaded in %f seconds"):format(os.clock() - load_start))
end
if minetest.settings:get_bool("enable_technic_integration_test") then
dofile(modpath.."/integration_test.lua")
end
@@ -0,0 +1,56 @@
minetest.log("warning", "[technic] integration-test enabled!")
-- those mods have to be present
local assert_mods = {
"technic",
"pipeworks"
}
-- those nodes have to be present
local assert_nodes = {
"technic:admin_anchor"
}
-- defered integration test function
minetest.register_on_mods_loaded(function()
minetest.log("warning", "[technic] all mods loaded, starting delayed test-function")
minetest.after(1, function()
minetest.log("warning", "[technic] starting integration test")
-- export stats
local file = io.open(minetest.get_worldpath().."/registered_nodes.txt", "w" );
if file then
for name in pairs(minetest.registered_nodes) do
file:write(name .. '\n')
end
file:close()
end
-- check mods
for _, modname in ipairs(assert_mods) do
if not minetest.get_modpath(modname) then
error("Mod not present: " .. modname)
end
end
-- check nodes
for _, nodename in ipairs(assert_nodes) do
if not minetest.registered_nodes[nodename] then
error("Node not present: " .. nodename)
end
end
-- write success flag
local data = minetest.write_json({ success = true }, true);
file = io.open(minetest.get_worldpath().."/integration_test.json", "w" );
if file then
file:write(data)
file:close()
end
minetest.log("warning", "[technic] integration tests done!")
minetest.request_shutdown("success")
end)
end)
+203
View File
@@ -0,0 +1,203 @@
local S = technic.getter
minetest.register_craftitem("technic:silicon_wafer", {
description = S("Silicon Wafer"),
inventory_image = "technic_silicon_wafer.png",
})
minetest.register_craftitem( "technic:doped_silicon_wafer", {
description = S("Doped Silicon Wafer"),
inventory_image = "technic_doped_silicon_wafer.png",
})
minetest.register_craftitem("technic:uranium_fuel", {
description = S("Uranium Fuel"),
inventory_image = "technic_uranium_fuel.png",
})
minetest.register_craftitem( "technic:diamond_drill_head", {
description = S("Diamond Drill Head"),
inventory_image = "technic_diamond_drill_head.png",
})
minetest.register_tool("technic:blue_energy_crystal", {
description = S("Blue Energy Crystal"),
inventory_image = minetest.inventorycube(
"technic_diamond_block_blue.png",
"technic_diamond_block_blue.png",
"technic_diamond_block_blue.png"),
wear_represents = "technic_RE_charge",
on_refill = technic.refill_RE_charge,
tool_capabilities = {
max_drop_level = 0,
groupcaps = {
fleshy = {times={}, uses=10000, maxlevel=0}
}
}
})
minetest.register_tool("technic:green_energy_crystal", {
description = S("Green Energy Crystal"),
inventory_image = minetest.inventorycube(
"technic_diamond_block_green.png",
"technic_diamond_block_green.png",
"technic_diamond_block_green.png"),
wear_represents = "technic_RE_charge",
on_refill = technic.refill_RE_charge,
tool_capabilities = {
max_drop_level = 0,
groupcaps = {
fleshy = {times={}, uses=10000, maxlevel=0}
}
}
})
minetest.register_tool("technic:red_energy_crystal", {
description = S("Red Energy Crystal"),
inventory_image = minetest.inventorycube(
"technic_diamond_block_red.png",
"technic_diamond_block_red.png",
"technic_diamond_block_red.png"),
wear_represents = "technic_RE_charge",
on_refill = technic.refill_RE_charge,
tool_capabilities = {
max_drop_level = 0,
groupcaps = {
fleshy = {times={}, uses=10000, maxlevel=0}
}
}
})
minetest.register_craftitem("technic:copper_coil", {
description = S("Copper Coil"),
inventory_image = "technic_copper_coil.png",
})
minetest.register_craftitem("technic:lv_transformer", {
description = S("Low Voltage Transformer"),
inventory_image = "technic_lv_transformer.png",
})
minetest.register_craftitem("technic:mv_transformer", {
description = S("Medium Voltage Transformer"),
inventory_image = "technic_mv_transformer.png",
})
minetest.register_craftitem( "technic:hv_transformer", {
description = S("High Voltage Transformer"),
inventory_image = "technic_hv_transformer.png",
})
minetest.register_craftitem( "technic:control_logic_unit", {
description = S("Control Logic Unit"),
inventory_image = "technic_control_logic_unit.png",
})
minetest.register_craftitem("technic:mixed_metal_ingot", {
description = S("Mixed Metal Ingot"),
inventory_image = "technic_mixed_metal_ingot.png",
})
minetest.register_craftitem("technic:composite_plate", {
description = S("Composite Plate"),
inventory_image = "technic_composite_plate.png",
})
minetest.register_craftitem("technic:copper_plate", {
description = S("Copper Plate"),
inventory_image = "technic_copper_plate.png",
})
minetest.register_craftitem("technic:carbon_plate", {
description = S("Carbon Plate"),
inventory_image = "technic_carbon_plate.png",
})
minetest.register_craftitem("technic:graphite", {
description = S("Graphite"),
inventory_image = "technic_graphite.png",
})
minetest.register_craftitem("technic:carbon_cloth", {
description = S("Carbon Cloth"),
inventory_image = "technic_carbon_cloth.png",
})
minetest.register_node("technic:machine_casing", {
description = S("Machine Casing"),
groups = {cracky=2},
sunlight_propagates = true,
paramtype = "light",
drawtype = "allfaces",
tiles = {"technic_machine_casing.png"},
sounds = default.node_sound_stone_defaults(),
})
for p = 0, 35 do
local nici = (p ~= 0 and p ~= 7 and p ~= 35) and 1 or nil
local psuffix = p == 7 and "" or p
local ingot = "technic:uranium"..psuffix.."_ingot"
local block = "technic:uranium"..psuffix.."_block"
local ov = p == 7 and minetest.override_item or nil;
(ov or minetest.register_craftitem)(ingot, {
description = string.format(S("%.1f%%-Fissile Uranium Ingot"), p/10),
inventory_image = "technic_uranium_ingot.png",
groups = {uranium_ingot=1, not_in_creative_inventory=nici},
});
-- Note on radioactivity of blocks:
-- Source: <http://www.wise-uranium.org/rup.html>
-- The baseline radioactivity of an isotope is not especially
-- correlated with whether it's fissile (i.e., suitable as
-- reactor fuel). Natural uranium consists mainly of fissile
-- U-235 and non-fissile U-238, and both U-235 and U-238 are
-- significantly radioactive. U-235's massic activity is
-- about 80.0 MBq/kg, and U-238's is about 12.4 MBq/kg, which
-- superficially suggests that 3.5%-fissile uranium should have
-- only 1.19 times the activity of fully-depleted uranium.
-- But a third isotope affects the result hugely: U-234 has
-- massic activity of 231 GBq/kg. Natural uranium has massic
-- composition of 99.2837% U-238, 0.711% U-235, and 0.0053% U-234,
-- so its activity comes roughly 49% each from U-234 and U-238
-- and only 2% from U-235. During enrichment via centrifuge,
-- the U-234 fraction is concentrated along with the U-235, with
-- the U-234:U-235 ratio remaining close to its original value.
-- (Actually the U-234 gets separated from U-238 slightly more
-- than the U-235 is, so the U-234:U-235 ratio is slightly
-- higher in enriched uranium.) A typical massic composition
-- for 3.5%-fissile uranium is 96.47116% U-238, 3.5% U-235, and
-- 0.02884% U-234. This gives 3.5%-fissile uranium about 6.55
-- times the activity of fully-depleted uranium. The values we
-- compute here for the "radioactive" group value are based on
-- linear interpolation of activity along that scale, rooted at
-- a natural (0.7%-fissile) uranium block having the activity of
-- 9 uranium ore blocks (due to 9 ingots per block). The group
-- value is proportional to the square root of the activity, and
-- uranium ore has radioactive=1. This yields radioactive=1.0
-- for a fully-depleted uranium block and radioactive=2.6 for
-- a 3.5%-fissile uranium block.
local radioactivity = math.floor(math.sqrt((1+5.55*p/35) * 18 / (1+5.55*7/35)) + 0.5);
(ov or minetest.register_node)(block, {
description = string.format(S("%.1f%%-Fissile Uranium Block"), p/10),
tiles = {"technic_uranium_block.png"},
is_ground_content = true,
groups = {uranium_block=1, not_in_creative_inventory=nici,
cracky=1, level=2, radioactive=radioactivity},
sounds = default.node_sound_stone_defaults(),
});
if not ov then
minetest.register_craft({
output = block,
recipe = {
{ingot, ingot, ingot},
{ingot, ingot, ingot},
{ingot, ingot, ingot},
},
})
minetest.register_craft({
output = ingot.." 9",
recipe = {{block}},
})
end
end
+41
View File
@@ -0,0 +1,41 @@
-- Aliases to convert from legacy node/item names
technic.legacy_nodenames = {
["technic:alloy_furnace"] = "technic:lv_alloy_furnace",
["technic:alloy_furnace_active"] = "technic:lv_alloy_furnace_active",
["technic:battery_box"] = "technic:lv_battery_box0",
["technic:battery_box1"] = "technic:lv_battery_box1",
["technic:battery_box2"] = "technic:lv_battery_box2",
["technic:battery_box3"] = "technic:lv_battery_box3",
["technic:battery_box4"] = "technic:lv_battery_box4",
["technic:battery_box5"] = "technic:lv_battery_box5",
["technic:battery_box6"] = "technic:lv_battery_box6",
["technic:battery_box7"] = "technic:lv_battery_box7",
["technic:battery_box8"] = "technic:lv_battery_box8",
["technic:electric_furnace"] = "technic:lv_electric_furnace",
["technic:electric_furnace_active"] = "technic:lv_electric_furnace_active",
["technic:grinder"] = "technic:lv_grinder",
["technic:grinder_active"] = "technic:lv_grinder_active",
["technic:extractor"] = "technic:lv_extractor",
["technic:extractor_active"] = "technic:lv_extractor_active",
["technic:compressor"] = "technic:lv_compressor",
["technic:compressor_active"] = "technic:lv_compressor_active",
["technic:hv_battery_box"] = "technic:hv_battery_box0",
["technic:mv_battery_box"] = "technic:mv_battery_box0",
["technic:generator"] = "technic:lv_generator",
["technic:generator_active"] = "technic:lv_generator_active",
["technic:iron_dust"] = "technic:wrought_iron_dust",
["technic:enriched_uranium"] = "technic:uranium35_ingot",
}
for old, new in pairs(technic.legacy_nodenames) do
minetest.register_alias(old, new)
end
for i = 0, 64 do
minetest.register_alias("technic:hv_cable"..i, "technic:hv_cable")
minetest.register_alias("technic:mv_cable"..i, "technic:mv_cable")
minetest.register_alias("technic:lv_cable"..i, "technic:lv_cable")
end
+168
View File
@@ -0,0 +1,168 @@
# German Translation for Technic Mod
# Deutsche Uebersetzung des Technic Mods
# by Xanthin
## Misc
[Technic] Loaded in %f seconds = [Technic] ist in %f Sekunden geladen
## Items
Silicon Wafer = Siliziumscheibe
Doped Silicon Wafer = Dotierte Siliziumscheibe
Enriched Uranium = Angereichertes Uran
Uranium Fuel = Uranbrennstoff
Diamond Drill Head = Diamantbohrkopf
Blue Energy Crystal = Blauer Energiekristall
Green Energy Crystal = Gruener Energiekristall
Red Energy Crystal = Roter Energiekristall
Fine Copper Wire = Feinkupferdraht
Copper Coil = Kupferspule
Electric Motor = Elektromotor
Low Voltage Transformer = Niederspannungstransformator
Medium Voltage Transformer = Mittelspannungstransformator
High Voltage Transformer = Hochspannungstransformator
Control Logic Unit = Steuer- und Regelungseinheit
Mixed Metal Ingot = Mischmetallbarren
Composite Plate = Verbundplatte
Copper Plate = Kupferplatte
Carbon Plate = Kohlefaserplatte
Graphite = Graphit
Carbon Cloth = Kohlefasergewebe
Raw Latex = Rohlatex
Rubber Fiber = Gummifaser
%.1f%%-Fissile Uranium Ingot =
%.1f%%-Fissile Uranium Block =
## Machine misc
Machine cannot be removed because it is not empty = Die Maschine kann nicht entfernt werden, weil sie noch nicht leer ist.
Inventory move disallowed due to protection = Das Inventar ist geschuetzt, Zugriff verweigert.
# $1: Machine name (Includes tier)
@1 Active (@2 EU) = @1 ist eingeschaltet (@2 EU)
%s Active = %s ist eingeschaltet
%s Disabled = %s ist ausgeschaltet
%s Enabled =
%s Idle = %s ist bereit
%s Improperly Placed = %s ist falsch plaziert
%s Unpowered = %s hat keine Stromversorgung
%s Out Of Fuel = %s hat keinen Brennstoff
%s Has Bad Cabling = %s ist falsch verkabelt
%s Has No Network = %s hat kein Netzwerk
%s Finished = %s ist fertig
Enable/Disable = Einschalten/Ausschalten
Range = Reichweite
Upgrade Slots = Verbesserungsfaecher
In: = Rein:
Out: = Raus:
Slot %d = Fach %d
Itemwise = Einzelstuecke
Stackwise = Ganzer Stapel
Owner: =
Unlocked =
Locked =
Radius: =
Enabled =
Disabled =
## Machine names
# $1: Tier
%s Alloy Furnace = %s Legierungsofen
%s Battery Box = %s Batteriebox
%s Cable = %s Kabel
%s Compressor = %s Kompressor
%s Extractor = %s Extraktor
%s Forcefield Emitter = %s Kraftfeld-Emitter
%s Furnace = %s Ofen
%s Grinder = %s Schleifmaschine
%s Music Player = %s Musikspieler
%s Quarry = %s Steinbruch
%s Tool Workshop = %s Werkzeugwerkstatt
Arrayed Solar %s Generator = %s Solaranlage
Fuel-Fired %s Generator = %s Kohle-Generator
Geothermal %s Generator = %s Geothermie-Generator
Hydro %s Generator = %s Wassermuehle
Nuclear %s Generator Core = %s Reaktorkern
Small Solar %s Generator = %s Solarmodul
Wind %s Generator = %s Windmuehle
Self-Contained Injector = Selbstversorger-Injektor
Constructor Mk%d = Konstruktor Modell %d
Frame = Rahmen
Frame Motor = Rahmenmotor
Template = Schablone
Template (replacing) = Schablone (ersetzend)
Template motor = Schablonenmotor
Template tool = Schablonenwerkzeug
Battery Box = Batteriebox
Supply Converter = Stromumwandler
Switching Station = Schaltanlage
Fuel-Fired Alloy Furnace = Kohle-Legierungsofen
Fuel-Fired Furnace = Kohle-Ofen
Wind Mill Frame = Windmuehlengeruest
Forcefield = Kraftfeld
Nuclear Reactor Rod Compartment = Brennstabfaecher
Administrative World Anchor =
## Machine-specific
# $1: Pruduced EU
Charge = Aufladen
Discharge = Entladen
Power level = Energiestufe
# $1: Tier $2: current_charge $3: max_charge
@1 Battery Box: @2/@3 = @1 Batteriebox: @2/@3
# $1: Machine name $2: Supply $3: Demand
@1. Supply: @2 Demand: @3 = @1. Versorgung: @2 Bedarf: @3
Production at %d%% = Produktion bei %d%%
Choose Milling Program: = Waehle ein Fraesprogramm:
Slim Elements half / normal height: = Schmale Elemente von halber / normaler Hoehe:
Current track %s = Aktueller Titel %s
Stopped =
Keeping %d/%d map blocks loaded =
Digging not started =
Digging finished =
Digging %d m above machine =
Digging %d m below machine =
## Grinder Recipes
# $1: Name
%s Dust = %sstaub
Akalin = Akalin
Alatro = Alatro
Arol = Arol
Brass = Messing
Bronze = Bronze
Carbon Steel = Kohlenstoffstahl
Cast Iron = Gusseisen
Chromium = Chrom
Coal = Kohle
Copper = Kupfer
Gold = Gold
Mithril = Mithril
Silver = Silber
Stainless Steel = Edelstahl
Talinite = Talinite
Tin = Zinn
Wrought Iron = Schmiedeeisen
Zinc = Zink
%.1f%%-Fissile Uranium =
## Tools
RE Battery = Akkubatterie
Water Can = Wasserkanister
Lava Can = Lavakanister
Chainsaw = Kettensaege
Flashlight = Taschenlampe
3 nodes deep. = 3 Bloecke tief.
3 nodes tall. = 3 Bloecke hoch.
3 nodes wide. = 3 Bloecke breit.
3x3 nodes. = 3x3 Bloecke.
Use while sneaking to change Mining Drill Mk%d modes. = Halte die Shift-Taste beim Benutzen gedrueckt, um die Funktion des Bergbaubohrers Modell %d zu aendern.
Mining Drill Mk%d Mode %d = Bergbaubohrer Modell %d Funktion %d
Mining Drill Mk%d = Bergbaubohrer Modell %d
Mining Laser Mk%d = Bergbaulaser Modell %d
Single node. = Einzelblock
Sonic Screwdriver = Schallschraubendreher
Tree Tap = Baumzapfhahn
## Craft descriptions
Alloy cooking =
Grinding =
Compressing =
Extracting =
+162
View File
@@ -0,0 +1,162 @@
# Spanish Translation for Technic Mod
# Traduccion al Español del Mod Technic
# Autor: Diego Martínez <kaeza>
## Misc
[Technic] Loaded in %f seconds = [Technic] Cargado en %f segundos
## Items
Silicon Wafer = Oblea de Silicio
Doped Silicon Wafer = Oblea de Silicio Dopada
Enriched Uranium = Uranio Enriquecido
Uranium Fuel = Combustible de Uranio
Diamond Drill Head = Mecha de Taladro de Diamante
Blue Energy Crystal = Cristal de Energia Azul
Green Energy Crystal = Cristal de Energia Verde
Red Energy Crystal = Cristal de Energia Rojo
Fine Copper Wire = Cable Fino de Cobre
Copper Coil = Resorte de Cobre
Electric Motor = Motor Electrico
Low Voltage Transformer = Transformador de Bajo Voltaje
Medium Voltage Transformer = Transformador de Voltaje Medio
High Voltage Transformer = Transformador de Alto Voltaje
Control Logic Unit = Unidad Logica de Control
Mixed Metal Ingot = Lingote de Metal Mezclado
Composite Plate = Placa de Compuestos
Copper Plate = Placa de Cobre
Carbon Plate = Placa de Carbon
Graphite = Grafito
Carbon Cloth = Tela de Carbon
Raw Latex = Latex Crudo
Rubber Fiber = Fibra de Hule
%.1f%%-Fissile Uranium Ingot =
%.1f%%-Fissile Uranium Block =
## Machine misc
Machine cannot be removed because it is not empty = La maquina no puede removerse porque no esta vacia
Inventory move disallowed due to protection =
# $1: Machine name (Includes tier)
@1 Active (@2 EU) = @1 Activo (@2 EU)
%s Active = %s Activo
%s Enabled =
%s Idle = %s Quieto
%s Unpowered = %s Sin Energia
%s Out Of Fuel = %s Sin Combustible
%s Has Bad Cabling = %s Tiene Mal Cableado
%s Has No Network = %s No Tiene Una Red
%s Finished = %s Terminado
%s Disabled = %s Deshabilitado
%s Improperly Placed = %s No Colocado Apropiadamente
Range = Alcance
Enable/Disable = Habilitar/Deshabilitar
Itemwise =
Stackwise =
Owner: =
Unlocked =
Locked =
Radius: =
Enabled =
Disabled =
## Machine names
# $1: Tier
%s Alloy Furnace = Horno de Aleacion %s
%s Battery Box = Caja de Bateria %s
%s Cable = Cable %s
%s Compressor = Compresor %s
%s Extractor = Extractor %s
%s Forcefield Emitter = Emisor de Campo de Fuerza %s
%s Furnace = Horno %s
%s Grinder = Amoladora %s
%s Music Player = Reproductor de Musica %s
%s Quarry = Cantera %s
%s Tool Workshop = Taller de Herramientas %s
Arrayed Solar %s Generator = Panel Solar %s
Fuel-Fired %s Generator = Generador a Carbon %s
Geothermal %s Generator = Generador Geotermico %s
Hydro %s Generator = Molino de Agua %s
Nuclear %s Generator Core = Nucleo de Reactor Nuclear %s
Small Solar %s Generator = Panel Solar %s
Wind %s Generator = Molino de Viento %s
Self-Contained Injector =
Constructor Mk%d =
Frame =
Frame Motor =
Template =
Template (replacing) =
Template Motor =
Template Tool =
Supply Converter = Convertidor de Alimentacion
Switching Station = Estacion de Conmutacion
Battery Box = Caja de Baterias
Fuel-Fired Alloy Furnace = Horno de Aleacion a Carbon
Fuel-Fired Furnace = Horno a Carbon
Forcefield = Campo de Fuerza
Nuclear Reactor Rod Compartment = Compartimiento para Vara de Reactor Nuclear
Wind Mill Frame = Armazon de Molino de Viento
Administrative World Anchor =
## Machine-specific
# $1: Pruduced EU
Charge = Cargar
Discharge = Descargar
Power level = Nivel de Poder
# $1: Tier $2: current_charge $3: max_charge
@1 Battery Box: @2/@3 = Caja de Bateria @1: @2/@3
# $1: Machine name $2: Supply $3: Demand
@1. Supply: @2 Demand: @3 = @1. Alimentacion: @2 Demanda: @3
# $1: Production percent
Production at %d%% = Produccion en %d%%
Stopped =
Keeping %d/%d map blocks loaded =
Digging not started =
Digging finished =
Digging %d m above machine =
Digging %d m below machine =
## Grinder Recipes
# $1: Name
%s Dust = Polvo de %s
Akalin = Akalina
Alatro = Alatro
Arol = Arol
Brass = Laton
Bronze = Bronce
Carbon Steel = Acero al Carbono
Cast Iron = Hierro Fundido
Chromium = Cromo
Coal = Carbon
Copper = Cobre
Gold = Oro
Mithril = Mitrilo
Silver = Plata
Stainless Steel = Acero Inoxidable
Talinite = Talinita
Tin = Estanio
Wrought Iron = Hierro Forjado
Zinc = Zinc
%.1f%%-Fissile Uranium =
## Tools
RE Battery =
Water Can = Bidon de Agua
Lava Can = Bidon de Lava
Chainsaw = Motosierra
Flashlight = Linterna
3 nodes deep. = 3 nodos de profundo.
3 nodes tall. = 3 nodos de alto.
3 nodes wide. = 3 nodos de ancho.
3x3 nodes. = 3x3 nodos.
Use while sneaking to change Mining Drill Mk%d modes. = Manten pulsado Mayus y Usar para cambiar el modo del Taladro de Mineria Mk%d.
Mining Drill Mk%d Mode %d = Taladro de Mineria Mk%d Modo %d
Mining Drill Mk%d = Taladro de Mineria Mk%d
Mining Laser Mk%d = Laser de Mineria Mk%d
Single node. = Nodo simple.
Sonic Screwdriver = Destonillador Sonico
Tree Tap = Grifo de Arbol
## Craft descriptions
Alloy cooking =
Grinding =
Compressing =
Extracting =
+215
View File
@@ -0,0 +1,215 @@
# template.txt
# Template for translations of Technic
## Misc
[Technic] Loaded in %f seconds = [Technic] Chargement en %f secondes
## Items
Silicon Wafer = Tranche de silicium
Doped Silicon Wafer = Tranche de silicium doppée
Enriched Uranium = Uranium enrichi
Uranium Fuel = Uranium 235
Diamond Drill Head = Tête de forage en diamant
Blue Energy Crystal = Cristal d'énergie bleu
Green Energy Crystal = Cristal d'énergie vert
Red Energy Crystal = Cristal d'énergie rouge
Fine Copper Wire = Fil en cuivre fin
Fine Gold Wire = Fil en or fin
Fine Silver Wire = Fil en argent fin
Copper Coil = Bobine de cuivre
Electric Motor = Moteur électrique
Low Voltage Transformer = Transformateur basse tension
Medium Voltage Transformer = Transformateur moyenne tension
High Voltage Transformer = Transformateur haute tension
Control Logic Unit = Unité de contrôle logique
Mixed Metal Ingot = Lingot de métal allié
Composite Plate = Plaque composite
Copper Plate = Plaque de cuivre
Carbon Plate = Plaque de carbone
Graphite = Graphite
Carbon Cloth = Fibre de carbone
Raw Latex = Latex brut
Rubber Fiber = Fibre de caoutchouc
%.1f%%-Fissile Uranium Ingot = Lingot d'uranium fissile (%.1f%%)
%.1f%%-Fissile Uranium Block = Bloc d'uranium fissile (%.1f%%)
## Machine misc
Machine cannot be removed because it is not empty = La machine ne peut pas être retirée car elle n'est pas vide
Inventory move disallowed due to protection = Le mouvement d'inventaire n'est pas autorisé en raison de la protection
# $1: Machine name (Includes tier)
@1 Active (@2 EU) = @1 Active (@2 EU)
%s Active = %s actif
%s Disabled = %s désactivé
%s Enabled = %s activé
%s Idle = %s au repos
%s Improperly Placed = %s est mal placé
%s is empty = %s est vide
%s Unpowered = %s non alimenté en énergie
%s Out Of Fuel = %s plus de carburant
%s Has Bad Cabling = %s est mal cablé
%s (Slave) = %s (esclave)
%s Has No Network = %s n'a pas de réseau
%s Finished = %s a fini
Enable/Disable = Activer/Désactiver
Range = Plage
Upgrade Slots = Emplacement d'amélioration
In: = Entrée :
Out: = Sortie :
Slot %d = Emplacement %d
Itemwise = Item par Item
Stackwise = Stack par Stack
Ignoring Mesecon Signal = Ignorer le signal Mesecon
Controlled by Mesecon Signal = Contrôlé par signal Mesecon
Owner: = Propriétaire :
Unlocked = Déverrouillé
Locked = Verrouillé
Radius: = Rayon :
Enabled = Activé
Disabled = Désactivé
## Machine names
# $1: Tier
%s Alloy Furnace = Four à alliage %s
%s Battery Box = Batterie %s
%s Cable = Câble %s
%s CNC Machine = Machine-outils %s
%s Centrifuge = Centrifugeuse %s
%s Compressor = Compresseur %s
%s Extractor = Extracteur %s
%s Forcefield Emitter = Emetteur de champ de force %s
%s Furnace = Four %s
%s Grinder = Broyeur %s
%s Music Player = Grammophone %s
%s Quarry = Carrière %s
%s Tool Workshop = Atelier d'outillage %s
Arrayed Solar %s Generator = Générateur solaire %s
Fuel-Fired %s Generator = Générateur thermique %s
Geothermal %s Generator = Géénarteur géothermique %s
Hydro %s Generator = Générateur hydroélectrique %s
Nuclear %s Generator Core = Générateur nucléaire %
Small Solar %s Generator = Petit générateur solaire %s
Wind %s Generator = Générateur éolien %s
Self-Contained Injector = Injecteur autonome
Constructor Mk%d = Constructeur Mk%d
Frame = Cadre
Frame Motor = Cadre de moteur
Template = Modèle
Template (replacing) =
Template Motor =
Template Tool =
Battery Box = Compartiment à batterie
Supply Converter = Convertisseur de tension
Switching Station = Station de commutation
Fuel-Fired Alloy Furnace = Four à alliage à carburant
Fuel-Fired Furnace = Four à carburant
Wind Mill Frame = Cadre d'éolienne
Forcefield = Champ de force
Nuclear Reactor Rod Compartment = Compartiment à barres du réacteur nucléaire
Administrative World Anchor =
## Machine-specific
# $1: Pruduced EU
Charge = Charger
Discharge = Décharger
Power level = Niveau d'énergie
# $1: Tier $2: current_charge $3: max_charge
@1 Battery Box: @2/@3 = @1 batterie : @2/@3
# $1: Machine name $2: Supply $3: Demand
@1. Supply: @2 Demand: @3 = @1. fournit : @2 demande : @3
Production at %d%% = Production à %d%%
Choose Milling Program: = Choisissez le programme de fraisage :
Slim Elements half / normal height: =
Current track %s = Morceau actuel %s
Stopped = Arrêté
Keeping %d/%d map blocks loaded =
Digging not started = Creusement non démarré
Digging finished = Creusement terminé
Digging %d m above machine = Creusement à %dm au dessus de la machine
Digging %d m below machine = Creusement à %dm en dessous de la machine
@1 (@2 @3 -> @4 @5) = @1 (@2 @3 -> @4 @5)
## CNC
Cylinder =
Element Cross = Elément croisé
Element Cross Double = Elément croisé (double)
Element Edge = Elément de bordure
Element Edge Double = Elément de bordure (double)
Element End = Elément de fin
Element End Double = Elément de fin (double)
Element Straight = Elément droit
Element Straight Double = Elément droit (double)
Element T = Elément en T
Element T Double = Elément en T (double)
Horizontal Cylinder = Cylindre horizontal
One Curved Edge Block = Bloc à un bord incurvé
Pyramid = Pyramide
Slope = Pente
Slope Edge =
Slope Inner Edge =
Slope Lying =
Slope Upside Down =
Slope Upside Down Edge =
Slope Upside Down Inner Edge =
Sphere = Sphère
Spike = Pointe
Stick = Bâton
Two Curved Edge Block = Bloc à deux bords incurvés
Brick = Brique
Cobble = Pierre taillée
Dirt = Terre
Leaves = Feuilles
Sandstone = Grès
Stone = Pierre
Tree = Arbre
Wooden = Bois
## Grinder Recipes
# $1: Name
%s Dust = Poudre de %s
Akalin =
Alatro =
Arol =
Brass = Laiton
Bronze = Bronze
Carbon Steel = Acier au carbone
Cast Iron = Fonte
Chromium = Chrome
Coal = Charbon
Copper = Cuivre
Gold = Or
Mithril = Mithril
Silver = Argent
Stainless Steel = Acier inoxydable
Talinite = Talanite
Tin = Etain
Wrought Iron = Fer
Zinc = Zinc
%.1f%%-Fissile Uranium = Uranium fissile (%.1f%%)
## Tools
RE Battery = Batterie RE
Water Can = Jerrycan d'eau
Lava Can = Jerrycan de lave
Chainsaw = Tronçonneuse
Flashlight = Lampe-torche
3 nodes deep. =
3 nodes tall. =
3 nodes wide. =
3x3 nodes. =
Use while sneaking to change Mining Drill Mk%d modes. =
Mining Drill Mk%d Mode %d = Foreuse Mk%d Mode %d
Mining Drill Mk%d = Foreuse Mk%d
Mining Laser Mk%d = Foreuse laser Mk%d
Single node. = Mode simple.
Sonic Screwdriver = Tournevis supersonique
Tree Tap = Robinet à sève
## Craft descriptions
Alloy cooking = Fonderie d'alliage
Grinding = Broyage
Compressing = Compression
Extracting = Extraction
Separating = Séparation
+165
View File
@@ -0,0 +1,165 @@
## Misc
[Technic] Loaded in %f seconds = [Technic] caricato in %f secondi
## Items
Silicon Wafer = Wafer di silicone
Doped Silicon Wafer = Wafer di silicone dopato
Enriched Uranium = Uranio arricchito
Uranium Fuel = Uranio Combustibile
Diamond Drill Head = Trivella diamantata
Blue Energy Crystal = Cristallo energetico blu
Green Energy Crystal = Cristallo energetico verde
Red Energy Crystal = Cristallo energetico rosso
Fine Copper Wire = Filo di rame fine
Copper Coil = Bobina di rame
Electric Motor = Motore elettrico
Low Voltage Transformer = Trasformatore in bassa tensione
Medium Voltage Transformer = Trasformatore in media tensione
High Voltage Transformer = Trasformatore in alta tensione
Control Logic Unit = Unità di controllo logica
Mixed Metal Ingot = Lingotto in lega ibrida
Composite Plate = Lastra composita
Copper Plate = Lastra di rame
Carbon Plate = Lastra in carbonio
Graphite = Lastra in graffite
Carbon Cloth = Fibra di carbonio
Raw Latex = Latex grezzo
Rubber Fiber = Fibra di gomma
%.1f%%-Fissile Uranium Ingot = %.1f%%-Lingotto di uranio fissile
%.1f%%-Fissile Uranium Block = %.1f%%-Blocco di uranio fissile
## Machine misc
Machine cannot be removed because it is not empty = La macchina non può essere rimossa perchè non è vuota
Inventory move disallowed due to protection = Impossibile muovere l'inventario a causa della protezione
# $1: Machine name (Includes tier)
@1 Active (@2 EU) = @1 Attivo (@2 EU)
%s Active = %s Attivo
%s Disabled = %s Disabilitato
%s Enabled = %s Abilitato
%s Idle = %s Inattivo
%s Improperly Placed = %s Piazzato impropiamente
%s Unpowered = %s Non alimentato
%s Out Of Fuel = %s senza carburante
%s Has Bad Cabling = %s ha un cablaggio scorretto
%s Has No Network = %s non è collegata
%s Finished = %s Finito
Enable/Disable = Abilita/Disabilita
Range = Raggio
Upgrade Slots = Alloggi di aggiornamento
In: = Ingresso:
Out: = Uscita:
Slot %d = Alloggio %d
Itemwise = Singolo elemento
Stackwise = pila completa
Owner: = Proprietario:
Unlocked = Non chiuso a chiave
Locked = Chiuso a chiave
Radius: = Raggio:
Enabled = Abilitato
Disabled = Disabilitato
## Machine names
# $1: Tier
%s Alloy Furnace = %s Fornace per leghe
%s Battery Box = %s Box batterie
%s Cable = Cavo %s
%s Compressor = Compressore %s
%s Extractor = Estrattore %s
%s Forcefield Emitter = Emettitore di campo di forza %s
%s Furnace = %s Fornace
%s Grinder = %s Tritatutto
%s Music Player = Music Player %s
%s Quarry = Cava %s
%s Tool Workshop = Officina per attrezzi %s
Arrayed Solar %s Generator = %s Pannello Solare
Fuel-Fired %s Generator = %s Generatore a carbone
Geothermal %s Generator = %s Generatore Geotermico
Hydro %s Generator = Turbina Elettrica %s
Nuclear %s Generator Core = Reattore nucleare %s
Small Solar %s Generator = %s Pannello solare
Wind %s Generator = %s Generatore eolico
Self-Contained Injector = Ignettore
Constructor Mk%d = Costruttore Mk%d
Frame = Cornice
Frame Motor = Cornice del motore
Template = Sagoma
Template (replacing) = Sagoma (di rimpiazzo)
Template Motor = Motore per sagome
Template Tool = Strumento per sagome
Battery Box = Box batterie
Supply Converter = Trasformatore
Switching Station = Stazione di controllo
Fuel-Fired Alloy Furnace = Fornace per leghe a carbone
Fuel-Fired Furnace = Fornace a carbone
Wind Mill Frame = Pala eolica
Forcefield = Campo di forza
Nuclear Reactor Rod Compartment = Compartimento combustibile nucleare
Administrative World Anchor = Ancora-mondo amministrativa
## Machine-specific
# $1: Pruduced EU
Charge = Carica
Discharge = Scarica
Power level = Livello di potenza
# $1: Tier $2: current_charge $3: max_charge
@1 Battery Box: @2/@3 = @1 Box Batterie: @2/@3
# $1: Machine name $2: Supply $3: Demand
@1. Supply: @2 Demand: @3 = @1. Prodotto: @2 Consumato: @3
Production at %d%% = Produzione a %d%%
Choose Milling Program: = Scegliere un programma di Fresatura
Slim Elements half / normal height: = Metà elementi sottili / altezza normale:
Current track %s = Traccia corrente %s
Stopped = Fermato
Keeping %d/%d map blocks loaded = Mantenimento di %d/%d blocchi mappa caricati
Digging not started = Scavo non iniziato
Digging finished = Scavo finito
Digging %d m above machine = Scavo di %d m sopra la macchina
Digging %d m below machine = Scavo di %d m sotto la macchina
## Grinder Recipes
# $1: Name
%s Dust = Polvere di %s
Akalin = Alcalino
Alatro = Alatro
Arol = Arol
Brass = Ottone
Bronze = Bronzo
Carbon Steel = Acciaio al Carbonio
Cast Iron = Ghisa
Chromium = Cromo
Coal = Carbone
Copper = Rame
Gold = Oro
Mithril = Mithril
Silver = Argento
Stainless Steel = Acciaio Inossidabile
Talinite = Talinite
Tin = Stagno
Wrought Iron = Ferro Battuto
Zinc = Zinco
%.1f%%-Fissile Uranium = %.1f%%-Uranio fissile
## Tools
RE Battery = Batteria RE
Water Can = Serbatoio d'acqua
Lava Can = Serbatoio di lava
Chainsaw = Motosega
Flashlight = Torcia
3 nodes deep. = 3 nodi in profondità.
3 nodes tall. = 3 nodi in altezza.
3 nodes wide. = 3 nodi in larghezza.
3x3 nodes. = 3x3 nodi.
Use while sneaking to change Mining Drill Mk%d modes. = Premi shift (freccia grossa) e usa per cambiare modalità nella trivella da miniera Mk%d.
Mining Drill Mk%d Mode %d = Trivella mk%d in modalità %d
Mining Drill Mk%d = Trivella da miniera mk%d
Mining Laser Mk%d = Laser da miniera mk%d
Single node. = Nodo singolo.
Sonic Screwdriver = Cacciavite sonico
Tree Tap = Batti albero
## Craft descriptions
Alloy cooking = Cottura lege
Grinding = Macinazione
Compressing = Compressione
Extracting = Estrazione
+175
View File
@@ -0,0 +1,175 @@
# Polish Translation for Technic mod
# Polskie tłumaczenie Technic mod
# by mat9117
## Misc
[Technic] Loaded in %f seconds = [Technic] Wczytany w %f sekund
## Items
Silicon Wafer = Płytka krzemowa
Doped Silicon Wafer = Domieszkowana płytka krzemowa
Enriched Uranium = Wzbogacony uran
Uranium Fuel = Paliwo uranowe
Diamond Drill Head = Diamentowa głowica wiertła
Blue Energy Crystal = Niebieski kryształ energii
Green Energy Crystal = Zielony kryształ energii
Red Energy Crystal = Czerwony kryształ energii
Fine Copper Wire = Cienki miedziany drut
Copper Coil = Miedziana cewka
Electric Motor = Silnik elektryczny
Low Voltage Transformer = Transformator niskiego napięcia
Medium Voltage Transformer = Transformator średniego napięcia
High Voltage Transformer = Transformator wysokiego napięcia
Control Logic Unit = Jednostka sterująca
Mixed Metal Ingot = Sztabka zmieszanych metali
Composite Plate = Płytka kompozytowa
Copper Plate = Płytka miedziana
Carbon Plate = Płytka węglowa
Graphite = Grafit
Carbon Cloth = Włókno węglowe
Raw Latex = Lateks naturalny
Rubber Fiber = Włókno gumowe
%.1f%%-Fissile Uranium Ingot = %.1f%% Sztabka uranu
%.1f%%-Fissile Uranium Block = %.1f%% Blok uranu
## Machine misc
Machine cannot be removed because it is not empty = Nie można usunąć maszyny, ponieważ nie jest pusta
Inventory move disallowed due to protection = Przenoszenie rzeczy z ekwipunku niemożliwe z powodu ochrony
# $1: Machine name (Includes tier)
@1 Active (@2 EU) = @1 Aktywny (@2 EU)
%s Active = %s Aktywny/a
%s Disabled = %s Wyłączony/a
%s Enabled = %s Włączony/a
%s Idle = %s Bezczynny/a
%s Improperly Placed = %s Ustawiony/a nieprawidłowo
%s is empty = %s jest pusty/a
%s Unpowered = %s brak zasilania
%s Out Of Fuel = %s brak paliwa
%s Has Bad Cabling = %s Źle podłączono kable
%s (Slave) =
%s Has No Network = %s Nie podłączony/a do sieci
%s Finished = %s Ukończony
Enable/Disable = Włącz/Wyłącz
Range = Zasięg
Upgrade Slots = Miejsca na ulepszenia
In: = Wejście
Out: = Wyjście
Slot %d = Otwór %d
Itemwise = Jeden przedmiot
Stackwise = Cały stack
Ignoring Mesecon Signal = Ignoruj sygnał Mesecon
Controlled by Mesecon Signal = Sterowany sygnałem Mesecon
Owner: = Właściciel:
Unlocked = Odblokowany/a
Locked = Zablokowany/a
Radius: = Promień:
Enabled = Włączony/a
Disabled = Wyłączony/a
## Machine names
# $1: Tier
%s Alloy Furnace = %s Piec stopowy
%s Battery Box = %s Skrzynka baterii
%s Cable = %s Przewód
%s Centrifuge = %s Centryfuga
%s Compressor = %s Kompresor
%s Extractor = %s Ekstraktor
%s Forcefield Emitter = %s Emiter pola siłowego
%s Furnace = %s Piec
%s Grinder = %s Młynek
%s Music Player = %s Odtwarzacz muzyki
%s Quarry = %s Kamieniołom
%s Tool Workshop = %s Warsztat narzędzi
Arrayed Solar %s Generator = %s Szeregowy generator słoneczny
Fuel-Fired %s Generator = %s Generator zasilany paliwem
Geothermal %s Generator = %s Generator geotermalny
Hydro %s Generator = %s Hydrogenerator
Nuclear %s Generator Core = %s Reaktor atomowy
Small Solar %s Generator = %s Mały generator słoneczny
Wind %s Generator = %s Generator wiatrowy
Self-Contained Injector = Samowystarczalny wtryskiwacz
Constructor Mk%d = Konstruktor Mk%d
Frame = Klatka
Frame Motor = Silnik klatkowy
Template = Szablon
Template (replacing) = Szablon (zastępczy)
Template Motor =Szablon silnika
Template Tool = Szablon narzędzia
Battery Box = Skrzynka baterii
Supply Converter = Konwerter zasilania
Switching Station = Rozdzielnia
Fuel-Fired Alloy Furnace = Piec stopowy zasilany paliwem
Fuel-Fired Furnace = Piec zasilany paliwem
Wind Mill Frame = Klatka wiatraka
Forcefield = Pole siłowe
Nuclear Reactor Rod Compartment = Komora rdzenia reaktora atomowego
Administrative World Anchor = Administracyjna kotwica świata
## Machine-specific
# $1: Pruduced EU
Charge = Ładuj
Discharge = Rozładuj
Power level = Poziom zasilania
# $1: Tier $2: current_charge $3: max_charge
@1 Battery Box: @2/@3 = @1 Skrzynka baterii: @2/@3
# $1: Machine name $2: Supply $3: Demand
@1. Supply: @2 Demand: @3 = @1. Zapas: @2 Pobór: @3
Production at %d%% = Produkowanie w %d%%
Choose Milling Program: = Wybierz program mielenia:
Slim Elements half / normal height: = Małe elementy połowa / normalna wysokość:
Current track %s =
Stopped = Zatrzymany/a
Keeping %d/%d map blocks loaded = Ciągle ładuję %d/%d bloki mapy
Digging not started = Nie rozpoczęto kopania
Digging finished = Kopanie skończone
Digging %d m above machine = Kopię %d m nad maszyną
Digging %d m below machine = Kopię %d m pod maszyną
@1 (@2 @3 -> @4 @5) = @1 (@2 @3 -> @4 @5)
## Grinder Recipes
# $1: Name
%s Dust = %s Pył
Akalin = Akalinowy
Alatro = Alatrowy
Arol = Arolowy
Brass = Mosiądzu
Bronze = Brązu
Carbon Steel = Stali węglowej
Cast Iron = Żeliwa
Chromium = Chromu
Coal = Węglowy
Copper = Miedzi
Gold = Złoty
Mithril = Mithrilu
Silver = Srebrny
Stainless Steel = Stali nierdzewnej
Talinite = Talinitu
Tin = Cyny
Wrought Iron = Kutego żelaza
Zinc = Cynku
%.1f%%-Fissile Uranium = %.1f%% Uranu
## Tools
RE Battery = Bateria ładowalna
Water Can = Kanister wody
Lava Can = Kanister lawy
Chainsaw = Piła łańcuchowa
Flashlight = Latarka
3 nodes deep. = Głęboki na 3 bloki.
3 nodes tall. = Wysoki na 3 bloki.
3 nodes wide. = Szeroki na 3 bloki.
3x3 nodes. = 3x3 bloki.
Use while sneaking to change Mining Drill Mk%d modes. = Użyj podczas skradania, aby zmienić tryby wiertła górniczego Mk%d
Mining Drill Mk%d Mode %d = Tryb wiertła górniczego Mk%d
Mining Drill Mk%d = Wiertło górnicze Mk%d
Mining Laser Mk%d = Laser górniczy Mk%d
Single node. = Pojedynczy blok.
Sonic Screwdriver = Dźwiękowy śrubokręt
Tree Tap = Nacinak drzewny
## Craft descriptions
Alloy cooking = Stapianie
Grinding = Mielenie
Compressing = Kompresowanie
Extracting = Ekstrakcja
Separating = Oddzielanie
+211
View File
@@ -0,0 +1,211 @@
# Braziliam portuguese translation for technic
# Tradução portuguesa brasileira para technic
# By Sires
## Misc
[Technic] Loaded in %f seconds = [Technic] Carregado em %f segundos
## Items
Silicon Wafer = Pastilha de Silício
Doped Silicon Wafer = Pastilha de Silício Dopada
Enriched Uranium = Urânio Enriquecido
Uranium Fuel = Combustivel de Urânio
Diamond Drill Head = Cabeça de Broca de Diamante
Blue Energy Crystal = Cristal de Energia Azul
Green Energy Crystal = Cristal de Energia Verde
Red Energy Crystal = Cristal de Energia Vermelho
Fine Copper Wire = Fio Fino de Cobre
Copper Coil = Bobina de Cobre
Electric Motor = Motor Elétrico
Low Voltage Transformer = Transformador de Baixa Voltagem
Medium Voltage Transformer = Transformador de Média Voltagem
High Voltage Transformer = Transformador de Alta Voltagem
Control Logic Unit = Unidade de Controle Lógico
Mixed Metal Ingot = Lingote de Metal Misturado
Composite Plate = Placa Composta
Copper Plate = Placa de Cobre
Carbon Plate = Placa de Carbono
Graphite = Grafite
Carbon Cloth = Recido de Carbono
Raw Latex = Latex bruto
Rubber Fiber = Fibra de Borracha
%.1f%%-Fissile Uranium Ingot = Lingote de Urânio %.1f%%-Físsil
%.1f%%-Fissile Uranium Block = Bloco de Urânio %.1f%%-Físsil
## Machine Misc
Machine cannot be removed because it is not empty = A máquina não pode ser removida porque ela não está vazia
Inventory move disallowed due to protection = Movimento de inventário não permitido pela proteção
# $1: Machine name (includes tier)
@1 Active (@2 EU) = @1 Ativo (@2 EU)
%s Active = %s Ativo
%s Disabled = %s Ativado
%s Enabled = %s Desativado
%s Idle = Ócio
%s Improperly Placed = %s Colocado Inapropriadamente
%s is empty = %s está vazio
%s Unpowered = %s Sem energia
%s Out Of Fuel = %s Sem Combustível
%s Has Bad Cabling = %s Tem Cabeamento Ruim
%s (Slave) = %s (Servo)
%s Has No Network = %s Não Tem Rede
%s Finished = %s Acabou
Enable/Disable = Ativar/Desativar
Range = Alcance
Upgrade Slots = Lugares para Melhoria
In: = Entrada:
Out: = Saída:
Slot %d = Lugar %d
Itemwise = Por item
Stackwise = Por pilha
Ignoring Mesecon Signal = Ignorar Sinaal de Mesecon
Controlled by Mesecon Signal = Controlado por Sinal de Mesecon
Owner: = Dono:
Unlocked = Destravado
Locked = Travado
Radius: = Raio:
Enabled = Ativado
Disabled = Desativado
## Machine names
# $1: Tier
%s Alloy Furnace = Fornalha de Liga %s
%s Battery Box = Caixa de Bateria %s
%s Cable = Cabo %s
%s CNC Machine = Máquina CNC %s
%s Centrifuge = Centrifuga %s
%s Compressor = Compresso %s
%s Extractor = Extrator %s
%s Forcefield Emitter = Emissor de Campo de Força %s
%s Furnace = Fornalha %s
%s Grinder = Triturador %s
%s Music Player = Tocador de Música %s
%s Quarry = Pedreira %s
%s Tool Workshop = Oficina de Ferramentas %s
Arrayed Solar %s Generator = Gerador Solar Equipado %s
Fuel-Fired %s Generator = Gerador Alimentado-por-Combustível %s
Geothermal %s Generator = Gerador Geotermal %s
Hydro %s Generator = Gerador Hidráulico %s
Nuclear %s Generator Core = Núcleo de Gerador Nuclear %s
Small Solar %s Generator = Gerador Solar Pequeno %s
Wind %s Generator = Gerador de Energia Eólica %s
Self-Contained Injector = Injetor Auto-Contido
Constructor Mk%d = Construtor Nv%d
Frame = Armação
Frame Motor = Motor de Armação
Template = Modelo
Template (replacing) = Modelo (recolocando)
Template Motor = Modelo de Motor
Template Tool = Modelo de Ferramenta
Battery Box = Caixa de Bateria
Supply Converter = Conversor de Energia
Switching Station = Estação de Comutação
Fuel-Fired Alloy Furnace = Fornalha de Liga Alimentada-por-Combustível
Fuel-Fired Furnace = Fornalha Alimentada-por-Combustível
Wind Mill Frame = Armação de Moinho de Vento
Forcefield = Campo de Força
Nuclear Reactor Rod Compartment = Compartimento de Barra do Reator Nuclear
Administrative World Anchor = Âncora de Mundo Administrativa
## Machine-specific
# $1: Pruduced EU
Charge = Carregar
Discharge = Descarregar
Power level = Nível de Energia
# $1: Tier $2: current_charge $3: max_charge
@1 Battery Box: @2/@3 = Caixa de Baterias @1: @2/@3
# $1: Machine name $2: Supply $3: Demand
@1. Supply: @2 Demand: @3 = @1. Suprimento: @2 Demanda: @3
Production at %d%% = Produção em %d%%
Choose Milling Program: = Escolha o Programa de Serragem:
Slim Elements half / normal height: = Metade de Elementos Finos / altura normal:
Current track %s = Música Atual %s
Stopped = Parado
Keeping %d/%d map blocks loaded = Mantendo %d/%d blocos de mapa carregados
Digging not started = Escavação não começada
Digging finished = Escavação terminada
Digging %d m above machine = Escavando %d m acima da máquina
Digging %d m below machine = Escavando %d m abaixo da máquina
@1 (@2 @3 -> @4 @5) = @1 (@2 @3 -> @4 @5)
## CNC
Cylinder = Cilindro
Element Cross = Cruz do Elemento
Element Cross Double = Cruz Dupla do Elemento
Element Edge = Borda do Elemento
Element Edge Double = Borda Dupla do Elemento
Element End = Final do Elemento
Element End Double = Final Duplo do Elemento
Element Straight = Elemento Contínuo
Element Straight Double = Elemento Contínuo duplo
Element T = Elemento em T
Element T Double = Elemento em T Duplo
Horizontal Cylinder = Cilindro Horizontal
One Curved Edge Block = Uma Borda de Bloco Curvada
Pyramid = Pirâmide
Slope = Ladeira
Slope Edge = Canto de Ladeira
Slope Inner Edge = Canto de Dentro de Ladeira
Slope Lying = Ladeira Deitada
Slope Upside Down = Ladeira de Cabeça para Baixo
Slope Upside Down Edge = Cande de Ladeira de Cabeça para Baixo
Slope Upside Down Inner Edge = Canto de Dentro de Ladeira de Cabeça para Baixo
Sphere = Esfera
Spike = Espinho
Stick = Graveto
Two Curved Edge Block = Bloco de Duas Bordas Curvadas
Brick = Tijolo
Cobble = Pedregulho
Dirt = Terra
Leaves = Folhas
Sandstone = Arenito
Stone = Pedra
Tree = Árvore
Wooden = de Madeira
## Grinder Recipes
# $1: Name
%s Dust = Pó de %s
Akalin = Akalin
Alatro = Alatro
Arol = Arol
Brass = Latão
Bronze = Bronze
Carbon Steel = Aço Carbono
Cast Iron = Ferro Fundido
Chromium = Crômio
Coal = Carvão
Copper = Cobre
Gold = Ouro
Mithril = Mithril
Silver = Prata
Stainless Steel = Aço Inoxidável
Talinite = Talinite
Tin = Estanho
Wrought Iron = Ferro Forjado
Zinc = Zinco
%.1f%%-Fissile Uranium = Urânio %.1f%%-Físsil
## Tools
RE Battery = Bateria RE
Water Can = Lata de Água
Lava Can = Lata de Lava
Chainsaw = Motosserra
Flashlight = Lanterna
3 nodes deep. = 3 nodes de profundidade.
3 nodes tall. = 3 nodes de altura.
3 nodes wide. = 3 nodes de largura.
3x3 nodes. = 3x3 nodes.
Use while sneaking to change Mining Drill Mk%d modes. = Use enquanto esgueirando para mudar os modos da Broca de Mineração Nv%d.
Mining Drill Mk%d Mode %d = Broca de Mineração Nv%d Modo %d
Mining Drill Mk%d = Broca de Mineração Nv%d
Mining Laser Mk%d = Laser de Mineração Nv%d
Single node. = Unico node.
Sonic Screwdriver = Chave de Fenda Sônica.
Tree Tap = Torneira de Árvore
## Craft descriptions
Alloy cooking = Cozinhando em liga
Grinding = Triturando
Compressing = Comprimindo
Extracting = Extraindo
Separating = Separando
@@ -0,0 +1,178 @@
# template.txt
# Template for translations of Technic
## Misc
[Technic] Loaded in %f seconds =
## Items
Silicon Wafer =
Doped Silicon Wafer =
Enriched Uranium =
Uranium Fuel =
Diamond Drill Head =
Blue Energy Crystal =
Green Energy Crystal =
Red Energy Crystal =
Fine Copper Wire =
Fine Gold Wire =
Fine Silver Wire =
Copper Coil =
Electric Motor =
Low Voltage Transformer =
Medium Voltage Transformer =
High Voltage Transformer =
Control Logic Unit =
Mixed Metal Ingot =
Composite Plate =
Copper Plate =
Carbon Plate =
Graphite =
Carbon Cloth =
Raw Latex =
Rubber Fiber =
%.1f%%-Fissile Uranium Ingot =
%.1f%%-Fissile Uranium Block =
## Machine misc
Machine cannot be removed because it is not empty =
Inventory move disallowed due to protection =
# $1: Machine name (Includes tier)
@1 Active (@2 EU) =
%s Active =
%s Disabled =
%s Enabled =
%s Idle =
%s Improperly Placed =
%s is empty =
%s Unpowered =
%s Out Of Fuel =
%s Has Bad Cabling =
%s (Slave) =
%s Has No Network =
%s Finished =
Enable/Disable =
Range =
Upgrade Slots =
In: =
Out: =
Slot %d =
Itemwise =
Stackwise =
Ignoring Mesecon Signal =
Controlled by Mesecon Signal =
Owner: =
Unlocked =
Locked =
Radius: =
Enabled =
Disabled =
## Machine names
# $1: Tier
%s Alloy Furnace =
%s Battery Box =
%s Cable =
%s Centrifuge =
%s Compressor =
%s Extractor =
%s Forcefield Emitter =
%s Furnace =
%s Grinder =
%s Music Player =
%s Quarry =
%s Tool Workshop =
Arrayed Solar %s Generator =
Fuel-Fired %s Generator =
Geothermal %s Generator =
Hydro %s Generator =
Nuclear %s Generator Core =
Small Solar %s Generator =
Wind %s Generator =
Self-Contained Injector =
Constructor Mk%d =
Frame =
Frame Motor =
Template =
Template (replacing) =
Template Motor =
Template Tool =
Battery Box =
Supply Converter =
Switching Station =
Fuel-Fired Alloy Furnace =
Fuel-Fired Furnace =
Wind Mill Frame =
Forcefield =
Nuclear Reactor Rod Compartment =
Administrative World Anchor =
## Machine-specific
# $1: Pruduced EU
Charge =
Discharge =
Power level =
# $1: Tier $2: current_charge $3: max_charge
@1 Battery Box: @2/@3 =
# $1: Machine name $2: Supply $3: Demand
@1. Supply: @2 Demand: @3 =
Production at %d%% =
Choose Milling Program: =
Slim Elements half / normal height: =
Current track %s =
Stopped =
Keeping %d/%d map blocks loaded =
Digging not started =
Digging finished =
Digging %d m above machine =
Digging %d m below machine =
@1 (@2 @3 -> @4 @5) =
## Grinder Recipes
# $1: Name
%s Dust =
Akalin =
Alatro =
Arol =
Brass =
Bronze =
Carbon Steel =
Cast Iron =
Chromium =
Coal =
Copper =
Gold =
Mithril =
Silver =
Stainless Steel =
Talinite =
Tin =
Wrought Iron =
Zinc =
%.1f%%-Fissile Uranium =
## Tools
RE Battery =
Water Can =
Lava Can =
Chainsaw =
Flashlight =
3 nodes deep. =
3 nodes tall. =
3 nodes wide. =
3x3 nodes. =
Use while sneaking to change Mining Drill Mk%d modes. =
Mining Drill Mk%d Mode %d =
Mining Drill Mk%d =
Mining Laser Mk%d =
Single node. =
Sonic Screwdriver =
Tree Tap =
## Craft descriptions
Alloy cooking =
Grinding =
Compressing =
Extracting =
Separating =
+145
View File
@@ -0,0 +1,145 @@
# Author: wzy2006 <3450354617@qq.com>
# Template for translations of Technic
[Technic] Loaded in %f seconds =[Technic]在%f秒内加载完成
Silicon Wafer =硅晶片
Doped Silicon Wafer =硅芯片
Enriched Uranium =浓缩铀
Uranium Fuel =铀燃料
Diamond Drill Head =金刚石钻头
Blue Energy Crystal =蓝色能量水晶
Green Energy Crystal =绿色能源水晶
Red Energy Crystal =红色能量水晶
Fine Copper Wire =细铜线
Copper Coil =铜盘管
Electric Motor =电动马达
Low Voltage Transformer =低压变压器
Medium Voltage Transformer =中压变压器
High Voltage Transformer =高压变压器
Control Logic Unit =控制逻辑单元
Mixed Metal Ingot =混合金属锭
Composite Plate =复合材料板
Copper Plate =铜板
Carbon Plate =碳板
Graphite =石墨
Carbon Cloth =碳布
Raw Latex =生乳胶
Rubber Fiber =橡胶纤维
%.1f%%-Fissile Uranium Ingot = %.1f%%-裂变铀锭
%.1f%%-Fissile Uranium Block = %.1f%%-裂变铀块
Machine cannot be removed because it is not empty =机器不能被拆除,因为它不是空的
Inventory move disallowed due to protection =库存移动不允许由于保护
@1 Active (@2 EU) =@1动作(@2 EU)
%s Active =%s 活动
%s Disabled =%s 禁用
%s Enabled =%s 启用
%s Idle =%s 闲置
%s Improperly Placed =%s 放置不当
%s Unpowered =%s 无能量的
%s Out Of Fuel =%s 燃料耗尽
%s Has Bad Cabling =%s 布线不良
%s Has No Network =%s 未并入电网(也许需要"交换站")
%s Finished =%s 完成
Enable/Disable =启用/禁用
Range =范围
Upgrade Slots =升级插槽
In: =输入:
Out: =输出:
Slot %d =插槽 %d
Itemwise =逐项
Stackwise =逐堆
Owner: =拥有者:
Unlocked =解锁
Locked =锁着的
Radius: =半径:
Enabled =启用
Disabled =禁用
%s Alloy Furnace =%s 合金炉
%s Battery Box =% s电池盒
%s Cable =%s 电缆
%s Compressor =%s 压缩机
%s Extractor =%s 提取机
%s Forcefield Emitter =%s 力场发射器
%s Furnace =%s 熔炉
%s Grinder =%s 研磨机
%s Music Player =%s 音乐播放器
%s Quarry =% s采石机
%s Tool Workshop =% s工具车间
Arrayed Solar %s Generator =太阳能%s发电板
Fuel-Fired %s Generator =火力%s发电机
Geothermal %s Generator =地热%s发电机
Hydro %s Generator =水力%s 发电机
Nuclear %s Generator Core =核裂变%s发电机核心
Small Solar %s Generator =小型太阳能%s发电板
Wind %s Generator =风力%s发电机
Self-Contained Injector =独立式输入器
Constructor Mk%d =建造器%d
Frame =框架
Frame Motor =框架电机
Template =模板
Template (replacing) =模板(替换)
Template motor =电动机模板
Template tool =工具模板
Battery Box =电池盒
Supply Converter =电源转换器
Switching Station =交换站
Fuel-Fired Alloy Furnace =火力合金炉
Fuel-Fired Furnace =火力熔炉
Wind Mill Frame =风力机框架
Forcefield =力场
Nuclear Reactor Rod Compartment =核反应堆舱
Administrative World Anchor =管理员区块锚
Charge =Charge
Discharge =Discharge
Power level =功率级
@1 Battery Box: @2/@3 =@1电池盒:@2/@3
@1. Supply: @2 Demand: @3 =@1. 供应: @2 需要: @3
Production at %d%% =生产 %d%%
Choose Milling Program: =选择研磨程序:
Slim Elements half / normal height: =苗条的元素一半/正常高度:
Current track %s =电流跟踪%s
Stopped =停止
Keeping %d/%d map blocks loaded =保持%d/%d地图模块加载
Digging not started =没有开始挖掘
Digging finished =挖完
Digging %d m above machine =挖掘 %d m以上的机器
Digging %d m below machine =挖掘 %d m以下的机器
%s Dust = %s 粉
Akalin =Akalin
Alatro =Alatro
Arol =Arol
Brass =黄铜
Bronze =青铜
Carbon Steel =碳钢
Cast Iron =铸铁
Chromium =铬
Coal =煤炭
Copper =铜
Gold =黄金
Mithril =秘银
Silver =银
Stainless Steel =不锈钢
Talinite = 绿泥石
Tin =锡
Wrought Iron =锻铁
Zinc =锌
%.1f%%-Fissile Uranium =%.1f%%-裂变铀
RE Battery =RE 电池
Water Can =水罐
Lava Can =岩浆罐
Chainsaw =电锯
Flashlight =手电筒
3 nodes deep. =3方块深度。
3 nodes tall. =3方块高。
3 nodes wide. =3方块宽。
3x3 nodes. =3x3 方块.。
Use while sneaking to change Mining Drill Mk%d modes. = 潜行时使用可更改采矿钻机Mk%d模式
Mining Drill Mk%d Mode %d =采矿钻 MK%d模式%d
Mining Drill Mk%d =采矿钻 MK%d
Mining Laser Mk%d =采矿激光 MK %d
Single node. =单方块。
Sonic Screwdriver =声波螺丝刀
Tree Tap =树形水龙头
Alloy cooking =熔炼合金
Grinding =研磨中
Compressing =压缩
Extracting =提取
@@ -0,0 +1,22 @@
-- HV battery box
minetest.register_craft({
output = 'technic:hv_battery_box0',
recipe = {
{'technic:mv_battery_box0', 'technic:mv_battery_box0', 'technic:mv_battery_box0'},
{'technic:mv_battery_box0', 'technic:hv_transformer', 'technic:mv_battery_box0'},
{'', 'technic:hv_cable', ''},
}
})
technic.register_battery_box({
tier = "HV",
max_charge = 1000000,
charge_rate = 100000,
discharge_rate = 400000,
charge_step = 10000,
discharge_step = 40000,
upgrade = 1,
tube = 1,
})
@@ -0,0 +1,47 @@
minetest.register_craft({
output = 'technic:hv_cable 3',
recipe = {
{'homedecor:plastic_sheeting', 'homedecor:plastic_sheeting', 'homedecor:plastic_sheeting'},
{'technic:mv_cable', 'technic:mv_cable', 'technic:mv_cable'},
{'homedecor:plastic_sheeting', 'homedecor:plastic_sheeting', 'homedecor:plastic_sheeting'},
}
})
technic.register_cable("HV", 3/16)
if minetest.get_modpath("digilines") then
local S = technic.getter
if minetest.get_modpath("digistuff") then
minetest.register_craft({
output = 'technic:hv_digi_cable 1',
type = "shapeless",
recipe = {'digistuff:digimese', 'technic:hv_cable'}
})
else
minetest.register_craft({
output = 'technic:hv_digi_cable 1',
recipe = {
{'digilines:wire_std_00000000', 'digilines:wire_std_00000000', 'digilines:wire_std_00000000'},
{'digilines:wire_std_00000000', 'technic:hv_cable', 'digilines:wire_std_00000000'},
{'digilines:wire_std_00000000', 'digilines:wire_std_00000000', 'digilines:wire_std_00000000'},
}
})
end
technic.register_cable("HV", 3/16, S("HV Cable (digiline)"), "_digi", {
digiline = {
wire = {
rules = {
{x = 1, y = 0, z = 0},
{x =-1, y = 0, z = 0},
{x = 0, y = 1, z = 0},
{x = 0, y =-1, z = 0},
{x = 0, y = 0, z = 1},
{x = 0, y = 0, z =-1}
}
}
}
})
end
@@ -0,0 +1,12 @@
-- HV compressor
minetest.register_craft({
output = 'technic:hv_compressor',
recipe = {
{'technic:carbon_plate', 'technic:mv_compressor', 'technic:composite_plate'},
{'pipeworks:tube_1', 'technic:hv_transformer', 'pipeworks:tube_1'},
{'technic:stainless_steel_ingot', 'technic:hv_cable', 'technic:stainless_steel_ingot'},
}
})
technic.register_compressor({tier = "HV", demand = {1500, 1000, 750}, speed = 5, upgrade = 1, tube = 1})
@@ -0,0 +1,18 @@
-- MV Electric Furnace
-- This is a faster version of the stone furnace which runs on EUs
-- In addition to this it can be upgraded with microcontrollers and batteries
-- This new version uses the batteries to lower the power consumption of the machine
-- Also in addition this furnace can be attached to the pipe system from the pipeworks mod.
-- FIXME: kpoppel I'd like to introduce an induction heating element here also
minetest.register_craft({
output = 'technic:hv_electric_furnace',
recipe = {
{'technic:stainless_steel_ingot', 'technic:mv_electric_furnace', 'technic:stainless_steel_ingot'},
{'pipeworks:tube_1', 'technic:hv_transformer', 'pipeworks:tube_1'},
{'technic:stainless_steel_ingot', 'technic:hv_cable', 'technic:stainless_steel_ingot'},
}
})
technic.register_electric_furnace({tier="HV", upgrade=1, tube=1, demand={4000, 2500, 1500}, speed=12})
@@ -0,0 +1,390 @@
--- Forcefield generator.
-- @author ShadowNinja
--
-- Forcefields are powerful barriers but they consume huge amounts of power.
-- The forcefield Generator is an HV machine.
-- How expensive is the generator?
-- Leaves room for upgrades lowering the power drain?
local digilines_path = minetest.get_modpath("digilines")
local forcefield_power_drain = 10
local S = technic.getter
local cable_entry = "^technic_cable_connection_overlay.png"
minetest.register_craft({
output = "technic:forcefield_emitter_off",
recipe = {
{"default:mese", "basic_materials:motor", "default:mese" },
{"technic:deployer_off", "technic:machine_casing", "technic:deployer_off"},
{"default:mese", "technic:hv_cable", "default:mese" },
}
})
local replaceable_cids = {}
minetest.after(0, function()
for name, ndef in pairs(minetest.registered_nodes) do
if ndef.buildable_to == true and name ~= "ignore" then
replaceable_cids[minetest.get_content_id(name)] = true
end
end
end)
-- Idea: Let forcefields have different colors by upgrade slot.
-- Idea: Let forcefields add up by detecting if one hits another.
-- ___ __
-- / \/ \
-- | |
-- \___/\___/
local function update_forcefield(pos, meta, active)
if active then
-- rate limit by chance
if math.floor(math.random()*4) ~= 0 then
return
end
end
local shape = meta:get_int("shape")
local range = meta:get_int("range")
local vm = VoxelManip()
local MinEdge, MaxEdge = vm:read_from_map(vector.subtract(pos, range),
vector.add(pos, range))
local area = VoxelArea:new({MinEdge = MinEdge, MaxEdge = MaxEdge})
local data = vm:get_data()
local c_air = minetest.get_content_id("air")
local c_field = minetest.get_content_id("technic:forcefield")
for z = -range, range do
for y = -range, range do
local vi = area:index(pos.x + (-range), pos.y + y, pos.z + z)
for x = -range, range do
local relevant
if shape == 0 then
local squared = x * x + y * y + z * z
relevant =
squared <= range * range + range and
squared >= (range - 1) * (range - 1) + (range - 1)
else
relevant =
x == -range or x == range or
y == -range or y == range or
z == -range or z == range
end
if relevant then
local cid = data[vi]
if active and replaceable_cids[cid] then
data[vi] = c_field
elseif not active and cid == c_field then
data[vi] = c_air
end
end
vi = vi + 1
end
end
end
vm:set_data(data)
vm:update_liquids()
vm:write_to_map()
end
local function set_forcefield_formspec(meta)
local formspec
if digilines_path then
formspec = "size[5,3.25]"..
"field[0.3,3;5,1;channel;Digiline Channel;"..meta:get_string("channel").."]"
else
formspec = "size[5,2.25]"
end
formspec = formspec..
"field[0.3,0.5;2,1;range;"..S("Range")..";"..meta:get_int("range").."]"
-- The names for these toggle buttons are explicit about which
-- state they'll switch to, so that multiple presses (arising
-- from the ambiguity between lag and a missed press) only make
-- the single change that the user expects.
if meta:get_int("shape") == 0 then
formspec = formspec.."button[3,0.2;2,1;shape1;"..S("Sphere").."]"
else
formspec = formspec.."button[3,0.2;2,1;shape0;"..S("Cube").."]"
end
if meta:get_int("mesecon_mode") == 0 then
formspec = formspec.."button[0,1;5,1;mesecon_mode_1;"..S("Ignoring Mesecon Signal").."]"
else
formspec = formspec.."button[0,1;5,1;mesecon_mode_0;"..S("Controlled by Mesecon Signal").."]"
end
if meta:get_int("enabled") == 0 then
formspec = formspec..
"button[0,1.75;5,1;enable;"..S("%s Disabled"):format(S("%s Forcefield Emitter"):format("HV")).."]"
else
formspec = formspec..
"button[0,1.75;5,1;disable;"..S("%s Enabled"):format(S("%s Forcefield Emitter"):format("HV")).."]"
end
meta:set_string("formspec", formspec)
end
local forcefield_receive_fields = function(pos, formname, fields, sender)
local player_name = sender:get_player_name()
if minetest.is_protected(pos, player_name) then
minetest.chat_send_player(player_name, "You are not allowed to edit this!")
minetest.record_protection_violation(pos, player_name)
return
end
local meta = minetest.get_meta(pos)
local range = nil
if fields.range then
range = tonumber(fields.range) or 0
-- Smallest field is 5. Anything less is asking for trouble.
-- Largest is 20. It is a matter of pratical node handling.
-- At the maximim range updating the forcefield takes about 0.2s
range = math.max(range, 5)
range = math.min(range, 20)
if range == meta:get_int("range") then range = nil end
end
if fields.shape0 or fields.shape1 or range then
update_forcefield(pos, meta, false)
end
if range then meta:set_int("range", range) end
if fields.channel then meta:set_string("channel", fields.channel) end
if fields.shape0 then meta:set_int("shape", 0) end
if fields.shape1 then meta:set_int("shape", 1) end
if fields.enable then meta:set_int("enabled", 1) end
if fields.disable then meta:set_int("enabled", 0) end
if fields.mesecon_mode_0 then meta:set_int("mesecon_mode", 0) end
if fields.mesecon_mode_1 then meta:set_int("mesecon_mode", 1) end
set_forcefield_formspec(meta)
end
local mesecons = {
effector = {
action_on = function(pos, node)
minetest.get_meta(pos):set_int("mesecon_effect", 1)
end,
action_off = function(pos, node)
minetest.get_meta(pos):set_int("mesecon_effect", 0)
end
}
}
local digiline_def = {
receptor = {
rules = technic.digilines.rules,
action = function() end
},
effector = {
rules = technic.digilines.rules,
action = function(pos, node, channel, msg)
local meta = minetest.get_meta(pos)
if channel ~= meta:get_string("channel") then
return
end
local msgt = type(msg)
if msgt == "string" then
local smsg = msg:lower()
msg = {}
if smsg == "get" then
msg.command = "get"
elseif smsg == "off" then
msg.command = "off"
elseif smsg == "on" then
msg.command = "on"
elseif smsg == "toggle" then
msg.command = "toggle"
elseif smsg:sub(1, 5) == "range" then
msg.command = "range"
msg.value = tonumber(smsg:sub(7))
elseif smsg:sub(1, 5) == "shape" then
msg.command = "shape"
msg.value = smsg:sub(7):lower()
msg.value = tonumber(msg.value) or msg.value
end
elseif msgt ~= "table" then
return
end
if msg.command == "get" then
digilines.receptor_send(pos, technic.digilines.rules, channel, {
enabled = meta:get_int("enabled"),
range = meta:get_int("range"),
shape = meta:get_int("shape")
})
return
elseif msg.command == "off" then
meta:set_int("enabled", 0)
elseif msg.command == "on" then
meta:set_int("enabled", 1)
elseif msg.command == "toggle" then
local onn = meta:get_int("enabled")
onn = 1-onn -- Mirror onn with pivot 0.5, so switch between 1 and 0.
meta:set_int("enabled", onn)
elseif msg.command == "range" then
if type(msg.value) ~= "number" then
return
end
msg.value = math.max(msg.value, 5)
msg.value = math.min(msg.value, 20)
update_forcefield(pos, meta, false)
meta:set_int("range", msg.value)
elseif msg.command == "shape" then
local valuet = type(msg.value)
if valuet == "string" then
if msg.value == "sphere" then
msg.value = 0
elseif msg.value == "cube" then
msg.value = 1
end
elseif valuet ~= "number" then
return
end
if not msg.value then
return
end
update_forcefield(pos, meta, false)
meta:set_int("shape", msg.value)
else
return
end
set_forcefield_formspec(meta)
end
},
}
local function run(pos, node)
local meta = minetest.get_meta(pos)
local eu_input = meta:get_int("HV_EU_input")
local enabled = meta:get_int("enabled") ~= 0 and
(meta:get_int("mesecon_mode") == 0 or meta:get_int("mesecon_effect") ~= 0)
local machine_name = S("%s Forcefield Emitter"):format("HV")
local range = meta:get_int("range")
local power_requirement
if meta:get_int("shape") == 0 then
power_requirement = math.floor(4 * math.pi * range * range)
else
power_requirement = 24 * range * range
end
power_requirement = power_requirement * forcefield_power_drain
if not enabled then
if node.name == "technic:forcefield_emitter_on" then
update_forcefield(pos, meta, false)
technic.swap_node(pos, "technic:forcefield_emitter_off")
meta:set_string("infotext", S("%s Disabled"):format(machine_name))
end
meta:set_int("HV_EU_demand", 0)
return
end
meta:set_int("HV_EU_demand", power_requirement)
if eu_input < power_requirement then
meta:set_string("infotext", S("%s Unpowered"):format(machine_name))
if node.name == "technic:forcefield_emitter_on" then
update_forcefield(pos, meta, false)
technic.swap_node(pos, "technic:forcefield_emitter_off")
end
elseif eu_input >= power_requirement then
if node.name == "technic:forcefield_emitter_off" then
technic.swap_node(pos, "technic:forcefield_emitter_on")
meta:set_string("infotext", S("%s Active"):format(machine_name))
end
update_forcefield(pos, meta, true)
end
end
minetest.register_node("technic:forcefield_emitter_off", {
description = S("%s Forcefield Emitter"):format("HV"),
tiles = {
"technic_forcefield_emitter_off.png",
"technic_machine_bottom.png"..cable_entry,
"technic_forcefield_emitter_off.png",
"technic_forcefield_emitter_off.png",
"technic_forcefield_emitter_off.png",
"technic_forcefield_emitter_off.png"
},
groups = {cracky = 1, technic_machine = 1, technic_hv = 1},
on_receive_fields = forcefield_receive_fields,
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_int("HV_EU_input", 0)
meta:set_int("HV_EU_demand", 0)
meta:set_int("range", 10)
meta:set_int("enabled", 0)
meta:set_int("mesecon_mode", 0)
meta:set_int("mesecon_effect", 0)
if digilines_path then
meta:set_string("channel", "forcefield"..minetest.pos_to_string(pos))
end
meta:set_string("infotext", S("%s Forcefield Emitter"):format("HV"))
set_forcefield_formspec(meta)
end,
mesecons = mesecons,
digiline = digiline_def,
technic_run = run,
})
minetest.register_node("technic:forcefield_emitter_on", {
description = S("%s Forcefield Emitter"):format("HV"),
tiles = {
"technic_forcefield_emitter_on.png",
"technic_machine_bottom.png"..cable_entry,
"technic_forcefield_emitter_on.png",
"technic_forcefield_emitter_on.png",
"technic_forcefield_emitter_on.png",
"technic_forcefield_emitter_on.png"
},
groups = {cracky = 1, technic_machine = 1, technic_hv = 1,
not_in_creative_inventory=1},
drop = "technic:forcefield_emitter_off",
on_receive_fields = forcefield_receive_fields,
on_destruct = function(pos)
local meta = minetest.get_meta(pos)
update_forcefield(pos, meta, false)
end,
mesecons = mesecons,
digiline = digiline_def,
technic_run = run,
technic_on_disable = function (pos, node)
local meta = minetest.get_meta(pos)
update_forcefield(pos, meta, false)
technic.swap_node(pos, "technic:forcefield_emitter_off")
end,
on_blast = function(pos, intensity)
minetest.dig_node(pos)
return {"technic:forcefield_emitter_off"}
end,
})
minetest.register_node("technic:forcefield", {
description = S("%s Forcefield"):format("HV"),
sunlight_propagates = true,
drawtype = "glasslike",
groups = {not_in_creative_inventory=1},
paramtype = "light",
light_source = default.LIGHT_MAX,
diggable = false,
drop = '',
tiles = {{
name = "technic_forcefield_animated.png",
animation = {
type = "vertical_frames",
aspect_w = 16,
aspect_h = 16,
length = 1.0,
},
}},
on_blast = function(pos, intensity)
end,
})
if minetest.get_modpath("mesecons_mvps") then
mesecon.register_mvps_stopper("technic:forcefield")
end
technic.register_machine("HV", "technic:forcefield_emitter_on", technic.receiver)
technic.register_machine("HV", "technic:forcefield_emitter_off", technic.receiver)
@@ -0,0 +1,13 @@
minetest.register_alias("hv_generator", "technic:hv_generator")
minetest.register_craft({
output = 'technic:hv_generator',
recipe = {
{'technic:carbon_plate', 'technic:mv_generator', 'technic:composite_plate'},
{'pipeworks:tube_1', 'technic:hv_transformer', 'pipeworks:tube_1'},
{'technic:stainless_steel_ingot', 'technic:hv_cable', 'technic:stainless_steel_ingot'},
}
})
technic.register_generator({tier="HV", tube=1, supply=1200})
@@ -0,0 +1,13 @@
-- HV grinder
minetest.register_craft({
output = 'technic:hv_grinder',
recipe = {
{'technic:carbon_plate', 'technic:mv_grinder', 'technic:composite_plate'},
{'pipeworks:tube_1', 'technic:hv_transformer', 'pipeworks:tube_1'},
{'technic:stainless_steel_ingot', 'technic:hv_cable', 'technic:stainless_steel_ingot'},
}
})
technic.register_grinder({tier="HV", demand={1200, 900, 600}, speed=5, upgrade=1, tube=1})
@@ -0,0 +1,22 @@
technic.register_tier("HV", "High Voltage")
local path = technic.modpath.."/machines/HV"
-- Wiring stuff
dofile(path.."/cables.lua")
dofile(path.."/battery_box.lua")
-- Generators
dofile(path.."/solar_array.lua")
dofile(path.."/nuclear_reactor.lua")
dofile(path.."/generator.lua")
-- Machines
dofile(path.."/quarry.lua")
dofile(path.."/forcefield.lua")
dofile(path.."/electric_furnace.lua")
dofile(path.."/grinder.lua")
dofile(path.."/compressor.lua")
@@ -0,0 +1,514 @@
--[[
The enriched uranium rod driven EU generator.
A very large and advanced machine providing vast amounts of power.
Very efficient but also expensive to run as it needs uranium.
Provides 10000 HV EUs for one week (only counted when loaded).
The nuclear reactor core requires a casing of water and a protective
shield to work. This is checked now and then and if the casing is not
intact the reactor will melt down!
--]]
local burn_ticks = 7 * 24 * 60 * 60 -- Seconds
local power_supply = 100000 -- EUs
local fuel_type = "technic:uranium_fuel" -- The reactor burns this
local digiline_meltdown = technic.config:get_bool("enable_nuclear_reactor_digiline_selfdestruct")
local has_digilines = minetest.get_modpath("digilines")
local S = technic.getter
local reactor_desc = S("@1 Nuclear Reactor Core", S("HV"))
local cable_entry = "^technic_cable_connection_overlay.png"
-- FIXME: Recipe should make more sense like a rod recepticle, steam chamber, HV generator?
minetest.register_craft({
output = 'technic:hv_nuclear_reactor_core',
recipe = {
{'technic:carbon_plate', 'default:obsidian_glass', 'technic:carbon_plate'},
{'technic:composite_plate', 'technic:machine_casing', 'technic:composite_plate'},
{'technic:stainless_steel_ingot', 'technic:hv_cable', 'technic:stainless_steel_ingot'},
}
})
local function make_reactor_formspec(meta)
local f = "size[8,9]"..
"label[0,0;"..S("Nuclear Reactor Rod Compartment").."]"..
"list[context;src;2,1;3,2;]"..
"list[current_player;main;0,5;8,4;]"..
"listring[]"..
"button[5.5,1.5;2,1;start;Start]"..
"checkbox[5.5,2.5;autostart;automatic Start;"..meta:get_string("autostart").."]"
if not has_digilines then
return f
end
local digiline_enabled = meta:get_string("enable_digiline")
f = f.."checkbox[0.5,2.8;enable_digiline;Enable Digiline;"..digiline_enabled.."]"
if digiline_enabled ~= "true" then
return f
end
return f..
"button_exit[4.6,3.69;2,1;save;Save]"..
"field[1,4;4,1;remote_channel;Digiline Remote Channel;${remote_channel}]"
end
local SS_OFF = 0
local SS_DANGER = 1
local SS_CLEAR = 2
local reactor_siren = {}
local function siren_set_state(pos, state)
local hpos = minetest.hash_node_position(pos)
local siren = reactor_siren[hpos]
if not siren then
if state == SS_OFF then return end
siren = {state=SS_OFF}
reactor_siren[hpos] = siren
end
if state == SS_DANGER and siren.state ~= SS_DANGER then
if siren.handle then minetest.sound_stop(siren.handle) end
siren.handle = minetest.sound_play("technic_hv_nuclear_reactor_siren_danger_loop",
{pos=pos, gain=1.5, loop=true, max_hear_distance=48})
siren.state = SS_DANGER
elseif state == SS_CLEAR then
if siren.handle then minetest.sound_stop(siren.handle) end
local clear_handle = minetest.sound_play("technic_hv_nuclear_reactor_siren_clear",
{pos=pos, gain=1.5, loop=false, max_hear_distance=48})
siren.handle = clear_handle
siren.state = SS_CLEAR
minetest.after(10, function()
if siren.handle ~= clear_handle then return end
minetest.sound_stop(clear_handle)
if reactor_siren[hpos] == siren then
reactor_siren[hpos] = nil
end
end)
elseif state == SS_OFF and siren.state ~= SS_OFF then
if siren.handle then minetest.sound_stop(siren.handle) end
reactor_siren[hpos] = nil
end
end
local function siren_danger(pos, meta)
meta:set_int("siren", 1)
siren_set_state(pos, SS_DANGER)
end
local function siren_clear(pos, meta)
if meta:get_int("siren") ~= 0 then
siren_set_state(pos, SS_CLEAR)
meta:set_int("siren", 0)
end
end
--[[
The standard reactor structure consists of a 9x9x9 cube. A cross
section through the middle:
CCCC CCCC
CBBB BBBC
CBLL LLBC
CBLWWWLBC
CBLW#WLBC
CBLW|WLBC
CBLL|LLBC
CBBB|BBBC
CCCC|CCCC
C = Concrete, B = Blast-resistant concrete, L = Lead,
W = water node, # = reactor core, | = HV cable
The man-hole is optional (but necessary for refueling).
For the reactor to operate and not melt down, it insists on the inner
7x7x7 portion (from the core out to the blast-resistant concrete)
being intact. Intactness only depends on the number of nodes of the
right type in each layer. The water layer must have water in all but
at most one node; the steel and blast-resistant concrete layers must
have the right material in all but at most two nodes. The permitted
gaps are meant for the cable and man-hole, but can actually be anywhere
and contain anything. For the reactor to be useful, a cable must
connect to the core, but it can go in any direction.
The outer concrete layer of the standard structure is not required
for the reactor to operate. It is noted here because it used to
be mandatory, and for historical reasons (that it predates the
implementation of radiation) it needs to continue being adequate
shielding of legacy reactors. If it ever ceases to be adequate
shielding for new reactors, legacy ones should be grandfathered.
For legacy reasons, if the reactor has a stainless steel layer instead
of a lead layer it will be converted to a lead layer.
--]]
local function reactor_structure_badness(pos)
local vm = VoxelManip()
local pos1 = vector.subtract(pos, 3)
local pos2 = vector.add(pos, 3)
local MinEdge, MaxEdge = vm:read_from_map(pos1, pos2)
local data = vm:get_data()
local area = VoxelArea:new({MinEdge=MinEdge, MaxEdge=MaxEdge})
local c_blast_concrete = minetest.get_content_id("technic:blast_resistant_concrete")
local c_lead = minetest.get_content_id("technic:lead_block")
local c_steel = minetest.get_content_id("technic:stainless_steel_block")
local c_water_source = minetest.get_content_id("default:water_source")
local c_water_flowing = minetest.get_content_id("default:water_flowing")
local blast_layer, steel_layer, lead_layer, water_layer = 0, 0, 0, 0
for z = pos1.z, pos2.z do
for y = pos1.y, pos2.y do
for x = pos1.x, pos2.x do
local cid = data[area:index(x, y, z)]
if x == pos1.x or x == pos2.x or
y == pos1.y or y == pos2.y or
z == pos1.z or z == pos2.z then
if cid == c_blast_concrete then
blast_layer = blast_layer + 1
end
elseif x == pos1.x+1 or x == pos2.x-1 or
y == pos1.y+1 or y == pos2.y-1 or
z == pos1.z+1 or z == pos2.z-1 then
if cid == c_lead then
lead_layer = lead_layer + 1
elseif cid == c_steel then
steel_layer = steel_layer + 1
end
elseif x == pos1.x+2 or x == pos2.x-2 or
y == pos1.y+2 or y == pos2.y-2 or
z == pos1.z+2 or z == pos2.z-2 then
if cid == c_water_source or cid == c_water_flowing then
water_layer = water_layer + 1
end
end
end
end
end
if steel_layer >= 96 then
for z = pos1.z+1, pos2.z-1 do
for y = pos1.y+1, pos2.y-1 do
for x = pos1.x+1, pos2.x-1 do
local vi = area:index(x, y, z)
if x == pos1.x+1 or x == pos2.x-1 or
y == pos1.y+1 or y == pos2.y-1 or
z == pos1.z+1 or z == pos2.z-1 then
if data[vi] == c_steel then
data[vi] = c_lead
end
end
end
end
end
vm:set_data(data)
vm:write_to_map()
lead_layer = steel_layer
end
if water_layer > 25 then water_layer = 25 end
if lead_layer > 96 then lead_layer = 96 end
if blast_layer > 216 then blast_layer = 216 end
return (25 - water_layer) + (96 - lead_layer) + (216 - blast_layer)
end
local function melt_down_reactor(pos)
minetest.log("action", "A reactor melted down at "..minetest.pos_to_string(pos))
minetest.set_node(pos, {name = "technic:corium_source"})
end
local function start_reactor(pos, meta)
if minetest.get_node(pos).name ~= "technic:hv_nuclear_reactor_core" then
return false
end
local inv = meta:get_inventory()
if inv:is_empty("src") then
return false
end
local src_list = inv:get_list("src")
local correct_fuel_count = 0
for _, src_stack in pairs(src_list) do
if src_stack and src_stack:get_name() == fuel_type then
correct_fuel_count = correct_fuel_count + 1
end
end
-- Check that the reactor is complete and has the correct fuel
if correct_fuel_count ~= 6 or reactor_structure_badness(pos) ~= 0 then
return false
end
meta:set_int("burn_time", 1)
technic.swap_node(pos, "technic:hv_nuclear_reactor_core_active")
meta:set_int("HV_EU_supply", power_supply)
for idx, src_stack in pairs(src_list) do
src_stack:take_item()
inv:set_stack("src", idx, src_stack)
end
return true
end
minetest.register_abm({
label = "Machines: reactor melt-down check",
nodenames = {"technic:hv_nuclear_reactor_core_active"},
interval = 4,
chance = 1,
action = function (pos, node)
local meta = minetest.get_meta(pos)
local badness = reactor_structure_badness(pos)
local accum_badness = meta:get_int("structure_accumulated_badness")
if badness == 0 then
if accum_badness ~= 0 then
meta:set_int("structure_accumulated_badness", math.max(accum_badness - 4, 0))
siren_clear(pos, meta)
end
else
siren_danger(pos, meta)
accum_badness = accum_badness + badness
if accum_badness >= 25 then
melt_down_reactor(pos)
else
meta:set_int("structure_accumulated_badness", accum_badness)
end
end
end,
})
local function run(pos, node)
local meta = minetest.get_meta(pos)
local burn_time = meta:get_int("burn_time") or 0
if burn_time >= burn_ticks or burn_time == 0 then
if has_digilines and meta:get_int("HV_EU_supply") == power_supply then
digilines.receptor_send(pos, technic.digilines.rules, meta:get_string("remote_channel"), {
command = "fuel_used",
pos = pos
})
end
if meta:get_string("autostart") == "true" then
if start_reactor(pos, meta) then
return
end
end
meta:set_int("HV_EU_supply", 0)
meta:set_int("burn_time", 0)
meta:set_string("infotext", S("%s Idle"):format(reactor_desc))
technic.swap_node(pos, "technic:hv_nuclear_reactor_core")
meta:set_int("structure_accumulated_badness", 0)
siren_clear(pos, meta)
elseif burn_time > 0 then
burn_time = burn_time + 1
meta:set_int("burn_time", burn_time)
local percent = math.floor(burn_time / burn_ticks * 100)
meta:set_string("infotext", reactor_desc.." ("..percent.."%)")
meta:set_int("HV_EU_supply", power_supply)
end
end
local nuclear_reactor_receive_fields = function(pos, formname, fields, sender)
local player_name = sender:get_player_name()
if minetest.is_protected(pos, player_name) then
minetest.chat_send_player(player_name, "You are not allowed to edit this!")
minetest.record_protection_violation(pos, player_name)
return
end
local meta = minetest.get_meta(pos)
local update_formspec = false
if fields.remote_channel then
meta:set_string("remote_channel", fields.remote_channel)
end
if fields.start then
local b = start_reactor(pos, meta)
if b then
minetest.chat_send_player(player_name, "Start successful")
else
minetest.chat_send_player(player_name, "Error")
end
end
if fields.autostart then
meta:set_string("autostart", fields.autostart)
update_formspec = true
end
if fields.enable_digiline then
meta:set_string("enable_digiline", fields.enable_digiline)
update_formspec = true
end
if update_formspec then
meta:set_string("formspec", make_reactor_formspec(meta))
end
end
local digiline_def = function(pos, _, channel, msg)
local meta = minetest.get_meta(pos)
if meta:get_string("enable_digiline") ~= "true" or
channel ~= meta:get_string("remote_channel") then
return
end
-- Convert string messages to tables:
local msgt = type(msg)
if msgt == "string" then
local smsg = msg:lower()
msg = {}
if smsg == "get" then
msg.command = "get"
elseif smsg:sub(1, 13) == "self_destruct" then
msg.command = "self_destruct"
msg.timer = tonumber(smsg:sub(15)) or 0
elseif smsg == "start" then
msg.command = "start"
end
elseif msgt ~= "table" then
return
end
if msg.command == "get" then
local inv = meta:get_inventory()
local invtable = {}
for i = 1, 6 do
local stack = inv:get_stack("src", i)
if stack:is_empty() then
invtable[i] = 0
elseif stack:get_name() == fuel_type then
invtable[i] = stack:get_count()
else
invtable[i] = -stack:get_count()
end
end
digilines.receptor_send(pos, technic.digilines.rules, channel, {
burn_time = meta:get_int("burn_time"),
enabled = meta:get_int("HV_EU_supply") == power_supply,
siren = meta:get_int("siren") == 1,
structure_accumulated_badness = meta:get_int("structure_accumulated_badness"),
rods = invtable
})
elseif digiline_meltdown and msg.command == "self_destruct" and
minetest.get_node(pos).name == "technic:hv_nuclear_reactor_core_active" then
if msg.timer ~= 0 and type(msg.timer) == "number" then
siren_danger(pos, meta)
minetest.after(msg.timer, melt_down_reactor, pos)
else
melt_down_reactor(pos)
end
elseif msg.command == "start" then
local b = start_reactor(pos, meta)
if b then
digilines.receptor_send(pos, technic.digilines.rules, channel, {
command = "start_success",
pos = pos
})
else
digilines.receptor_send(pos, technic.digilines.rules, channel, {
command = "start_error",
pos = pos
})
end
end
end
minetest.register_node("technic:hv_nuclear_reactor_core", {
description = reactor_desc,
tiles = {
"technic_hv_nuclear_reactor_core.png",
"technic_hv_nuclear_reactor_core.png"..cable_entry
},
drawtype = "mesh",
mesh = "technic_reactor.obj",
groups = {cracky = 1, technic_machine = 1, technic_hv = 1},
legacy_facedir_simple = true,
sounds = default.node_sound_wood_defaults(),
paramtype = "light",
paramtype2 = "facedir",
stack_max = 1,
on_receive_fields = nuclear_reactor_receive_fields,
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string("infotext", reactor_desc)
meta:set_string("formspec", make_reactor_formspec(meta))
if has_digilines then
meta:set_string("remote_channel",
"nuclear_reactor"..minetest.pos_to_string(pos))
end
local inv = meta:get_inventory()
inv:set_size("src", 6)
end,
-- digiline interface
digiline = {
receptor = {
rules = technic.digilines.rules,
action = function() end,
},
effector = {
rules = technic.digilines.rules,
action = digiline_def,
},
},
can_dig = technic.machine_can_dig,
on_destruct = function(pos) siren_set_state(pos, SS_OFF) end,
allow_metadata_inventory_put = technic.machine_inventory_put,
allow_metadata_inventory_take = technic.machine_inventory_take,
allow_metadata_inventory_move = technic.machine_inventory_move,
technic_run = run,
})
minetest.register_node("technic:hv_nuclear_reactor_core_active", {
tiles = {
"technic_hv_nuclear_reactor_core.png",
"technic_hv_nuclear_reactor_core.png"..cable_entry
},
drawtype = "mesh",
mesh = "technic_reactor.obj",
groups = {cracky = 1, technic_machine = 1, technic_hv = 1, radioactive = 4,
not_in_creative_inventory = 1},
legacy_facedir_simple = true,
sounds = default.node_sound_wood_defaults(),
drop = "technic:hv_nuclear_reactor_core",
light_source = 14,
paramtype = "light",
paramtype2 = "facedir",
on_receive_fields = nuclear_reactor_receive_fields,
-- digiline interface
digiline = {
receptor = {
rules = technic.digilines.rules,
action = function() end,
},
effector = {
rules = technic.digilines.rules,
action = digiline_def,
},
},
can_dig = technic.machine_can_dig,
after_dig_node = melt_down_reactor,
on_destruct = function(pos) siren_set_state(pos, SS_OFF) end,
allow_metadata_inventory_put = technic.machine_inventory_put,
allow_metadata_inventory_take = technic.machine_inventory_take,
allow_metadata_inventory_move = technic.machine_inventory_move,
technic_run = run,
technic_on_disable = function(pos, node)
local timer = minetest.get_node_timer(pos)
timer:start(1)
end,
on_timer = function(pos, node)
-- Connected back?
if technic.get_timeout("HV", pos) > 0 then return false end
local meta = minetest.get_meta(pos)
local burn_time = meta:get_int("burn_time") or 0
if burn_time >= burn_ticks or burn_time == 0 then
meta:set_int("HV_EU_supply", 0)
meta:set_int("burn_time", 0)
technic.swap_node(pos, "technic:hv_nuclear_reactor_core")
meta:set_int("structure_accumulated_badness", 0)
siren_clear(pos, meta)
return false
end
meta:set_int("burn_time", burn_time + 1)
return true
end,
})
technic.register_machine("HV", "technic:hv_nuclear_reactor_core", technic.producer)
technic.register_machine("HV", "technic:hv_nuclear_reactor_core_active", technic.producer)
@@ -0,0 +1,421 @@
local S = technic.getter
local has_digilines = minetest.get_modpath("digilines")
local quarry_dig_above_nodes = tonumber(technic.config:get("quarry_dig_above_nodes"))
local quarry_max_depth = tonumber(technic.config:get("quarry_max_depth"))
local quarry_time_limit = tonumber(technic.config:get("quarry_time_limit"))
local quarry_demand = 10000
local quarry_eject_dir = vector.new(0, 1, 0)
local machine_name = S("%s Quarry"):format("HV")
local quarry_formspec =
"size[8,9]"..
"item_image[7,0;1,1;technic:quarry]"..
"list[context;cache;0,1;4,3;]"..
"listring[context;cache]"..
"list[current_player;main;0,5;8,4;]"..
"listring[current_player;main]"..
"label[0,0;"..machine_name.."]"..
"button[6,0.9;2,1;restart;"..S("Restart").."]"
if has_digilines then
quarry_formspec = quarry_formspec
.. "field[4.3,3.4;4,1;channel;Channel;${channel}]"
end
-- hard-coded spiral dig pattern for up to 17x17 dig area
local quarry_dig_pattern = {
0,1,2,2,3,3,0,0,0,1,1,1,2,2,2,2,3,3,3,3,0,0,0,0,0,1,1,1,1,1,2,2,
2,2,2,2,3,3,3,3,3,3,0,0,0,0,0,0,0,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,
3,3,3,3,3,3,3,3,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,
2,2,2,2,3,3,3,3,3,3,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,
1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,0,
0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,
2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
}
local function is_player_allowed(pos, name)
local owner = minetest.get_meta(pos):get_string("owner")
if owner == "" or owner == name then
return true
end
if not minetest.is_protected(pos, name) then
return true
end
return false
end
local function reset_quarry(pos)
local meta = minetest.get_meta(pos)
local node = technic.get_or_load_node(pos) or minetest.get_node(pos)
meta:set_int("quarry_dir", node.param2)
meta:set_string("quarry_pos", minetest.pos_to_string(pos))
meta:set_string("dig_pos", "")
meta:set_int("dig_level", pos.y + quarry_dig_above_nodes)
local radius = meta:get_int("size")
local diameter = (radius*2)+1
meta:set_int("dig_steps", diameter*diameter)
meta:set_int("dig_index", 0)
meta:set_int("purge_on", 1)
meta:set_int("finished", 0)
meta:set_int("dug", 0)
end
local function set_quarry_status(pos)
local meta = minetest.get_meta(pos)
local formspec = quarry_formspec.."field[4.3,2.4;2,1;size;"..S("Radius:")..";"..meta:get_int("size").."]"
local status = S("Digging not started")
if meta:get_int("enabled") == 1 then
formspec = formspec.."button[4,0.9;2,1;disable;"..S("Enabled").."]"
if meta:get_int("purge_on") == 1 then
status = S("Purging cache")
meta:set_string("infotext", S("%s purging cache"):format(machine_name))
meta:set_int("HV_EU_demand", 0)
elseif meta:get_int("finished") == 1 then
status = S("Digging finished")
meta:set_string("infotext", S("%s Finished"):format(machine_name))
meta:set_int("HV_EU_demand", 0)
else
local rel_y = meta:get_int("dig_level") - pos.y
status = S("Digging %d m "..(rel_y > 0 and "above" or "below").." machine"):format(math.abs(rel_y))
if meta:get_int("HV_EU_input") >= quarry_demand then
meta:set_string("infotext", S("%s Active"):format(machine_name))
else
meta:set_string("infotext", S("%s Unpowered"):format(machine_name))
end
meta:set_int("HV_EU_demand", quarry_demand)
end
else
formspec = formspec.."button[4,0.9;2,1;enable;"..S("Disabled").."]"
meta:set_string("infotext", S("%s Disabled"):format(machine_name))
meta:set_int("HV_EU_demand", 0)
end
meta:set_string("formspec", formspec.."label[0,4.1;"..minetest.formspec_escape(status).."]")
end
local function quarry_receive_fields(pos, formname, fields, sender)
local player_name = sender:get_player_name()
if not is_player_allowed(pos, player_name) then
minetest.chat_send_player(player_name, "You are not allowed to edit this!")
minetest.record_protection_violation(pos, player_name)
return
end
local meta = minetest.get_meta(pos)
if fields.size and string.find(fields.size, "^[0-9]+$") then
local size = tonumber(fields.size)
if size >= 1 and size <= 8 and size ~= meta:get_int("size") then
meta:set_int("size", size)
reset_quarry(pos)
end
end
if fields.channel then
meta:set_string("channel", fields.channel)
end
if fields.enable then meta:set_int("enabled", 1) end
if fields.disable then meta:set_int("enabled", 0) end
if fields.restart then reset_quarry(pos) end
set_quarry_status(pos)
end
local function quarry_handle_purge(pos)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
local cache = inv:get_list("cache")
if not cache then
inv:set_size("cache", 12)
return
end
local i = 0
for _,stack in ipairs(cache) do
i = i + 1
if stack then
local item = stack:to_table()
if item then
technic.tube_inject_item(pos, pos, quarry_eject_dir, item)
stack:clear()
inv:set_stack("cache", i, stack)
break
end
end
end
if inv:is_empty("cache") then
meta:set_int("purge_on", 0)
end
end
local function can_dig_node(pos, node_name, owner, digger)
if node_name == "air" or node_name == "vacuum:vacuum" then
return false
end
local def = minetest.registered_nodes[node_name]
if not def or not def.diggable or (def.can_dig and not def.can_dig(pos, digger)) then
return false
end
if minetest.is_protected(pos, owner) then
return false
end
return true
end
local function find_ground(quarry_pos, quarry_dir, meta)
local dir = minetest.facedir_to_dir(quarry_dir % 4)
local radius = meta:get_int("size")
local dig_level = meta:get_int("dig_level")
local middle_pos = vector.add(quarry_pos, vector.multiply(dir, radius + 1))
local pos1 = vector.new(middle_pos.x - radius, dig_level, middle_pos.z - radius)
local pos2 = vector.new(middle_pos.x + radius, dig_level, middle_pos.z + radius)
local nodes = minetest.find_nodes_in_area(pos1, pos2, {"air", "vacuum:vacuum"})
if #nodes < meta:get_int("dig_steps") then
return vector.new(middle_pos.x, dig_level, middle_pos.z)
end
meta:set_int("dig_level", dig_level - 1)
return nil
end
local function get_dig_pos(quarry_pos, quarry_dir, dig_pos, dig_index, dig_steps, meta)
if dig_index > 0 and dig_index < dig_steps then
local facedir = (quarry_dir + quarry_dig_pattern[dig_index]) % 4
dig_pos = vector.add(dig_pos, minetest.facedir_to_dir(facedir))
elseif dig_index >= dig_steps then
local dig_level = meta:get_int("dig_level")
if (quarry_pos.y - dig_level) >= quarry_max_depth then
return nil, dig_index
end
local dir = minetest.facedir_to_dir(quarry_dir % 4)
dig_pos = vector.add(quarry_pos, vector.multiply(dir, meta:get_int("size") + 1))
dig_pos.y = dig_level - 1
meta:set_int("dig_level", dig_pos.y)
dig_index = 0
end
dig_index = dig_index + 1
return dig_pos, dig_index
end
local function execute_dig(pos, node, meta)
local dig_pos = minetest.string_to_pos(meta:get_string("dig_pos"))
local quarry_dir = meta:get_int("quarry_dir")
if not dig_pos then
-- quarry has not hit ground yet
dig_pos = find_ground(pos, quarry_dir, meta)
else
local owner = meta:get_string("owner")
local digger = pipeworks.create_fake_player({name = owner})
local dig_steps = meta:get_int("dig_steps")
local dig_index = meta:get_int("dig_index")
local t0 = minetest.get_us_time()
local us_used = 0
-- search for something to dig
while us_used < quarry_time_limit do
dig_pos, dig_index = get_dig_pos(pos, quarry_dir, dig_pos, dig_index, dig_steps, meta)
if not dig_pos then
-- finished digging
meta:set_int("finished", 1)
meta:set_int("purge_on", 1)
break
end
local dig_node = technic.get_or_load_node(dig_pos) or minetest.get_node(dig_pos)
if can_dig_node(dig_pos, dig_node.name, owner, digger) then
-- found something to dig, dig it and stop searching
minetest.remove_node(dig_pos)
local inv = meta:get_inventory()
local drops = minetest.get_node_drops(dig_node.name, "")
for _, dropped_item in ipairs(drops) do
local left = inv:add_item("cache", dropped_item)
while not left:is_empty() do
meta:set_int("purge_on", 1)
quarry_handle_purge(pos)
left = inv:add_item("cache", left)
end
end
local dug_nodes = meta:get_int("dug") + 1
meta:set_int("dug", dug_nodes)
if dug_nodes % 100 == 0 then
meta:set_int("purge_on", 1)
end
break
end
us_used = minetest.get_us_time() - t0
end
meta:set_int("dig_index", dig_index)
end
if dig_pos then
meta:set_string("dig_pos", minetest.pos_to_string(dig_pos))
end
end
local function quarry_run(pos, node)
local meta = minetest.get_meta(pos)
if minetest.pos_to_string(pos) ~= meta:get_string("quarry_pos") then
-- quarry has been moved since last dig
reset_quarry(pos)
elseif meta:get_int("purge_on") == 1 then
quarry_handle_purge(pos)
elseif meta:get_int("enabled") and meta:get_int("HV_EU_input") >= quarry_demand and meta:get_int("finished") == 0 then
execute_dig(pos, node, meta)
elseif not meta:get_inventory():is_empty("cache") then
meta:set_int("purge_on", 1)
end
set_quarry_status(pos)
end
local digiline_def = function(pos, _, channel, msg)
local meta = minetest.get_meta(pos)
if channel ~= meta:get_string("channel") then
return
end
-- Convert string messages to tables:
if type(msg) == "string" then
local smsg = msg:lower()
msg = {}
if smsg == "get" then
msg.command = "get"
elseif smsg:sub(1,7) == "radius " then
msg.command = "radius"
msg.value = smsg:sub(8,-1)
elseif smsg == "on" then
msg.command = "on"
elseif smsg == "off" then
msg.command = "off"
elseif smsg == "restart" then
msg.command = "restart"
end
end
if type(msg) ~= "table" then
return
end
if msg.command == "get" then
digilines.receptor_send(pos, technic.digilines.rules, channel, {
enabled = meta:get_int("enabled"),
radius = meta:get_int("size"),
finished = meta:get_int("finished"),
dug_nodes = meta:get_int("dug"),
dig_level = meta:get_int("dig_level") - pos.y
})
elseif msg.command == "radius" then
local size = tonumber(msg.value)
if not size or size < 1 or size > 8 or size == meta:get_int("size") then
return
end
meta:set_int("size", size)
reset_quarry(pos)
set_quarry_status(pos)
elseif msg.command == "on" then
meta:set_int("enabled", 1)
set_quarry_status(pos)
elseif msg.command == "off" then
meta:set_int("enabled", 0)
set_quarry_status(pos)
elseif msg.command == "restart" then
reset_quarry(pos)
set_quarry_status(pos)
end
end
minetest.register_node("technic:quarry", {
description = S("%s Quarry"):format("HV"),
tiles = {
"technic_carbon_steel_block.png^pipeworks_tube_connection_metallic.png",
"technic_carbon_steel_block.png^technic_cable_connection_overlay.png",
"technic_carbon_steel_block.png^technic_cable_connection_overlay.png",
"technic_carbon_steel_block.png^technic_cable_connection_overlay.png",
"technic_carbon_steel_block.png^default_tool_mesepick.png",
"technic_carbon_steel_block.png^technic_cable_connection_overlay.png"
},
paramtype2 = "facedir",
groups = {cracky=2, tubedevice=1, technic_machine=1, technic_hv=1},
connect_sides = {"bottom", "front", "left", "right"},
tube = {
connect_sides = {top = 1},
-- lower priority than other tubes, so that quarries will prefer any
-- other tube to another quarry, which could lead to server freezes
-- in certain quarry placements (2x2 for example would never eject)
priority = 10,
can_go = function(pos, node, velocity, stack)
-- always eject the same, even if items came in another way
-- this further mitigates loops and generally avoids random sideway movement
-- that can be expected in certain quarry placements
return { quarry_eject_dir }
end
},
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_int("size", 4)
meta:get_inventory():set_size("cache", 12)
reset_quarry(pos)
set_quarry_status(pos)
end,
on_rotate = function(pos, node, player, click, new_param2)
local meta = minetest.get_meta(pos)
if meta:get_int("enabled") == 1 then
return false
end
-- only allow rotation around y-axis
node.param2 = new_param2 % 4
minetest.swap_node(pos, node)
reset_quarry(pos)
set_quarry_status(pos)
return true
end,
after_place_node = function(pos, placer, itemstack)
minetest.get_meta(pos):set_string("owner", placer:get_player_name())
pipeworks.scan_for_tube_objects(pos)
end,
can_dig = function(pos,player)
return minetest.get_meta(pos):get_inventory():is_empty("cache")
end,
after_dig_node = pipeworks.scan_for_tube_objects,
on_receive_fields = quarry_receive_fields,
technic_run = quarry_run,
allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
return is_player_allowed(pos, player:get_player_name()) and count or 0
end,
allow_metadata_inventory_put = function(pos, listname, index, stack, player)
return is_player_allowed(pos, player:get_player_name()) and stack:get_count() or 0
end,
allow_metadata_inventory_take = function(pos, listname, index, stack, player)
return is_player_allowed(pos, player:get_player_name()) and stack:get_count() or 0
end,
mesecons = {
effector = {
action_on = function(pos)
local meta = minetest.get_meta(pos)
meta:set_int("enabled", 1)
set_quarry_status(pos)
end,
action_off = function(pos)
local meta = minetest.get_meta(pos)
meta:set_int("enabled", 0)
set_quarry_status(pos)
end
}
},
digiline = {
receptor = {
rules = technic.digilines.rules,
action = function() end,
},
effector = {
rules = technic.digilines.rules,
action = digiline_def,
},
},
})
minetest.register_craft({
recipe = {
{"technic:carbon_plate", "pipeworks:filter", "technic:composite_plate"},
{"basic_materials:motor", "technic:machine_casing", "technic:diamond_drill_head"},
{"technic:carbon_steel_block", "technic:hv_cable", "technic:carbon_steel_block"}},
output = "technic:quarry"
})
technic.register_machine("HV", "technic:quarry", technic.receiver)
@@ -0,0 +1,14 @@
-- The high voltage solar array is an assembly of medium voltage arrays.
-- Solar arrays are not able to store large amounts of energy.
minetest.register_craft({
output = 'technic:solar_array_hv 1',
recipe = {
{'technic:solar_array_mv', 'technic:solar_array_mv', 'technic:solar_array_mv'},
{'technic:carbon_plate', 'technic:hv_transformer', 'technic:composite_plate'},
{'', 'technic:hv_cable', ''},
}
})
technic.register_solar_array({tier="HV", power=100})
@@ -0,0 +1,14 @@
-- LV Alloy furnace
-- FIXME: kpoppel: I'd like to introduce an induction heating element here...
minetest.register_craft({
output = 'technic:lv_alloy_furnace',
recipe = {
{'default:brick', 'default:brick', 'default:brick'},
{'default:brick', 'technic:machine_casing', 'default:brick'},
{'default:brick', 'technic:lv_cable', 'default:brick'},
}
})
technic.register_alloy_furnace({tier = "LV", speed = 1, demand = {300}})
@@ -0,0 +1,20 @@
-- LV Battery box
minetest.register_craft({
output = 'technic:lv_battery_box0',
recipe = {
{'group:wood', 'group:wood', 'group:wood'},
{'technic:battery', 'technic:machine_casing', 'technic:battery'},
{'technic:battery', 'technic:lv_cable', 'technic:battery'},
}
})
technic.register_battery_box({
tier = "LV",
max_charge = 40000,
charge_rate = 1000,
discharge_rate = 4000,
charge_step = 500,
discharge_step = 800,
})
@@ -0,0 +1,50 @@
minetest.register_alias("lv_cable", "technic:lv_cable")
minetest.register_craft({
output = 'technic:lv_cable 6',
recipe = {
{'default:paper', 'default:paper', 'default:paper'},
{'default:copper_ingot', 'default:copper_ingot', 'default:copper_ingot'},
{'default:paper', 'default:paper', 'default:paper'},
}
})
technic.register_cable("LV", 2/16)
if minetest.get_modpath("digilines") then
local S = technic.getter
if minetest.get_modpath("digistuff") then
minetest.register_craft({
output = 'technic:lv_digi_cable 1',
type = "shapeless",
recipe = {'digistuff:digimese', 'technic:lv_cable'}
})
else
minetest.register_craft({
output = 'technic:lv_digi_cable 1',
recipe = {
{'digilines:wire_std_00000000', 'digilines:wire_std_00000000', 'digilines:wire_std_00000000'},
{'digilines:wire_std_00000000', 'technic:lv_cable', 'digilines:wire_std_00000000'},
{'digilines:wire_std_00000000', 'digilines:wire_std_00000000', 'digilines:wire_std_00000000'},
}
})
end
technic.register_cable("LV", 2/16, S("LV Cable (digiline)"), "_digi", {
digiline = {
wire = {
rules = {
{x = 1, y = 0, z = 0},
{x =-1, y = 0, z = 0},
{x = 0, y = 1, z = 0},
{x = 0, y =-1, z = 0},
{x = 0, y = 0, z = 1},
{x = 0, y = 0, z =-1}
}
}
}
})
end
@@ -0,0 +1,17 @@
minetest.register_alias("compressor", "technic:lv_compressor")
minetest.register_craft({
output = 'technic:lv_compressor',
recipe = {
{'default:stone', 'basic_materials:motor', 'default:stone'},
{'mesecons:piston', 'technic:machine_casing', 'mesecons:piston'},
{'basic_materials:silver_wire', 'technic:lv_cable', 'basic_materials:silver_wire'},
},
replacements = {
{"basic_materials:silver_wire", "basic_materials:empty_spool"},
{"basic_materials:silver_wire", "basic_materials:empty_spool"}
},
})
technic.register_compressor({tier = "LV", demand = {300}, speed = 1})
@@ -0,0 +1,16 @@
-- LV Electric Furnace
-- This is a faster version of the stone furnace which runs on EUs
-- FIXME: kpoppel I'd like to introduce an induction heating element here also
minetest.register_craft({
output = 'technic:electric_furnace',
recipe = {
{'default:cobble', 'default:cobble', 'default:cobble'},
{'default:cobble', 'technic:machine_casing', 'default:cobble'},
{'default:cobble', 'technic:lv_cable', 'default:cobble'},
}
})
technic.register_electric_furnace({tier="LV", demand={300}, speed = 2})
@@ -0,0 +1,13 @@
minetest.register_alias("extractor", "technic:lv_extractor")
minetest.register_craft({
output = 'technic:lv_extractor',
recipe = {
{'technic:treetap', 'basic_materials:motor', 'technic:treetap'},
{'technic:treetap', 'technic:machine_casing', 'technic:treetap'},
{'', 'technic:lv_cable', ''},
}
})
technic.register_extractor({tier = "LV", demand = {300}, speed = 1})
@@ -0,0 +1,18 @@
-- The electric generator.
-- A simple device to get started on the electric machines.
-- Inefficient and expensive in fuel (200EU per tick)
-- Also only allows for LV machinery to run.
minetest.register_alias("lv_generator", "technic:lv_generator")
minetest.register_craft({
output = 'technic:lv_generator',
recipe = {
{'default:stone', 'default:furnace', 'default:stone'},
{'default:stone', 'technic:machine_casing', 'default:stone'},
{'default:stone', 'technic:lv_cable', 'default:stone'},
}
})
technic.register_generator({tier="LV", supply=200})
@@ -0,0 +1,117 @@
-- A geothermal EU generator
-- Using hot lava and water this device can create energy from steam
-- The machine is only producing LV EUs and can thus not drive more advanced equipment
-- The output is a little more than the coal burning generator (max 300EUs)
minetest.register_alias("geothermal", "technic:geothermal")
local S = technic.getter
minetest.register_craft({
output = 'technic:geothermal',
recipe = {
{'technic:granite', 'default:diamond', 'technic:granite'},
{'basic_materials:copper_wire', 'technic:machine_casing', 'basic_materials:copper_wire'},
{'technic:granite', 'technic:lv_cable', 'technic:granite'},
},
replacements = {
{"basic_materials:copper_wire", "basic_materials:empty_spool"},
{"basic_materials:copper_wire", "basic_materials:empty_spool"}
},
})
minetest.register_craftitem("technic:geothermal", {
description = S("Geothermal %s Generator"):format("LV"),
})
local check_node_around = function(pos)
local node = minetest.get_node(pos)
if node.name == "default:water_source" or node.name == "default:water_flowing" then return 1 end
if node.name == "default:lava_source" or node.name == "default:lava_flowing" then return 2 end
return 0
end
local run = function(pos, node)
local meta = minetest.get_meta(pos)
local water_nodes = 0
local lava_nodes = 0
local production_level = 0
local eu_supply = 0
-- Correct positioning is water on one side and lava on the other.
-- The two cannot be adjacent because the lava the turns into obsidian or rock.
-- To get to 100% production stack the water and lava one extra block down as well:
-- WGL (W=Water, L=Lava, G=the generator, |=an LV cable)
-- W|L
local positions = {
{x=pos.x+1, y=pos.y, z=pos.z},
{x=pos.x+1, y=pos.y-1, z=pos.z},
{x=pos.x-1, y=pos.y, z=pos.z},
{x=pos.x-1, y=pos.y-1, z=pos.z},
{x=pos.x, y=pos.y, z=pos.z+1},
{x=pos.x, y=pos.y-1, z=pos.z+1},
{x=pos.x, y=pos.y, z=pos.z-1},
{x=pos.x, y=pos.y-1, z=pos.z-1},
}
for _, p in pairs(positions) do
local check = check_node_around(p)
if check == 1 then water_nodes = water_nodes + 1 end
if check == 2 then lava_nodes = lava_nodes + 1 end
end
if water_nodes == 1 and lava_nodes == 1 then production_level = 25; eu_supply = 50 end
if water_nodes == 2 and lava_nodes == 1 then production_level = 50; eu_supply = 100 end
if water_nodes == 1 and lava_nodes == 2 then production_level = 75; eu_supply = 200 end
if water_nodes == 2 and lava_nodes == 2 then production_level = 100; eu_supply = 300 end
if production_level > 0 then
meta:set_int("LV_EU_supply", eu_supply)
end
meta:set_string("infotext",
S("Geothermal %s Generator"):format("LV").." ("..production_level.."%)")
if production_level > 0 and minetest.get_node(pos).name == "technic:geothermal" then
technic.swap_node (pos, "technic:geothermal_active")
return
end
if production_level == 0 then
technic.swap_node(pos, "technic:geothermal")
meta:set_int("LV_EU_supply", 0)
end
end
minetest.register_node("technic:geothermal", {
description = S("Geothermal %s Generator"):format("LV"),
tiles = {"technic_geothermal_top.png", "technic_machine_bottom.png", "technic_geothermal_side.png",
"technic_geothermal_side.png", "technic_geothermal_side.png", "technic_geothermal_side.png"},
groups = {snappy=2, choppy=2, oddly_breakable_by_hand=2,
technic_machine=1, technic_lv=1},
paramtype2 = "facedir",
legacy_facedir_simple = true,
sounds = default.node_sound_wood_defaults(),
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string("infotext", S("Geothermal %s Generator"):format("LV"))
meta:set_int("LV_EU_supply", 0)
end,
technic_run = run,
})
minetest.register_node("technic:geothermal_active", {
description = S("Geothermal %s Generator"):format("LV"),
tiles = {"technic_geothermal_top_active.png", "technic_machine_bottom.png", "technic_geothermal_side.png",
"technic_geothermal_side.png", "technic_geothermal_side.png", "technic_geothermal_side.png"},
paramtype2 = "facedir",
groups = {snappy=2, choppy=2, oddly_breakable_by_hand=2,
technic_machine=1, technic_lv=1, not_in_creative_inventory=1},
legacy_facedir_simple = true,
sounds = default.node_sound_wood_defaults(),
drop = "technic:geothermal",
technic_run = run,
})
technic.register_machine("LV", "technic:geothermal", technic.producer)
technic.register_machine("LV", "technic:geothermal_active", technic.producer)
@@ -0,0 +1,13 @@
minetest.register_alias("grinder", "technic:lv_grinder")
minetest.register_craft({
output = 'technic:lv_grinder',
recipe = {
{'default:desert_stone', 'default:diamond', 'default:desert_stone'},
{'default:desert_stone', 'technic:machine_casing', 'default:desert_stone'},
{'technic:granite', 'technic:lv_cable', 'technic:granite'},
}
})
technic.register_grinder({tier="LV", demand={200}, speed=1})
@@ -0,0 +1,26 @@
technic.register_tier("LV", "Low Voltage")
local path = technic.modpath.."/machines/LV"
-- Wiring stuff
dofile(path.."/cables.lua")
dofile(path.."/battery_box.lua")
-- Generators
dofile(path.."/solar_panel.lua")
dofile(path.."/solar_array.lua")
dofile(path.."/geothermal.lua")
dofile(path.."/water_mill.lua")
dofile(path.."/generator.lua")
-- Machines
dofile(path.."/alloy_furnace.lua")
dofile(path.."/electric_furnace.lua")
dofile(path.."/grinder.lua")
dofile(path.."/extractor.lua")
dofile(path.."/compressor.lua")
dofile(path.."/music_player.lua")
dofile(path.."/lamp.lua")
@@ -0,0 +1,246 @@
-- LV LED and LV LED Lamp
-- LED - a weak light source, intended primarily as a core component for LED lamps
-- LED Lamp - a powerful light source, illuminating a 7x7x3(H) volume below itself
-- with light bright as the sun.
local S = technic.getter
local illuminate = function(pos, mode)
for y=1,3,1 do
for x=-3,3,1 do
for z = -3,3,1 do
local loc = {x = pos.x - x, y = pos.y - y, z = pos.z - z}
if mode then
if minetest.get_node(loc).name == "air" then
minetest.swap_node(loc, {name = "technic:dummy_light_source"})
end
else
if minetest.get_node(loc).name == "technic:dummy_light_source" then
minetest.swap_node(loc, {name = "air"})
end
end
end
end
end
end
local led_on = function(pos, node)
local meta = minetest.get_meta(pos)
local eu_input = meta:get_int("LV_EU_input")
local machine_name = S("%s LED"):format("LV")
local machine_node = "technic:lv_led"
local demand = 5
if eu_input < demand then
technic.swap_node(pos, machine_node)
meta:set_string("infotext", S("%s Unpowered"):format(machine_name))
elseif eu_input >= demand then
technic.swap_node(pos, machine_node.."_active")
meta:set_string("infotext", S("%s Active"):format(machine_name))
end
meta:set_int("LV_EU_demand", demand)
end
local led_off = function(pos, node)
local meta = minetest.get_meta(pos)
local machine_name = S("%s LED"):format("LV")
local machine_node = "technic:lv_led"
technic.swap_node(pos, machine_node)
meta:set_string("infotext", S("%s Unpowered"):format(machine_name))
meta:set_int("LV_EU_demand", 0)
end
local lamp_on = function(pos, node)
local meta = minetest.get_meta(pos)
local eu_input = meta:get_int("LV_EU_input")
local machine_name = S("%s Lamp"):format("LV")
local machine_node = "technic:lv_lamp"
local demand = 50
if eu_input < demand then
technic.swap_node(pos, machine_node)
meta:set_string("infotext", S("%s Unpowered"):format(machine_name))
illuminate(pos, false)
elseif eu_input >= demand then
technic.swap_node(pos, machine_node.."_active")
meta:set_string("infotext", S("%s Active"):format(machine_name))
illuminate(pos, true)
end
meta:set_int("LV_EU_demand", demand)
end
local lamp_off = function(pos, node)
local meta = minetest.get_meta(pos)
local machine_name = S("%s Lamp"):format("LV")
local machine_node = "technic:lv_lamp"
illuminate(pos, false)
technic.swap_node(pos, machine_node)
meta:set_string("infotext", S("%s Unpowered"):format(machine_name))
meta:set_int("LV_EU_demand", 0)
end
minetest.register_node("technic:dummy_light_source", {
description = S("Dummy light source node"),
node_box = {
type = "fixed",
fixed = {}
},
collision_box = {
type = "fixed",
fixed = {}
},
selection_box = {
type = "fixed",
fixed = {}
},
drawtype = "airlike",
buildable_to = true,
light_source = 14,
sunlight_propagates = true,
diggable = false,
walkable = false,
groups = { not_in_creative_inventory = 1 }
})
minetest.register_node("technic:lv_led", {
description = S("LV LED"),
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {0.2,0.2,0.2,-0.2,-0.2,-0.2}
},
collision_box = {
type = "fixed",
fixed = {0.2,0.2,0.2,-0.2,-0.2,-0.2}
},
selection_box = {
type = "fixed",
fixed = {0.2,0.2,0.2,-0.2,-0.2,-0.2}
},
tiles = {"technic_lv_led.png"},
inventory_image = "technic_lv_led_inv.png",
sunlight_propagates = true,
groups = {cracky=2, technic_machine=1, technic_lv=1},
connect_sides = {"front", "back", "left", "right", "top", "bottom"},
can_dig = technic.machine_can_dig,
technic_run = led_on,
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string("infotext", S("%s LED"):format("LV"))
end,
drop = "technic:lv_led"
})
minetest.register_node("technic:lv_led_active", {
description = S("LV LED Active"),
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {0.2,0.2,0.2,-0.2,-0.2,-0.2}
},
collision_box = {
type = "fixed",
fixed = {0.2,0.2,0.2,-0.2,-0.2,-0.2}
},
selection_box = {
type = "fixed",
fixed = {0.2,0.2,0.2,-0.2,-0.2,-0.2}
},
tiles = {"technic_lv_led.png"},
light_source = 9,
sunlight_propagates = true,
groups = {cracky=2, technic_machine=1, technic_lv=1, not_in_creative_inventory=1},
connect_sides = {"front", "back", "left", "right", "top", "bottom"},
can_dig = technic.machine_can_dig,
technic_run = led_on,
technic_on_disable = led_off,
drop = "technic:lv_led"
})
minetest.register_node("technic:lv_lamp", {
description = S("%s Lamp"):format("LV"),
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {0.5,0.5,0.5,-0.5,-0.2,-0.5}
},
collision_box = {
type = "fixed",
fixed = {0.5,0.5,0.5,-0.5,-0.2,-0.5}
},
selection_box = {
type = "fixed",
fixed = {0.5,0.5,0.5,-0.5,-0.2,-0.5}
},
tiles = {"technic_lv_lamp_top.png", "technic_lv_lamp_bottom.png", "technic_lv_lamp_side.png",
"technic_lv_lamp_side.png", "technic_lv_lamp_side.png", "technic_lv_lamp_side.png"},
groups = {cracky=2, technic_machine=1, technic_lv=1},
connect_sides = {"front", "back", "left", "right", "top",},
can_dig = technic.machine_can_dig,
technic_run = lamp_on,
on_destruct = lamp_off,
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string("infotext", S("%s Lamp"):format("LV"))
end,
})
minetest.register_node("technic:lv_lamp_active", {
description = S("%s Lamp Active"):format("LV"),
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {0.5,0.5,0.5,-0.5,-0.2,-0.5}
},
collision_box = {
type = "fixed",
fixed = {0.5,0.5,0.5,-0.5,-0.2,-0.5}
},
selection_box = {
type = "fixed",
fixed = {0.5,0.5,0.5,-0.5,-0.2,-0.5}
},
tiles = {"technic_lv_lamp_top.png", "technic_lv_lamp_bottom.png", "technic_lv_lamp_side.png",
"technic_lv_lamp_side.png", "technic_lv_lamp_side.png", "technic_lv_lamp_side.png"},
groups = {cracky=2, technic_machine=1, technic_lv=1, not_in_creative_inventory=1},
connect_sides = {"front", "back", "left", "right", "top"},
light_source = 1,
can_dig = technic.machine_can_dig,
technic_run = lamp_on,
on_destruct = lamp_off,
technic_on_disable = lamp_off,
})
minetest.register_craft({
output = 'technic:lv_led 2',
recipe = {
{'', 'homedecor:plastic_sheeting', ''},
{'homedecor:plastic_sheeting', 'technic:doped_silicon_wafer', 'homedecor:plastic_sheeting'},
{'', 'technic:fine_silver_wire', ''},
},
})
minetest.register_craft({
output = 'technic:lv_lamp',
recipe = {
{'default:glass', 'default:glass', 'default:glass'},
{'technic:lv_led', 'technic:lv_led', 'technic:lv_led'},
{'mesecons_materials:glue', 'technic:lv_cable', 'mesecons_materials:glue'},
},
})
technic.register_machine("LV", "technic:lv_lamp", technic.receiver)
technic.register_machine("LV", "technic:lv_lamp_active", technic.receiver)
technic.register_machine("LV", "technic:lv_led", technic.receiver)
technic.register_machine("LV", "technic:lv_led_active", technic.receiver)
@@ -0,0 +1,131 @@
-- LV Music player.
-- The player can play music. But it is high ampage!
local S = technic.getter
minetest.register_alias("music_player", "technic:music_player")
minetest.register_craft({
output = 'technic:music_player',
recipe = {
{'technic:chromium_ingot', 'default:diamond', 'technic:chromium_ingot'},
{'default:diamond', 'technic:machine_casing', 'default:diamond'},
{'default:mossycobble', 'technic:lv_cable', 'default:mossycobble'},
}
})
local music_handles = {}
local function play_track(pos, track)
return minetest.sound_play("technic_track"..tostring(track),
{pos = pos, gain = 1.0, loop = true, max_hear_distance = 72,})
end
local run = function(pos, node)
local meta = minetest.get_meta(pos)
local eu_input = meta:get_int("LV_EU_input")
local machine_name = S("%s Music Player"):format("LV")
local demand = 150
local current_track = meta:get_int("current_track")
local pos_hash = minetest.hash_node_position(pos)
local music_handle = music_handles[pos_hash]
-- Setup meta data if it does not exist.
if not eu_input then
meta:set_int("LV_EU_demand", demand)
meta:set_int("LV_EU_input", 0)
return
end
if meta:get_int("active") == 0 then
meta:set_string("infotext", S("%s Idle"):format(machine_name))
meta:set_int("LV_EU_demand", 0)
return
end
if eu_input < demand then
meta:set_string("infotext", S("%s Unpowered"):format(machine_name))
if music_handle then
minetest.sound_stop(music_handle)
music_handle = nil
end
elseif eu_input >= demand then
meta:set_string("infotext", S("%s Active"):format(machine_name))
if not music_handle then
music_handle = play_track(pos, current_track)
end
end
music_handles[pos_hash] = music_handle
meta:set_int("LV_EU_demand", demand)
end
local function stop_player(pos, node)
local pos_hash = minetest.hash_node_position(pos)
local music_handle = music_handles[pos_hash]
if music_handle then
minetest.sound_stop(music_handle)
music_handles[pos_hash] = nil
end
end
local function set_display(meta)
meta:set_string("formspec",
"size[4,4.5]"..
"item_image[0,0;1,1;technic:music_player]"..
"label[1,0;"..S("%s Music Player"):format("LV").."]"..
"button[0,1;1,1;track1;1]"..
"button[1,1;1,1;track2;2]"..
"button[2,1;1,1;track3;3]"..
"button[0,2;1,1;track4;4]"..
"button[1,2;1,1;track5;5]"..
"button[2,2;1,1;track6;6]"..
"button[0,3;1,1;track7;7]"..
"button[1,3;1,1;track8;8]"..
"button[2,3;1,1;track9;9]"..
"button[3,1;1,1;stop;Stop]"..
"label[0,4;"..minetest.formspec_escape(
meta:get_int("active") == 0 and
S("Stopped") or
S("Current track %s"):format(meta:get_int("current_track"))).."]")
end
minetest.register_node("technic:music_player", {
description = S("%s Music Player"):format("LV"),
tiles = {"technic_music_player_top.png", "technic_machine_bottom.png", "technic_music_player_side.png",
"technic_music_player_side.png", "technic_music_player_side.png", "technic_music_player_side.png"},
groups = {snappy=2, choppy=2, oddly_breakable_by_hand=2,
technic_machine=1, technic_lv=1},
connect_sides = {"bottom"},
sounds = default.node_sound_wood_defaults(),
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string("infotext", S("%s Music Player"):format("LV"))
set_display(meta)
end,
on_receive_fields = function(pos, formanme, fields, sender)
local new_track = nil
if fields.stop then new_track = 0 end
if fields.track1 then new_track = 1 end
if fields.track2 then new_track = 2 end
if fields.track3 then new_track = 3 end
if fields.track4 then new_track = 4 end
if fields.track5 then new_track = 5 end
if fields.track6 then new_track = 6 end
if fields.track7 then new_track = 7 end
if fields.track8 then new_track = 8 end
if fields.track9 then new_track = 9 end
if new_track then
stop_player(pos)
local meta = minetest.get_meta(pos)
meta:set_int("active", new_track == 0 and 0 or 1)
meta:set_int("current_track", new_track)
set_display(meta)
end
end,
on_destruct = stop_player,
technic_run = run,
technic_on_disable = stop_player,
})
technic.register_machine("LV", "technic:music_player", technic.receiver)
@@ -0,0 +1,18 @@
-- The solar array is an assembly of panels into a powerful array
-- The assembly can deliver more energy than the individual panel because
-- of the transformer unit which converts the panel output variations into
-- a stable supply.
-- Solar arrays are not able to store large amounts of energy.
-- The LV arrays are used to make medium voltage arrays.
minetest.register_craft({
output = 'technic:solar_array_lv 1',
recipe = {
{'technic:solar_panel', 'technic:solar_panel', 'technic:solar_panel'},
{'technic:carbon_steel_ingot', 'technic:lv_transformer', 'technic:carbon_steel_ingot'},
{'', 'technic:lv_cable', ''},
}
})
technic.register_solar_array({tier="LV", power=10})
@@ -0,0 +1,72 @@
-- Solar panels are the building blocks of LV solar arrays
-- They can however also be used separately but with reduced efficiency due to the missing transformer.
-- Individual panels are less efficient than when the panels are combined into full arrays.
local S = technic.getter
minetest.register_craft({
output = 'technic:solar_panel',
recipe = {
{'technic:doped_silicon_wafer', 'technic:doped_silicon_wafer', 'technic:doped_silicon_wafer'},
{'basic_materials:silver_wire', 'technic:lv_cable', 'mesecons_materials:glue'},
},
replacements = { {"basic_materials:silver_wire", "basic_materials:empty_spool"}, },
})
local run = function(pos, node)
-- The action here is to make the solar panel prodice power
-- Power is dependent on the light level and the height above ground
-- There are many ways to cheat by using other light sources like lamps.
-- As there is no way to determine if light is sunlight that is just a shame.
-- To take care of some of it solar panels do not work outside daylight hours or if
-- built below 0m
local pos1 = {x=pos.x, y=pos.y+1, z=pos.z}
local machine_name = S("Small Solar %s Generator"):format("LV")
local light = minetest.get_node_light(pos1, nil)
local time_of_day = minetest.get_timeofday()
local meta = minetest.get_meta(pos)
if light == nil then light = 0 end
-- turn on panel only during day time and if sufficient light
-- I know this is counter intuitive when cheating by using other light sources underground.
if light >= 12 and time_of_day >= 0.24 and time_of_day <= 0.76 and pos.y > -10 then
local charge_to_give = math.floor((light + pos1.y) * 3)
charge_to_give = math.max(charge_to_give, 0)
charge_to_give = math.min(charge_to_give, 200)
meta:set_string("infotext", S("@1 Active (@2)", machine_name,
technic.EU_string(charge_to_give)))
meta:set_int("LV_EU_supply", charge_to_give)
else
meta:set_string("infotext", S("%s Idle"):format(machine_name))
meta:set_int("LV_EU_supply", 0)
end
end
minetest.register_node("technic:solar_panel", {
tiles = {"technic_solar_panel_top.png", "technic_solar_panel_bottom.png", "technic_solar_panel_side.png",
"technic_solar_panel_side.png", "technic_solar_panel_side.png", "technic_solar_panel_side.png"},
groups = {snappy=2, choppy=2, oddly_breakable_by_hand=2,
technic_machine=1, technic_lv=1},
connect_sides = {"bottom"},
sounds = default.node_sound_wood_defaults(),
description = S("Small Solar %s Generator"):format("LV"),
active = false,
drawtype = "nodebox",
paramtype = "light",
is_ground_content = true,
node_box = {
type = "fixed",
fixed = {-0.5, -0.5, -0.5, 0.5, 0, 0.5},
},
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_int("LV_EU_supply", 0)
meta:set_string("infotext", S("Small Solar %s Generator"):format("LV"))
end,
technic_run = run,
})
technic.register_machine("LV", "technic:solar_panel", technic.producer)
@@ -0,0 +1,109 @@
-- A water mill produces LV EUs by exploiting flowing water across it
-- It is a LV EU supplier and fairly low yield (max 180EUs)
-- It is a little over half as good as the thermal generator.
local S = technic.getter
local cable_entry = "^technic_cable_connection_overlay.png"
minetest.register_alias("water_mill", "technic:water_mill")
minetest.register_craft({
output = 'technic:water_mill',
recipe = {
{'technic:marble', 'default:diamond', 'technic:marble'},
{'group:wood', 'technic:machine_casing', 'group:wood'},
{'technic:marble', 'technic:lv_cable', 'technic:marble'},
}
})
local function check_node_around_mill(pos)
local node = minetest.get_node(pos)
if node.name == "default:water_flowing"
or node.name == "default:river_water_flowing" then
return node.param2 -- returns approx. water flow, if any
end
return false
end
local run = function(pos, node)
local meta = minetest.get_meta(pos)
local water_flow = 0
local production_level
local eu_supply
local max_output = 4 * 45 -- keeping it around 180, little more than previous 150 :)
local positions = {
{x=pos.x+1, y=pos.y, z=pos.z},
{x=pos.x-1, y=pos.y, z=pos.z},
{x=pos.x, y=pos.y, z=pos.z+1},
{x=pos.x, y=pos.y, z=pos.z-1},
}
for _, p in pairs(positions) do
local check = check_node_around_mill(p)
if check then
water_flow = water_flow + check
end
end
eu_supply = math.min(4 * water_flow, max_output)
production_level = math.floor(100 * eu_supply / max_output)
meta:set_int("LV_EU_supply", eu_supply)
meta:set_string("infotext",
S("Hydro %s Generator"):format("LV").." ("..production_level.."%)")
if production_level > 0 and
minetest.get_node(pos).name == "technic:water_mill" then
technic.swap_node (pos, "technic:water_mill_active")
meta:set_int("LV_EU_supply", 0)
return
end
if production_level == 0 then
technic.swap_node(pos, "technic:water_mill")
end
end
minetest.register_node("technic:water_mill", {
description = S("Hydro %s Generator"):format("LV"),
tiles = {
"technic_water_mill_top.png",
"technic_machine_bottom.png"..cable_entry,
"technic_water_mill_side.png",
"technic_water_mill_side.png",
"technic_water_mill_side.png",
"technic_water_mill_side.png"
},
paramtype2 = "facedir",
groups = {snappy=2, choppy=2, oddly_breakable_by_hand=2,
technic_machine=1, technic_lv=1},
legacy_facedir_simple = true,
sounds = default.node_sound_wood_defaults(),
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string("infotext", S("Hydro %s Generator"):format("LV"))
meta:set_int("LV_EU_supply", 0)
end,
technic_run = run,
})
minetest.register_node("technic:water_mill_active", {
description = S("Hydro %s Generator"):format("LV"),
tiles = {"technic_water_mill_top_active.png", "technic_machine_bottom.png",
"technic_water_mill_side.png", "technic_water_mill_side.png",
"technic_water_mill_side.png", "technic_water_mill_side.png"},
paramtype2 = "facedir",
groups = {snappy=2, choppy=2, oddly_breakable_by_hand=2,
technic_machine=1, technic_lv=1, not_in_creative_inventory=1},
legacy_facedir_simple = true,
sounds = default.node_sound_wood_defaults(),
drop = "technic:water_mill",
technic_run = run,
technic_disabled_machine_name = "technic:water_mill",
})
technic.register_machine("LV", "technic:water_mill", technic.producer)
technic.register_machine("LV", "technic:water_mill_active", technic.producer)
@@ -0,0 +1,14 @@
-- MV alloy furnace
minetest.register_craft({
output = 'technic:mv_alloy_furnace',
recipe = {
{'technic:stainless_steel_ingot', 'technic:lv_alloy_furnace', 'technic:stainless_steel_ingot'},
{'pipeworks:tube_1', 'technic:mv_transformer', 'pipeworks:tube_1'},
{'technic:stainless_steel_ingot', 'technic:mv_cable', 'technic:stainless_steel_ingot'},
}
})
technic.register_alloy_furnace({tier = "MV", speed = 1.5, upgrade = 1, tube = 1, demand = {3000, 2000, 1000}})
@@ -0,0 +1,22 @@
-- MV Battery box
minetest.register_craft({
output = 'technic:mv_battery_box0',
recipe = {
{'technic:lv_battery_box0', 'technic:lv_battery_box0', 'technic:lv_battery_box0'},
{'technic:lv_battery_box0', 'technic:mv_transformer', 'technic:lv_battery_box0'},
{'', 'technic:mv_cable', ''},
}
})
technic.register_battery_box({
tier = "MV",
max_charge = 200000,
charge_rate = 20000,
discharge_rate = 80000,
charge_step = 2000,
discharge_step = 8000,
upgrade = 1,
tube = 1,
})
@@ -0,0 +1,50 @@
minetest.register_alias("mv_cable", "technic:mv_cable")
minetest.register_craft({
output = 'technic:mv_cable 3',
recipe ={
{'technic:rubber', 'technic:rubber', 'technic:rubber'},
{'technic:lv_cable', 'technic:lv_cable', 'technic:lv_cable'},
{'technic:rubber', 'technic:rubber', 'technic:rubber'},
}
})
technic.register_cable("MV", 2.5/16)
if minetest.get_modpath("digilines") then
local S = technic.getter
if minetest.get_modpath("digistuff") then
minetest.register_craft({
output = 'technic:mv_digi_cable 1',
type = "shapeless",
recipe = {'digistuff:digimese', 'technic:mv_cable'}
})
else
minetest.register_craft({
output = 'technic:mv_digi_cable 1',
recipe = {
{'digilines:wire_std_00000000', 'digilines:wire_std_00000000', 'digilines:wire_std_00000000'},
{'digilines:wire_std_00000000', 'technic:mv_cable', 'digilines:wire_std_00000000'},
{'digilines:wire_std_00000000', 'digilines:wire_std_00000000', 'digilines:wire_std_00000000'},
}
})
end
technic.register_cable("MV", 2.5/16, S("MV Cable (digiline)"), "_digi", {
digiline = {
wire = {
rules = {
{x = 1, y = 0, z = 0},
{x =-1, y = 0, z = 0},
{x = 0, y = 1, z = 0},
{x = 0, y =-1, z = 0},
{x = 0, y = 0, z = 1},
{x = 0, y = 0, z =-1}
}
}
}
})
end
@@ -0,0 +1,16 @@
minetest.register_craft({
output = "technic:mv_centrifuge",
recipe = {
{"basic_materials:motor", "technic:copper_plate", "technic:diamond_drill_head"},
{"technic:copper_plate", "technic:machine_casing", "technic:copper_plate" },
{"pipeworks:one_way_tube", "technic:mv_cable", "pipeworks:mese_filter" },
}
})
technic.register_centrifuge({
tier = "MV",
demand = { 8000, 7000, 6000 },
speed = 2,
upgrade = 1,
tube = 1,
})
@@ -0,0 +1,12 @@
-- MV compressor
minetest.register_craft({
output = 'technic:mv_compressor',
recipe = {
{'technic:stainless_steel_ingot', 'technic:lv_compressor', 'technic:stainless_steel_ingot'},
{'pipeworks:tube_1', 'technic:mv_transformer', 'pipeworks:tube_1'},
{'technic:stainless_steel_ingot', 'technic:mv_cable', 'technic:stainless_steel_ingot'},
}
})
technic.register_compressor({tier = "MV", demand = {800, 600, 400}, speed = 2, upgrade = 1, tube = 1})
@@ -0,0 +1,18 @@
-- MV Electric Furnace
-- This is a faster version of the stone furnace which runs on EUs
-- In addition to this it can be upgraded with microcontrollers and batteries
-- This new version uses the batteries to lower the power consumption of the machine
-- Also in addition this furnace can be attached to the pipe system from the pipeworks mod.
-- FIXME: kpoppel I'd like to introduce an induction heating element here also
minetest.register_craft({
output = 'technic:mv_electric_furnace',
recipe = {
{'technic:stainless_steel_ingot', 'technic:lv_electric_furnace', 'technic:stainless_steel_ingot'},
{'pipeworks:tube_1', 'technic:mv_transformer', 'pipeworks:tube_1'},
{'technic:stainless_steel_ingot', 'technic:mv_cable', 'technic:stainless_steel_ingot'},
}
})
technic.register_electric_furnace({tier="MV", upgrade=1, tube=1, demand={2000, 1000, 500}, speed=4})
@@ -0,0 +1,12 @@
-- MV extractor
minetest.register_craft({
output = 'technic:mv_extractor',
recipe = {
{'technic:stainless_steel_ingot', 'technic:lv_extractor', 'technic:stainless_steel_ingot'},
{'pipeworks:tube_1', 'technic:mv_transformer', 'pipeworks:tube_1'},
{'technic:stainless_steel_ingot', 'technic:mv_cable', 'technic:stainless_steel_ingot'},
}
})
technic.register_extractor({tier = "MV", demand = {800, 600, 400}, speed = 2, upgrade = 1, tube = 1})
@@ -0,0 +1,12 @@
-- MV freezer
minetest.register_craft({
output = 'technic:mv_freezer',
recipe = {
{'technic:stainless_steel_ingot', 'technic:motor', 'technic:stainless_steel_ingot'},
{'pipeworks:pipe_1_empty', 'technic:mv_transformer', 'pipeworks:pipe_1_empty'},
{'technic:stainless_steel_ingot', 'technic:mv_cable', 'technic:stainless_steel_ingot'},
}
})
technic.register_freezer({tier = "MV", demand = {800, 600, 400}, speed = 0.5, upgrade = 1, tube = 1})
@@ -0,0 +1,13 @@
minetest.register_alias("generator_mv", "technic:generator_mv")
minetest.register_craft({
output = 'technic:mv_generator',
recipe = {
{'technic:stainless_steel_ingot', 'technic:lv_generator', 'technic:stainless_steel_ingot'},
{'pipeworks:tube_1', 'technic:mv_transformer', 'pipeworks:tube_1'},
{'technic:stainless_steel_ingot', 'technic:mv_cable', 'technic:stainless_steel_ingot'},
}
})
technic.register_generator({tier="MV", tube=1, supply=600})
@@ -0,0 +1,13 @@
-- MV grinder
minetest.register_craft({
output = 'technic:mv_grinder',
recipe = {
{'technic:stainless_steel_ingot', 'technic:lv_grinder', 'technic:stainless_steel_ingot'},
{'pipeworks:tube_1', 'technic:mv_transformer', 'pipeworks:tube_1'},
{'technic:stainless_steel_ingot', 'technic:mv_cable', 'technic:stainless_steel_ingot'},
}
})
technic.register_grinder({tier="MV", demand={600, 450, 300}, speed=2, upgrade=1, tube=1})
@@ -0,0 +1,105 @@
-- A Hydro Turbine produces MV EUs by exploiting flowing water across it
-- It is a MV EU supplier and fairly high yield (max 1800EUs)
local S = technic.getter
local cable_entry = "^technic_cable_connection_overlay.png"
minetest.register_alias("hydro_turbine", "technic:hydro_turbine")
minetest.register_craft({
output = 'technic:hydro_turbine',
recipe = {
{'technic:stainless_steel_ingot', 'technic:water_mill', 'technic:stainless_steel_ingot'},
{'technic:water_mill', 'technic:mv_transformer', 'technic:water_mill'},
{'technic:stainless_steel_ingot', 'technic:mv_cable', 'technic:stainless_steel_ingot'},
}
})
local function get_water_flow(pos)
local node = minetest.get_node(pos)
if minetest.get_item_group(node.name, "water") == 3 then
return node.param2 -- returns approx. water flow, if any
end
return 0
end
---
-- 10 times better than LV hydro because of 2 extra water mills and 4 stainless steel, a transformer and whatnot ;P.
-- Man hydro turbines are tough and long lasting. So, give it some value :)
local run = function(pos, node)
local meta = minetest.get_meta(pos)
local water_flow = 0
local production_level
local eu_supply
local max_output = 40 * 45 -- Generates 1800EU/s
local positions = {
{x=pos.x+1, y=pos.y, z=pos.z},
{x=pos.x-1, y=pos.y, z=pos.z},
{x=pos.x, y=pos.y, z=pos.z+1},
{x=pos.x, y=pos.y, z=pos.z-1},
}
for _, p in pairs(positions) do
water_flow = water_flow + get_water_flow(p)
end
eu_supply = math.min(40 * water_flow, max_output)
production_level = math.floor(100 * eu_supply / max_output)
meta:set_int("MV_EU_supply", eu_supply)
meta:set_string("infotext",
S("Hydro %s Generator"):format("MV").." ("..production_level.."%)")
if production_level > 0 and
minetest.get_node(pos).name == "technic:hydro_turbine" then
technic.swap_node(pos, "technic:hydro_turbine_active")
meta:set_int("MV_EU_supply", 0)
return
end
if production_level == 0 then
technic.swap_node(pos, "technic:hydro_turbine")
end
end
minetest.register_node("technic:hydro_turbine", {
description = S("Hydro %s Generator"):format("MV"),
tiles = {
"technic_hydro_turbine_top.png",
"technic_machine_bottom.png"..cable_entry,
"technic_hydro_turbine_side.png",
"technic_hydro_turbine_side.png",
"technic_hydro_turbine_side.png",
"technic_hydro_turbine_side.png"
},
paramtype2 = "facedir",
groups = {snappy=2, choppy=2, oddly_breakable_by_hand=2,
technic_machine=1, technic_mv=1},
legacy_facedir_simple = true,
sounds = default.node_sound_wood_defaults(),
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string("infotext", S("Hydro %s Generator"):format("MV"))
meta:set_int("MV_EU_supply", 0)
end,
technic_run = run,
})
minetest.register_node("technic:hydro_turbine_active", {
description = S("Hydro %s Generator"):format("MV"),
tiles = {"technic_hydro_turbine_top_active.png", "technic_machine_bottom.png",
"technic_hydro_turbine_side.png", "technic_hydro_turbine_side.png",
"technic_hydro_turbine_side.png", "technic_hydro_turbine_side.png"},
paramtype2 = "facedir",
groups = {snappy=2, choppy=2, oddly_breakable_by_hand=2,
technic_machine=1, technic_mv=1, not_in_creative_inventory=1},
legacy_facedir_simple = true,
sounds = default.node_sound_wood_defaults(),
drop = "technic:hydro_turbine",
technic_run = run,
technic_disabled_machine_name = "technic:hydro_turbine",
})
technic.register_machine("MV", "technic:hydro_turbine", technic.producer)
technic.register_machine("MV", "technic:hydro_turbine_active", technic.producer)
@@ -0,0 +1,35 @@
technic.register_tier("MV", "Medium Voltage")
local path = technic.modpath.."/machines/MV"
-- Wiring stuff
dofile(path.."/cables.lua")
dofile(path.."/battery_box.lua")
-- Generators
if technic.config:get_bool("enable_wind_mill") then
dofile(path.."/wind_mill.lua")
end
dofile(path.."/generator.lua")
dofile(path.."/solar_array.lua")
dofile(path.."/hydro_turbine.lua")
-- Machines
dofile(path.."/alloy_furnace.lua")
dofile(path.."/electric_furnace.lua")
dofile(path.."/grinder.lua")
dofile(path.."/extractor.lua")
dofile(path.."/compressor.lua")
dofile(path.."/centrifuge.lua")
dofile(path.."/tool_workshop.lua")
dofile(path.."/freezer.lua")
-- The power radiator supplies appliances with inductive coupled power:
-- Lighting and associated textures is taken directly from VanessaE's homedecor and made electric.
-- This is currently useless, slow, and mostly copied
--dofile(path.."/power_radiator.lua")
--dofile(path.."/lighting.lua")
@@ -0,0 +1,575 @@
-- NOTE: The code is takes directly from VanessaE's homedecor mod.
-- I just made it the lights into indictive appliances for this mod.
-- This file supplies electric powered glowlights
-- Boilerplate to support localized strings if intllib mod is installed.
local S
if (minetest.get_modpath("intllib")) then
dofile(minetest.get_modpath("intllib").."/intllib.lua")
S = intllib.Getter(minetest.get_current_modname())
else
S = function (s) return s end
end
local function technic_homedecor_node_is_owned(pos, placer)
local ownername = false
if type(isprotect) == "function" then -- glomie's protection mod
if not isprotect(5, pos, placer) then
ownername = S("someone")
end
elseif type(protector) == "table" and type(protector.can_dig) == "function" then -- Zeg9's protection mod
if not protector.can_dig(5, pos, placer) then
ownername = S("someone")
end
end
if ownername ~= false then
minetest.chat_send_player(placer:get_player_name(), S("Sorry, %s owns that spot."):format(ownername) )
return true
else
return false
end
end
local dirs2 = {9, 18, 7, 12}
local technic_homedecor_rotate_and_place = function(itemstack, placer, pointed_thing)
if not technic_homedecor_node_is_owned(pointed_thing.under, placer)
and not technic_homedecor_node_is_owned(pointed_thing.above, placer) then
local node = minetest.get_node(pointed_thing.under)
if not minetest.registered_nodes[node.name] or not minetest.registered_nodes[node.name].on_rightclick then
local above = pointed_thing.above
local under = pointed_thing.under
local pitch = placer:get_look_pitch()
local pname = minetest.get_node(under).name
local fdir = minetest.dir_to_facedir(placer:get_look_dir())
local wield_name = itemstack:get_name()
if not minetest.registered_nodes[pname]
or not minetest.registered_nodes[pname].on_rightclick then
local iswall = (above.x ~= under.x) or (above.z ~= under.z)
local isceiling = (above.x == under.x) and (above.z == under.z) and (pitch > 0)
local pos1 = above
if minetest.registered_nodes[pname]["buildable_to"] then
pos1 = under
iswall = false
end
if not minetest.registered_nodes[minetest.get_node(pos1).name]["buildable_to"] then return end
if iswall then
minetest.add_node(pos1, {name = wield_name, param2 = dirs2[fdir+1] }) -- place wall variant
elseif isceiling then
minetest.add_node(pos1, {name = wield_name, param2 = 20 }) -- place upside down variant
else
minetest.add_node(pos1, {name = wield_name, param2 = 0 }) -- place right side up
end
if not homedecor_expect_infinite_stacks then
itemstack:take_item()
return itemstack
end
end
else
minetest.registered_nodes[node.name].on_rightclick(pointed_thing.under, node, placer, itemstack)
end
end
end
-- Yellow -- Half node
minetest.register_node('technic:homedecor_glowlight_half_yellow', {
description = S("Yellow Glowlight (thick)"),
drawtype = "nodebox",
tiles = {
'technic_homedecor_glowlight_yellow_tb.png',
'technic_homedecor_glowlight_yellow_tb.png',
'technic_homedecor_glowlight_thick_yellow_sides.png',
'technic_homedecor_glowlight_thick_yellow_sides.png',
'technic_homedecor_glowlight_thick_yellow_sides.png',
'technic_homedecor_glowlight_thick_yellow_sides.png'
},
selection_box = {
type = "fixed",
fixed = { -0.5, -0.5, -0.5, 0.5, 0, 0.5 }
},
node_box = {
type = "fixed",
fixed = { -0.5, -0.5, -0.5, 0.5, 0, 0.5 }
},
sunlight_propagates = false,
paramtype = "light",
paramtype2 = "facedir",
walkable = true,
sounds = default.node_sound_wood_defaults(),
groups = { snappy = 3 },
on_place = function(itemstack, placer, pointed_thing)
technic_homedecor_rotate_and_place(itemstack, placer, pointed_thing)
return itemstack
end,
on_construct = function(pos)
technic.inductive_on_construct(pos, 100, "Yellow Glowlight (thick)")
end,
on_punch = function(pos, node, puncher)
technic.inductive_on_punch_off(pos, 100, "technic:homedecor_glowlight_half_yellow_active")
end
})
minetest.register_node('technic:homedecor_glowlight_half_yellow_active', {
description = S("Yellow Glowlight (thick)"),
drawtype = "nodebox",
tiles = {
'technic_homedecor_glowlight_yellow_tb.png',
'technic_homedecor_glowlight_yellow_tb.png',
'technic_homedecor_glowlight_thick_yellow_sides.png',
'technic_homedecor_glowlight_thick_yellow_sides.png',
'technic_homedecor_glowlight_thick_yellow_sides.png',
'technic_homedecor_glowlight_thick_yellow_sides.png'
},
selection_box = {
type = "fixed",
fixed = { -0.5, -0.5, -0.5, 0.5, 0, 0.5 }
},
node_box = {
type = "fixed",
fixed = { -0.5, -0.5, -0.5, 0.5, 0, 0.5 }
},
sunlight_propagates = false,
paramtype = "light",
paramtype2 = "facedir",
walkable = true,
light_source = minetest.LIGHT_MAX,
sounds = default.node_sound_wood_defaults(),
groups = { snappy = 3, not_in_creative_inventory=1},
drop="technic:homedecor_glowlight_half_yellow",
on_place = function(itemstack, placer, pointed_thing)
technic_homedecor_rotate_and_place(itemstack, placer, pointed_thing)
return itemstack
end,
on_construct = function(pos)
technic.inductive_on_construct(pos, 100, "Yellow Glowlight (thick)")
end,
on_punch = function(pos, node, puncher)
technic.inductive_on_punch_on(pos, 0, "technic:homedecor_glowlight_half_yellow")
end
})
-- Yellow -- Quarter node
minetest.register_node('technic:homedecor_glowlight_quarter_yellow', {
description = S("Yellow Glowlight (thin)"),
drawtype = "nodebox",
tiles = {
'technic_homedecor_glowlight_yellow_tb.png',
'technic_homedecor_glowlight_yellow_tb.png',
'technic_homedecor_glowlight_thin_yellow_sides.png',
'technic_homedecor_glowlight_thin_yellow_sides.png',
'technic_homedecor_glowlight_thin_yellow_sides.png',
'technic_homedecor_glowlight_thin_yellow_sides.png'
},
selection_box = {
type = "fixed",
fixed = { -0.5, -0.5, -0.5, 0.5, -0.25, 0.5 }
},
node_box = {
type = "fixed",
fixed = { -0.5, -0.5, -0.5, 0.5, -0.25, 0.5 }
},
sunlight_propagates = false,
paramtype = "light",
paramtype2 = "facedir",
walkable = true,
sounds = default.node_sound_wood_defaults(),
groups = { snappy = 3 },
on_place = function(itemstack, placer, pointed_thing)
technic_homedecor_rotate_and_place(itemstack, placer, pointed_thing)
return itemstack
end,
on_construct = function(pos)
technic.inductive_on_construct(pos, 100, "Yellow Glowlight (thin)")
end,
on_punch = function(pos, node, puncher)
technic.inductive_on_punch_off(pos, 100, "technic:homedecor_glowlight_quarter_yellow_active")
end
})
minetest.register_node('technic:homedecor_glowlight_quarter_yellow_active', {
description = S("Yellow Glowlight (thin)"),
drawtype = "nodebox",
tiles = {
'technic_homedecor_glowlight_yellow_tb.png',
'technic_homedecor_glowlight_yellow_tb.png',
'technic_homedecor_glowlight_thin_yellow_sides.png',
'technic_homedecor_glowlight_thin_yellow_sides.png',
'technic_homedecor_glowlight_thin_yellow_sides.png',
'technic_homedecor_glowlight_thin_yellow_sides.png'
},
selection_box = {
type = "fixed",
fixed = { -0.5, -0.5, -0.5, 0.5, -0.25, 0.5 }
},
node_box = {
type = "fixed",
fixed = { -0.5, -0.5, -0.5, 0.5, -0.25, 0.5 }
},
sunlight_propagates = false,
paramtype = "light",
paramtype2 = "facedir",
walkable = true,
light_source = minetest.LIGHT_MAX-1,
sounds = default.node_sound_wood_defaults(),
groups = { snappy = 3, not_in_creative_inventory=1},
drop="technic:homedecor_glowlight_quarter_yellow",
on_place = function(itemstack, placer, pointed_thing)
technic_homedecor_rotate_and_place(itemstack, placer, pointed_thing)
return itemstack
end,
on_construct = function(pos)
technic.inductive_on_construct(pos, 100, "Yellow Glowlight (thin)")
end,
on_punch = function(pos, node, puncher)
technic.inductive_on_punch_on(pos, 0, "technic:homedecor_glowlight_quarter_yellow")
end
})
-- White -- half node
minetest.register_node('technic:homedecor_glowlight_half_white', {
description = S("White Glowlight (thick)"),
drawtype = "nodebox",
tiles = {
'technic_homedecor_glowlight_white_tb.png',
'technic_homedecor_glowlight_white_tb.png',
'technic_homedecor_glowlight_thick_white_sides.png',
'technic_homedecor_glowlight_thick_white_sides.png',
'technic_homedecor_glowlight_thick_white_sides.png',
'technic_homedecor_glowlight_thick_white_sides.png'
},
selection_box = {
type = "fixed",
fixed = { -0.5, -0.5, -0.5, 0.5, 0, 0.5 }
},
node_box = {
type = "fixed",
fixed = { -0.5, -0.5, -0.5, 0.5, 0, 0.5 }
},
sunlight_propagates = false,
paramtype = "light",
paramtype2 = "facedir",
walkable = true,
sounds = default.node_sound_wood_defaults(),
groups = { snappy = 3 },
on_place = function(itemstack, placer, pointed_thing)
technic_homedecor_rotate_and_place(itemstack, placer, pointed_thing)
return itemstack
end,
on_construct = function(pos)
technic.inductive_on_construct(pos, 100, "White Glowlight (thick)")
end,
on_punch = function(pos, node, puncher)
technic.inductive_on_punch_off(pos, 100, "technic:homedecor_glowlight_half_white_active")
end
})
minetest.register_node('technic:homedecor_glowlight_half_white_active', {
description = S("White Glowlight (thick)"),
drawtype = "nodebox",
tiles = {
'technic_homedecor_glowlight_white_tb.png',
'technic_homedecor_glowlight_white_tb.png',
'technic_homedecor_glowlight_thick_white_sides.png',
'technic_homedecor_glowlight_thick_white_sides.png',
'technic_homedecor_glowlight_thick_white_sides.png',
'technic_homedecor_glowlight_thick_white_sides.png'
},
selection_box = {
type = "fixed",
fixed = { -0.5, -0.5, -0.5, 0.5, 0, 0.5 }
},
node_box = {
type = "fixed",
fixed = { -0.5, -0.5, -0.5, 0.5, 0, 0.5 }
},
sunlight_propagates = false,
paramtype = "light",
paramtype2 = "facedir",
walkable = true,
light_source = minetest.LIGHT_MAX,
sounds = default.node_sound_wood_defaults(),
groups = { snappy = 3, not_in_creative_inventory=1},
drop="technic:homedecor_glowlight_half_white",
on_place = function(itemstack, placer, pointed_thing)
technic_homedecor_rotate_and_place(itemstack, placer, pointed_thing)
return itemstack
end,
on_construct = function(pos)
technic.inductive_on_construct(pos, 100, "White Glowlight (thick)")
end,
on_punch = function(pos, node, puncher)
technic.inductive_on_punch_on(pos, 0, "technic:homedecor_glowlight_half_white")
end
})
-- White -- Quarter node
minetest.register_node('technic:homedecor_glowlight_quarter_white', {
description = S("White Glowlight (thin)"),
drawtype = "nodebox",
tiles = {
'technic_homedecor_glowlight_white_tb.png',
'technic_homedecor_glowlight_white_tb.png',
'technic_homedecor_glowlight_thin_white_sides.png',
'technic_homedecor_glowlight_thin_white_sides.png',
'technic_homedecor_glowlight_thin_white_sides.png',
'technic_homedecor_glowlight_thin_white_sides.png'
},
selection_box = {
type = "fixed",
fixed = { -0.5, -0.5, -0.5, 0.5, -0.25, 0.5 }
},
node_box = {
type = "fixed",
fixed = { -0.5, -0.5, -0.5, 0.5, -0.25, 0.5 }
},
sunlight_propagates = false,
paramtype = "light",
paramtype2 = "facedir",
walkable = true,
sounds = default.node_sound_wood_defaults(),
groups = { snappy = 3 },
on_place = function(itemstack, placer, pointed_thing)
technic_homedecor_rotate_and_place(itemstack, placer, pointed_thing)
return itemstack
end,
on_construct = function(pos)
technic.inductive_on_construct(pos, 100, "White Glowlight (thin)")
end,
on_punch = function(pos, node, puncher)
technic.inductive_on_punch_off(pos, 100, "technic:homedecor_glowlight_quarter_white_active")
end
})
minetest.register_node('technic:homedecor_glowlight_quarter_white_active', {
description = S("White Glowlight (thin)"),
drawtype = "nodebox",
tiles = {
'technic_homedecor_glowlight_white_tb.png',
'technic_homedecor_glowlight_white_tb.png',
'technic_homedecor_glowlight_thin_white_sides.png',
'technic_homedecor_glowlight_thin_white_sides.png',
'technic_homedecor_glowlight_thin_white_sides.png',
'technic_homedecor_glowlight_thin_white_sides.png'
},
selection_box = {
type = "fixed",
fixed = { -0.5, -0.5, -0.5, 0.5, -0.25, 0.5 }
},
node_box = {
type = "fixed",
fixed = { -0.5, -0.5, -0.5, 0.5, -0.25, 0.5 }
},
sunlight_propagates = false,
paramtype = "light",
paramtype2 = "facedir",
walkable = true,
light_source = minetest.LIGHT_MAX-1,
sounds = default.node_sound_wood_defaults(),
groups = { snappy = 3, not_in_creative_inventory=1},
drop="technic:homedecor_glowlight_quarter_white",
on_place = function(itemstack, placer, pointed_thing)
technic_homedecor_rotate_and_place(itemstack, placer, pointed_thing)
return itemstack
end,
on_construct = function(pos)
technic.inductive_on_construct(pos, 100, "White Glowlight (thin)")
end,
on_punch = function(pos, node, puncher)
technic.inductive_on_punch_on(pos, 0, "technic:homedecor_glowlight_quarter_white")
end
})
-- Glowlight "cubes" - yellow
minetest.register_node('technic:homedecor_glowlight_small_cube_yellow', {
description = S("Yellow Glowlight (small cube)"),
drawtype = "nodebox",
tiles = {
'technic_homedecor_glowlight_cube_yellow_tb.png',
'technic_homedecor_glowlight_cube_yellow_tb.png',
'technic_homedecor_glowlight_cube_yellow_sides.png',
'technic_homedecor_glowlight_cube_yellow_sides.png',
'technic_homedecor_glowlight_cube_yellow_sides.png',
'technic_homedecor_glowlight_cube_yellow_sides.png'
},
selection_box = {
type = "fixed",
fixed = { -0.25, -0.5, -0.25, 0.25, 0, 0.25 }
},
node_box = {
type = "fixed",
fixed = { -0.25, -0.5, -0.25, 0.25, 0, 0.25 }
},
sunlight_propagates = false,
paramtype = "light",
paramtype2 = "facedir",
walkable = true,
sounds = default.node_sound_wood_defaults(),
groups = { snappy = 3 },
on_place = function(itemstack, placer, pointed_thing)
technic_homedecor_rotate_and_place(itemstack, placer, pointed_thing)
return itemstack
end,
on_construct = function(pos)
technic.inductive_on_construct(pos, 50, "Yellow Glowlight (small cube)")
end,
on_punch = function(pos, node, puncher)
technic.inductive_on_punch_off(pos, 50, "technic:homedecor_glowlight_small_cube_yellow_active")
end
})
minetest.register_node('technic:homedecor_glowlight_small_cube_yellow_active', {
description = S("Yellow Glowlight (small cube)"),
drawtype = "nodebox",
tiles = {
'technic_homedecor_glowlight_cube_yellow_tb.png',
'technic_homedecor_glowlight_cube_yellow_tb.png',
'technic_homedecor_glowlight_cube_yellow_sides.png',
'technic_homedecor_glowlight_cube_yellow_sides.png',
'technic_homedecor_glowlight_cube_yellow_sides.png',
'technic_homedecor_glowlight_cube_yellow_sides.png'
},
selection_box = {
type = "fixed",
fixed = { -0.25, -0.5, -0.25, 0.25, 0, 0.25 }
},
node_box = {
type = "fixed",
fixed = { -0.25, -0.5, -0.25, 0.25, 0, 0.25 }
},
sunlight_propagates = false,
paramtype = "light",
paramtype2 = "facedir",
walkable = true,
light_source = minetest.LIGHT_MAX-1,
sounds = default.node_sound_wood_defaults(),
groups = { snappy = 3, not_in_creative_inventory=1},
drop="technic:homedecor_glowlight_small_cube_yellow",
on_place = function(itemstack, placer, pointed_thing)
technic_homedecor_rotate_and_place(itemstack, placer, pointed_thing)
return itemstack
end,
on_construct = function(pos)
technic.inductive_on_construct(pos, 50, "Yellow Glowlight (small cube)")
end,
on_punch = function(pos, node, puncher)
technic.inductive_on_punch_on(pos, 0, "technic:homedecor_glowlight_small_cube_yellow")
end
})
-- Glowlight "cubes" - white
minetest.register_node('technic:homedecor_glowlight_small_cube_white', {
description = S("White Glowlight (small cube)"),
drawtype = "nodebox",
tiles = {
'technic_homedecor_glowlight_cube_white_tb.png',
'technic_homedecor_glowlight_cube_white_tb.png',
'technic_homedecor_glowlight_cube_white_sides.png',
'technic_homedecor_glowlight_cube_white_sides.png',
'technic_homedecor_glowlight_cube_white_sides.png',
'technic_homedecor_glowlight_cube_white_sides.png'
},
selection_box = {
type = "fixed",
fixed = { -0.25, -0.5, -0.25, 0.25, 0, 0.25 }
},
node_box = {
type = "fixed",
fixed = { -0.25, -0.5, -0.25, 0.25, 0, 0.25 }
},
sunlight_propagates = false,
paramtype = "light",
paramtype2 = "facedir",
walkable = true,
sounds = default.node_sound_wood_defaults(),
groups = { snappy = 3 },
on_place = function(itemstack, placer, pointed_thing)
technic_homedecor_rotate_and_place(itemstack, placer, pointed_thing)
return itemstack
end,
on_construct = function(pos)
technic.inductive_on_construct(pos, 50, "White Glowlight (small cube)")
end,
on_punch = function(pos, node, puncher)
technic.inductive_on_punch_off(pos, 50, "technic:homedecor_glowlight_small_cube_white_active")
end
})
minetest.register_node('technic:homedecor_glowlight_small_cube_white_active', {
description = S("White Glowlight (small cube)"),
drawtype = "nodebox",
tiles = {
'technic_homedecor_glowlight_cube_white_tb.png',
'technic_homedecor_glowlight_cube_white_tb.png',
'technic_homedecor_glowlight_cube_white_sides.png',
'technic_homedecor_glowlight_cube_white_sides.png',
'technic_homedecor_glowlight_cube_white_sides.png',
'technic_homedecor_glowlight_cube_white_sides.png'
},
selection_box = {
type = "fixed",
fixed = { -0.25, -0.5, -0.25, 0.25, 0, 0.25 }
},
node_box = {
type = "fixed",
fixed = { -0.25, -0.5, -0.25, 0.25, 0, 0.25 }
},
sunlight_propagates = false,
paramtype = "light",
paramtype2 = "facedir",
walkable = true,
light_source = minetest.LIGHT_MAX-1,
sounds = default.node_sound_wood_defaults(),
groups = { snappy = 3, not_in_creative_inventory=1},
drop="technic:homedecor_glowlight_small_cube_white",
on_place = function(itemstack, placer, pointed_thing)
technic_homedecor_rotate_and_place(itemstack, placer, pointed_thing)
return itemstack
end,
on_construct = function(pos)
technic.inductive_on_construct(pos, 50, "White Glowlight (small cube)")
end,
on_punch = function(pos, node, puncher)
technic.inductive_on_punch_on(pos, 0, "technic:homedecor_glowlight_small_cube_white")
end
})
technic.register_inductive_machine("technic:homedecor_glowlight_half_yellow")
technic.register_inductive_machine("technic:homedecor_glowlight_half_white")
technic.register_inductive_machine("technic:homedecor_glowlight_quarter_yellow")
technic.register_inductive_machine("technic:homedecor_glowlight_quarter_white")
technic.register_inductive_machine("technic:homedecor_glowlight_small_cube_yellow")
technic.register_inductive_machine("technic:homedecor_glowlight_small_cube_white")
@@ -0,0 +1,220 @@
-- The power radiator fuctions like an inductive charger
-- only better in the game setting.
-- The purpose is to allow small appliances to receive power
-- without the overhead of the wiring needed for larger machines.
--
-- The power radiator will consume power corresponding to the
-- sum(power rating of the attached appliances)/0.06
-- Using inductive power transfer is very inefficient so this is
-- set to the factor 0.06.
--
-- Punching the radiator will toggle the power state of all attached appliances.
local power_radius = 12
minetest.register_craft({
output = 'technic:power_radiator 1',
recipe = {
{'technic:stainless_steel_ingot', 'technic:mv_transformer', 'technic:stainless_steel_ingot'},
{'technic:copper_coil', 'technic:machine_casing', 'technic:copper_coil'},
{'technic:rubber', 'technic:mv_cable', 'technic:rubber'},
}
})
------------------------------------------------------------------
-- API for inductive powered nodes:
-- Use the functions below to set the corresponding callbacks
-- Also two nodes are needed: The inactive and the active one. The active must be called <name>_active .
------------------------------------------------------------------
-- Register a new appliance using this function
technic.inductive_nodes = {}
technic.register_inductive_machine = function(name)
table.insert(technic.inductive_nodes, name)
table.insert(technic.inductive_nodes, name.."_active")
end
-- Appliances:
-- has_supply: pos of supply node if the appliance has a power radiator near with sufficient power for the demand
-- EU_demand: The power demand of the device.
-- EU_charge: Actual use. set to EU_demand if active==1
-- active: set to 1 if the device is on
technic.inductive_on_construct = function(pos, eu_demand, infotext)
local meta = minetest.get_meta(pos)
meta:set_string("infotext", infotext)
meta:set_int("technic_inductive_power_machine", 1)
meta:set_int("EU_demand", eu_demand) -- The power demand of this appliance
meta:set_int("EU_charge", 0) -- The actual power draw of this appliance
meta:set_string("has_supply", "") -- Register whether we are powered or not. For use with several radiators.
meta:set_int("active", 0) -- If the appliance can be turned on and off by using it use this.
end
technic.inductive_on_punch_off = function(pos, eu_charge, swapnode)
local meta = minetest.get_meta(pos)
if meta:get_string("has_supply") ~= "" then
technic.swap_node(pos, swapnode)
meta:set_int("active", 1)
meta:set_int("EU_charge",eu_charge)
--print("-----------")
--print("Turn on:")
--print("EU_charge: "..meta:get_int("EU_charge"))
--print("has_supply: "..meta:get_string("has_supply"))
--print("<----------->")
end
end
technic.inductive_on_punch_on = function(pos, eu_charge, swapnode)
local meta = minetest.get_meta(pos)
technic.swap_node(pos, swapnode)
meta:set_int("active", 0)
meta:set_int("EU_charge",eu_charge)
--print("-----------")
--print("Turn off:")
--print("EU_charge: "..meta:get_int("EU_charge"))
--print("has_supply: "..meta:get_string("has_supply"))
--print("<---------->")
end
local shutdown_inductive_appliances = function(pos)
-- The supply radius
local rad = power_radius
-- If the radiator is removed. turn off all appliances in region
-- If another radiator is near it will turn on the appliances again
local positions = minetest.find_nodes_in_area(
{x=pos.x-rad, y=pos.y-rad, z=pos.z-rad},
{x=pos.x+rad, y=pos.y+rad, z=pos.z+rad},
technic.inductive_nodes)
for _, pos1 in pairs(positions) do
local meta1 = minetest.get_meta(pos1)
-- If the appliance is belonging to this node
if meta1:get_string("has_supply") == pos.x..pos.y..pos.z then
local nodename = minetest.get_node(pos1).name
-- Swap the node and make sure it is off and unpowered
if string.sub(nodename, -7) == "_active" then
technic.swap_node(pos1, string.sub(nodename, 1, -8))
meta1:set_int("active", 0)
meta1:set_int("EU_charge", 0)
end
meta1:set_string("has_supply", "")
end
end
end
local toggle_on_off_inductive_appliances = function(pos, node, puncher)
if pos == nil then return end
-- The supply radius
local rad = power_radius
local positions = minetest.find_nodes_in_area(
{x=pos.x-rad, y=pos.y-rad, z=pos.z-rad},
{x=pos.x+rad, y=pos.y+rad, z=pos.z+rad},
technic.inductive_nodes)
for _, pos1 in pairs(positions) do
local meta1 = minetest.get_meta(pos1)
if meta1:get_string("has_supply") == pos.x..pos.y..pos.z then
minetest.punch_node(pos1)
end
end
end
minetest.register_node("technic:power_radiator", {
description = "MV Power Radiator",
tiles = {"technic_lv_cable.png", "technic_lv_cable.png", "technic_lv_cable.png",
"technic_lv_cable.png", "technic_lv_cable.png", "technic_lv_cable.png"},
groups = {snappy=2, choppy=2, oddly_breakable_by_hand=2},
sounds = default.node_sound_wood_defaults(),
drawtype = "nodebox",
paramtype = "light",
is_ground_content = true,
node_box = {
type = "fixed",
fixed = {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5},
},
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_int("MV_EU_demand",1) -- Demand on the primary side when idle
meta:set_int("connected_EU_demand",0) -- Potential demand of connected appliances
meta:set_string("infotext", "MV Power Radiator")
end,
on_dig = function(pos, node, digger)
shutdown_inductive_appliances(pos)
return minetest.node_dig(pos, node, digger)
end,
on_punch = function(pos, node, puncher)
toggle_on_off_inductive_appliances(pos, node, puncher)
end
})
minetest.register_abm({
label = "Machines: run power radiator",
nodenames = {"technic:power_radiator"},
interval = 1,
chance = 1,
action = function(pos, node, active_object_count, active_object_count_wider)
local meta = minetest.get_meta(pos)
local eu_input = meta:get_int("MV_EU_input")
local eu_demand = meta:get_int("MV_EU_demand")
-- Power off automatically if no longer connected to a switching station
technic.switching_station_timeout_count(pos, "MV")
if eu_input == 0 then
-- No power
meta:set_string("infotext", "MV Power Radiator is unpowered");
-- meta:set_int("active", 1) -- used for setting textures someday maybe
shutdown_inductive_appliances(pos)
meta:set_int("connected_EU_demand", 0)
meta:set_int("MV_EU_demand",1)
elseif eu_input == eu_demand then
-- Powered and ready
-- The maximum EU sourcing a single radiator can provide.
local max_charge = 30000 -- == the max EU demand of the radiator
local connected_EU_demand = meta:get_int("connected_EU_demand")
-- Efficiency factor
local eff_factor = 0.06
-- The supply radius
local rad = power_radius
local used_charge = 0
-- Index all nodes within supply range
local positions = minetest.find_nodes_in_area(
{x=pos.x-rad, y=pos.y-rad, z=pos.z-rad},
{x=pos.x+rad, y=pos.y+rad, z=pos.z+rad},
technic.inductive_nodes)
for _, pos1 in pairs(positions) do
local meta1 = minetest.get_meta(pos1)
-- If not supplied see if this node can handle it.
if meta1:get_string("has_supply") == "" then
-- if demand surpasses the capacity of this node, don't bother adding it.
local app_eu_demand = math.floor(meta1:get_int("EU_demand") / eff_factor)
if connected_EU_demand + app_eu_demand <= max_charge then
-- We can power the appliance. Register, and spend power if it is on.
connected_EU_demand = connected_EU_demand + app_eu_demand
meta1:set_string("has_supply", pos.x..pos.y..pos.z)
--Always 0: used_charge = math.floor(used_charge + meta1:get_int("EU_charge") / eff_factor)
end
elseif meta1:get_string("has_supply") == pos.x..pos.y..pos.z then
-- The appliance has power from this node. Spend power if it is on.
used_charge = used_charge + math.floor(meta1:get_int("EU_charge") / eff_factor)
end
meta:set_string("infotext", "MV Power Radiator is powered ("
..math.floor(used_charge / max_charge * 100)
.."% of maximum power)");
if used_charge == 0 then
meta:set_int("MV_EU_demand", 1) -- Still idle
else
meta:set_int("MV_EU_demand", used_charge)
end
end
-- Save state
meta:set_int("connected_EU_demand", connected_EU_demand)
end
end,
})
technic.register_machine("MV", "technic:power_radiator", technic.receiver)
@@ -0,0 +1,14 @@
minetest.register_craft({
output = 'technic:solar_array_mv 1',
recipe = {
{'technic:solar_array_lv', 'technic:solar_array_lv', 'technic:solar_array_lv'},
{'technic:carbon_steel_ingot', 'technic:mv_transformer', 'technic:carbon_steel_ingot'},
{'', 'technic:mv_cable', ''},
}
})
technic.register_solar_array({tier="MV", power=30})
-- compatibility alias for upgrading from old versions of technic
minetest.register_alias("technic:solar_panel_mv", "technic:solar_array_mv")
@@ -0,0 +1,126 @@
-- Tool workshop
-- This machine repairs tools.
minetest.register_alias("tool_workshop", "technic:tool_workshop")
local S = technic.getter
local tube_entry = "^pipeworks_tube_connection_wooden.png"
minetest.register_craft({
output = 'technic:tool_workshop',
recipe = {
{'group:wood', 'default:diamond', 'group:wood'},
{'mesecons_pistons:piston_sticky_off', 'technic:machine_casing', 'technic:carbon_cloth'},
{'default:obsidian', 'technic:mv_cable', 'default:obsidian'},
}
})
local workshop_demand = {5000, 3500, 2000}
local workshop_formspec =
"size[8,9;]"..
"list[context;src;3,1;1,1;]"..
"label[0,0;"..S("%s Tool Workshop"):format("MV").."]"..
"list[context;upgrade1;1,3;1,1;]"..
"list[context;upgrade2;2,3;1,1;]"..
"label[1,4;"..S("Upgrade Slots").."]"..
"list[current_player;main;0,5;8,4;]"..
"listring[current_player;main]"..
"listring[context;src]"..
"listring[current_player;main]"..
"listring[context;upgrade1]"..
"listring[current_player;main]"..
"listring[context;upgrade2]"..
"listring[current_player;main]"
local run = function(pos, node)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
local eu_input = meta:get_int("MV_EU_input")
local machine_name = S("%s Tool Workshop"):format("MV")
-- Setup meta data if it does not exist.
if not eu_input then
meta:set_int("MV_EU_demand", workshop_demand[1])
meta:set_int("MV_EU_input", 0)
return
end
local EU_upgrade, tube_upgrade = technic.handle_machine_upgrades(meta)
local repairable = false
local srcstack = inv:get_stack("src", 1)
if not srcstack:is_empty() then
local itemdef = minetest.registered_items[srcstack:get_name()]
if itemdef and
(not itemdef.wear_represents or
itemdef.wear_represents == "mechanical_wear") and
srcstack:get_wear() ~= 0 then
repairable = true
end
end
technic.handle_machine_pipeworks(pos, tube_upgrade, function(pos2, x_velocity, z_velocity)
if not repairable then
technic.send_items(pos2, x_velocity, z_velocity, "src")
end
end)
if not repairable then
meta:set_string("infotext", S("%s Idle"):format(machine_name))
meta:set_int("MV_EU_demand", 0)
return
end
if eu_input < workshop_demand[EU_upgrade+1] then
meta:set_string("infotext", S("%s Unpowered"):format(machine_name))
elseif eu_input >= workshop_demand[EU_upgrade+1] then
meta:set_string("infotext", S("%s Active"):format(machine_name))
srcstack:add_wear(-1000)
inv:set_stack("src", 1, srcstack)
end
meta:set_int("MV_EU_demand", workshop_demand[EU_upgrade+1])
end
minetest.register_node("technic:tool_workshop", {
description = S("%s Tool Workshop"):format("MV"),
paramtype2 = "facedir",
tiles = {
"technic_workshop_top.png"..tube_entry,
"technic_machine_bottom.png"..tube_entry,
"technic_workshop_side.png"..tube_entry,
"technic_workshop_side.png"..tube_entry,
"technic_workshop_side.png"..tube_entry,
"technic_workshop_side.png"
},
groups = {snappy=2, choppy=2, oddly_breakable_by_hand=2,
technic_machine=1, technic_mv=1, tubedevice=1, tubedevice_receiver=1},
connect_sides = {"bottom", "back", "left", "right"},
sounds = default.node_sound_wood_defaults(),
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string("infotext", S("%s Tool Workshop"):format("MV"))
meta:set_string("formspec", workshop_formspec)
local inv = meta:get_inventory()
inv:set_size("src", 1)
inv:set_size("upgrade1", 1)
inv:set_size("upgrade2", 1)
end,
can_dig = technic.machine_can_dig,
allow_metadata_inventory_put = technic.machine_inventory_put,
allow_metadata_inventory_take = technic.machine_inventory_take,
tube = {
can_insert = function (pos, node, stack, direction)
return minetest.get_meta(pos):get_inventory():room_for_item("src", stack)
end,
insert_object = function (pos, node, stack, direction)
return minetest.get_meta(pos):get_inventory():add_item("src", stack)
end,
connect_sides = {left = 1, right = 1, back = 1, top = 1, bottom = 1},
},
technic_run = run,
after_place_node = pipeworks.after_place,
after_dig_node = technic.machine_after_dig_node
})
technic.register_machine("MV", "technic:tool_workshop", technic.receiver)
@@ -0,0 +1,96 @@
local S = technic.getter
minetest.register_craft({
output = 'technic:wind_mill_frame 5',
recipe = {
{'technic:carbon_steel_ingot', '', 'technic:carbon_steel_ingot'},
{'', 'technic:carbon_steel_ingot', ''},
{'technic:carbon_steel_ingot', '', 'technic:carbon_steel_ingot'},
}
})
minetest.register_craft({
output = 'technic:wind_mill',
recipe = {
{'', 'basic_materials:motor', ''},
{'technic:carbon_steel_ingot', 'technic:carbon_steel_block', 'technic:carbon_steel_ingot'},
{'', 'technic:mv_cable', ''},
}
})
minetest.register_node("technic:wind_mill_frame", {
description = S("Wind Mill Frame"),
drawtype = "glasslike_framed",
tiles = {"technic_carbon_steel_block.png", "default_glass.png"},
sunlight_propagates = true,
groups = {cracky=3},
sounds = default.node_sound_stone_defaults(),
paramtype = "light",
})
local function check_wind_mill(pos)
if pos.y < 30 then
return false
end
pos = {x=pos.x, y=pos.y, z=pos.z}
for i = 1, 20 do
pos.y = pos.y - 1
local node = minetest.get_node_or_nil(pos)
if not node then
-- we reached CONTENT_IGNORE, we can assume, that nothing changed
-- as the user will have to load the block to change it
return
end
if node.name ~= "technic:wind_mill_frame" then
return false
end
end
return true
end
local run = function(pos, node)
local meta = minetest.get_meta(pos)
local machine_name = S("Wind %s Generator"):format("MV")
local check = check_wind_mill(pos)
if check == false then
meta:set_int("MV_EU_supply", 0)
meta:set_string("infotext", S("%s Improperly Placed"):format(machine_name))
elseif check == true then
local power = math.min(pos.y * 100, 5000)
meta:set_int("MV_EU_supply", power)
meta:set_string("infotext", S("@1 (@2)", machine_name,
technic.EU_string(power)))
end
-- check == nil: assume nothing has changed
end
minetest.register_node("technic:wind_mill", {
description = S("Wind %s Generator"):format("MV"),
tiles = {"technic_carbon_steel_block.png"},
paramtype2 = "facedir",
groups = {cracky=1, technic_machine=1, technic_mv=1},
connect_sides = {"top", "bottom", "back", "left", "right"},
sounds = default.node_sound_stone_defaults(),
drawtype = "nodebox",
paramtype = "light",
node_box = {
type = "fixed",
fixed = {
{-0.5, -0.5, -0.5, 0.5, 0.5, 0.5}, -- Main box
{-0.1, -0.1, -0.5, 0.1, 0.1, -0.6}, -- Shaft
{-0.1, -1, -0.6, 0.1, 1, -0.7}, -- Vertical blades
{-1, -0.1, -0.6, 1, 0.1, -0.7}, -- Horizontal blades
}
},
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string("infotext", S("Wind %s Generator"):format("MV"))
meta:set_int("MV_EU_supply", 0)
end,
technic_run = run,
})
technic.register_machine("MV", "technic:wind_mill", technic.producer)
@@ -0,0 +1,30 @@
--
-- Compatibility hacks for digtron to work well with technic plus new network system
--
-- More information:
-- https://github.com/mt-mods/technic/issues/100
--
local function power_connector_compat()
local digtron_technic_run = minetest.registered_nodes["digtron:power_connector"].technic_run
minetest.override_item("digtron:power_connector",{
technic_run = function(pos, node)
local network_id = technic.pos2network(pos)
local sw_pos = network_id and technic.network2sw_pos(network_id)
local meta = minetest.get_meta(pos)
meta:set_string("HV_network", sw_pos and minetest.pos_to_string(sw_pos) or "")
return digtron_technic_run(pos, node)
end,
technic_on_disable = function(pos, node)
local meta = minetest.get_meta(pos)
meta:set_string("HV_network", "")
meta:set_string("HV_EU_input", "")
end,
})
end
minetest.register_on_mods_loaded(function()
if minetest.registered_nodes["digtron:power_connector"] then
power_connector_compat()
end
end)
+180
View File
@@ -0,0 +1,180 @@
local path = technic.modpath.."/machines"
technic.digilines = {
rules = {
-- digilines.rules.default
{x= 1,y= 0,z= 0},{x=-1,y= 0,z= 0}, -- along x beside
{x= 0,y= 0,z= 1},{x= 0,y= 0,z=-1}, -- along z beside
{x= 1,y= 1,z= 0},{x=-1,y= 1,z= 0}, -- 1 node above along x diagonal
{x= 0,y= 1,z= 1},{x= 0,y= 1,z=-1}, -- 1 node above along z diagonal
{x= 1,y=-1,z= 0},{x=-1,y=-1,z= 0}, -- 1 node below along x diagonal
{x= 0,y=-1,z= 1},{x= 0,y=-1,z=-1}, -- 1 node below along z diagonal
-- added rules for digi cable
{x = 0, y = -1, z = 0}, -- along y below
}
}
-- https://github.com/mt-mods/technic/issues/100
dofile(path.."/compat/digtron.lua")
dofile(path.."/network.lua")
dofile(path.."/register/init.lua")
-- Tiers
dofile(path.."/LV/init.lua")
dofile(path.."/MV/init.lua")
dofile(path.."/HV/init.lua")
dofile(path.."/switching_station.lua")
dofile(path.."/switching_station_globalstep.lua")
dofile(path.."/power_monitor.lua")
dofile(path.."/supply_converter.lua")
dofile(path.."/other/init.lua")
--
-- Technic power network administrative functions
--
local active_networks = technic.active_networks
local networks = technic.networks
local cables = technic.cables
--
-- Enable / disable technic globalstep
--
technic.powerctrl_state = true
minetest.register_chatcommand("powerctrl", {
params = "[on|off]",
description = "Enables or disables technic network globalstep handler",
privs = { [technic.config:get("admin_priv")] = true },
func = function(name, state)
if state == "on" then
technic.powerctrl_state = true
elseif state == "off" then
technic.powerctrl_state = false
end
minetest.chat_send_player(name, ("Technic network globalstep %s."):format(
technic.powerctrl_state and "enabled" or "disabled"
))
end
})
--
-- List all active networks with additional data
--
minetest.register_chatcommand("technic_get_active_networks", {
params = "[minlag]",
description = "list all active networks with additional network data",
privs = { [technic.config:get("admin_priv")] = true },
func = function(name, minlag)
minlag = tonumber(minlag) or 0
local activecount = 0
local network_info = {}
local netcount = 0
local nodecount = 0
local function align(s, w)
s = tostring(s)
return string.rep(' ', w - #s) .. s
end
local function net2str(id)
local p=technic.network2pos(id)
return align(("%s,%s,%s"):format(p.x,p.y,p.z),21)
end
for id,net in pairs(active_networks) do
if minlag == 0 or (net.lag and net.lag >= minlag * 1000) then
activecount = activecount + 1
table.insert(network_info, ("Pos:%s PR:%s RE:%s BA:%s Skip:%s Lag:%sms"):format(
net2str(id), align(#net.PR_nodes, 4), align(#net.RE_nodes, 4), align(#net.BA_nodes, 4),
align(net.skip, 3), net.lag and align(("%0.2f"):format(net.lag / 1000), 6) or ""
))
end
end
for _ in pairs(networks) do netcount = netcount + 1 end
for _ in pairs(cables) do nodecount = nodecount + 1 end
minetest.chat_send_player(name,
("Cached networks: %d active, %d total, %d nodes, %0.2f max lag.\n%s"):format(
activecount, netcount, nodecount, technic.get_max_lag(), table.concat(network_info, "\n")
))
end
})
--
-- Clear technic active networks
--
minetest.register_chatcommand("technic_flush_switch_cache", {
description = "removes all active networks from the cache",
privs = { [technic.config:get("admin_priv")] = true },
func = function(name)
local activecount = 0
for id in pairs(active_networks) do
activecount = activecount + 1
active_networks[id] = nil
end
minetest.chat_send_player(name, ("Network data removed: %d active networks deactivated."):format(activecount))
end
})
--
-- Completely clear all technic network caches
--
minetest.register_chatcommand("technic_clear_network_data", {
description = "removes all networks and network nodes from the cache",
privs = { [technic.config:get("admin_priv")] = true },
func = function(name)
-- Clear all network data keeping all reference links intact
local activecount = 0
local netcount = 0
local nodecount = 0
for id in pairs(active_networks) do
activecount = activecount + 1
active_networks[id] = nil
end
for id in pairs(networks) do
netcount = netcount + 1
networks[id] = nil
end
for id in pairs(cables) do
nodecount = nodecount + 1
cables[id] = nil
end
minetest.chat_send_player(name, string.format(
"Network data removed: %d active networks, %d total networks, %d network nodes.",
activecount, netcount, nodecount
))
end
})
--
-- Metadata cleanup LBM, removes old metadata values from nodes
--
--luacheck: ignore 511
if false then
minetest.register_lbm({
name = "technic:metadata-cleanup",
nodenames = {
"group:technic_machine",
"group:technic_all_tiers",
"technic:switching_station",
"technic:power_monitor",
},
action = function(pos, node)
-- Delete all listed metadata key/value pairs from technic machines
local keys = {
"LV_EU_timeout", "MV_EU_timeout", "HV_EU_timeout",
"LV_network", "MV_network", "HV_network",
"active_pos", "supply", "demand",
"battery_count", "battery_charge", "battery_charge_max",
}
local meta = minetest.get_meta(pos)
for _,key in ipairs(keys) do
-- Value of `""` will delete the key.
meta:set_string(key, "")
end
if node.name == "technic:switching_station" then
meta:set_string("active", "")
end
end,
})
end
@@ -0,0 +1,586 @@
--
-- Power network specific functions and data should live here
--
local S = technic.getter
local switch_max_range = tonumber(technic.config:get("switch_max_range"))
local off_delay_seconds = tonumber(technic.config:get("switch_off_delay_seconds"))
technic.active_networks = {}
local networks = {}
technic.networks = networks
local cables = {}
technic.cables = cables
local poshash = minetest.hash_node_position
local hashpos = minetest.get_position_from_hash
function technic.create_network(sw_pos)
local network_id = poshash({x=sw_pos.x,y=sw_pos.y-1,z=sw_pos.z})
technic.build_network(network_id)
return network_id
end
function technic.activate_network(network_id, timeout)
-- timeout is optional ttl for network in seconds, if not specified use default
local network = networks[network_id]
if network then
-- timeout is absolute time in microseconds
network.timeout = minetest.get_us_time() + ((timeout or off_delay_seconds) * 1000 * 1000)
technic.active_networks[network_id] = network
end
end
function technic.sw_pos2tier(pos, use_vm)
-- Get cable tier for switching station or nil if no cable
-- use_vm true to use VoxelManip to load node
local cable_pos = {x=pos.x,y=pos.y-1,z=pos.z}
if use_vm then
technic.get_or_load_node(cable_pos)
end
return technic.get_cable_tier(minetest.get_node(cable_pos).name)
end
-- Destroy network data
function technic.remove_network(network_id)
for pos_hash,cable_net_id in pairs(cables) do
if cable_net_id == network_id then
cables[pos_hash] = nil
end
end
networks[network_id] = nil
technic.active_networks[network_id] = nil
end
-- Remove machine or cable from network
local network_node_arrays = {"PR_nodes","BA_nodes","RE_nodes"}
function technic.remove_network_node(network_id, pos)
local network = networks[network_id]
if not network then return end
-- Clear hash tables, cannot use table.remove
local node_id = poshash(pos)
cables[node_id] = nil
network.all_nodes[node_id] = nil
-- TODO: All following things can be skipped if node is not machine
-- check here if it is or is not cable
-- or add separate function to remove cables and move responsibility to caller
-- Clear indexed arrays, do NOT leave holes
local machine_removed = false
for _,tblname in ipairs(network_node_arrays) do
local tbl = network[tblname]
for i=#tbl,1,-1 do
local mpos = tbl[i]
if mpos.x == pos.x and mpos.y == pos.y and mpos.z == pos.z then
table.remove(tbl, i)
machine_removed = true
break
end
end
end
if machine_removed then
-- Machine can still be in world, just not connected to any network. If so then disable it.
local node = minetest.get_node(pos)
technic.disable_machine(pos, node)
end
end
function technic.sw_pos2network(pos)
return cables[poshash({x=pos.x,y=pos.y-1,z=pos.z})]
end
function technic.sw_pos2network(pos)
return cables[poshash({x=pos.x,y=pos.y-1,z=pos.z})]
end
function technic.pos2network(pos)
return cables[poshash(pos)]
end
function technic.network2pos(network_id)
return hashpos(network_id)
end
function technic.network2sw_pos(network_id)
-- Return switching station position for network.
-- It is not guaranteed that position actually contains switching station.
local sw_pos = hashpos(network_id)
sw_pos.y = sw_pos.y + 1
return sw_pos
end
function technic.network_infotext(network_id, text)
if networks[network_id] == nil then return end
if text then
networks[network_id].infotext = text
else
return networks[network_id].infotext
end
end
local node_timeout = {}
local default_timeout = 2
function technic.set_default_timeout(timeout)
default_timeout = timeout or 2
end
function technic.get_timeout(tier, pos)
if node_timeout[tier] == nil then
-- it is normal that some multi tier nodes always drop here when checking all LV, MV and HV tiers
return 0
end
return node_timeout[tier][poshash(pos)] or 0
end
local function touch_node(tier, pos, timeout)
if node_timeout[tier] == nil then
-- this should get built up during registration
node_timeout[tier] = {}
end
node_timeout[tier][poshash(pos)] = timeout or default_timeout
end
technic.touch_node = touch_node
function technic.disable_machine(pos, node)
local nodedef = minetest.registered_nodes[node.name]
if nodedef and nodedef.technic_disabled_machine_name then
node.name = nodedef.technic_disabled_machine_name
minetest.swap_node(pos, node)
elseif nodedef and nodedef.technic_on_disable then
nodedef.technic_on_disable(pos, node)
end
if nodedef then
local meta = minetest.get_meta(pos)
meta:set_string("infotext", S("%s Has No Network"):format(nodedef.description))
end
local node_id = poshash(pos)
for _,nodes in pairs(node_timeout) do
nodes[node_id] = nil
end
end
--
-- Network overloading (incomplete cheat mitigation)
--
local overload_reset_time = tonumber(technic.config:get("network_overload_reset_time"))
local overloaded_networks = {}
local function overload_network(network_id)
local network = networks[network_id]
if network then
network.supply = 0
network.battery_charge = 0
end
overloaded_networks[network_id] = minetest.get_us_time() + (overload_reset_time * 1000 * 1000)
end
technic.overload_network = overload_network
local function reset_overloaded(network_id)
local remaining = math.max(0, overloaded_networks[network_id] - minetest.get_us_time())
if remaining == 0 then
-- Clear cache, remove overload and restart network
technic.remove_network(network_id)
overloaded_networks[network_id] = nil
end
-- Returns 0 when network reset or remaining time if reset timer has not expired yet
return remaining
end
technic.reset_overloaded = reset_overloaded
local function is_overloaded(network_id)
return overloaded_networks[network_id]
end
technic.is_overloaded = is_overloaded
--
-- Functions to traverse the electrical network
--
-- Add a machine node to the LV/MV/HV network
local function add_network_machine(nodes, pos, network_id, all_nodes, multitier)
local node_id = poshash(pos)
local net_id_old = cables[node_id]
if net_id_old == nil or (multitier and net_id_old ~= network_id and all_nodes[node_id] == nil) then
-- Add machine to network only if it is not already added
table.insert(nodes, pos)
-- FIXME: Machines connecting to multiple networks should have way to store multiple network ids
cables[node_id] = network_id
all_nodes[node_id] = pos
return true
elseif not multitier and net_id_old ~= network_id then
-- Do not allow running from multiple networks, trigger overload
overload_network(network_id)
overload_network(net_id_old)
local meta = minetest.get_meta(pos)
meta:set_string("infotext",S("Network Overloaded"))
end
end
-- Add a wire node to the LV/MV/HV network
local function add_cable_node(nodes, pos, network_id, queue)
local node_id = poshash(pos)
if not cables[node_id] then
cables[node_id] = network_id
nodes[node_id] = pos
table.insert(queue, pos)
end
end
-- Generic function to add found connected nodes to the right classification array
local function add_network_node(PR_nodes, RE_nodes, BA_nodes, all_nodes, pos, machines, tier, network_id, queue)
technic.get_or_load_node(pos)
local name = minetest.get_node(pos).name
if technic.is_tier_cable(name, tier) then
add_cable_node(all_nodes, pos, network_id, queue)
elseif machines[name] then
if machines[name] == technic.producer then
add_network_machine(PR_nodes, pos, network_id, all_nodes)
elseif machines[name] == technic.receiver then
add_network_machine(RE_nodes, pos, network_id, all_nodes)
elseif machines[name] == technic.producer_receiver then
if add_network_machine(PR_nodes, pos, network_id, all_nodes, true) then
table.insert(RE_nodes, pos)
end
elseif machines[name] == technic.battery then
add_network_machine(BA_nodes, pos, network_id, all_nodes)
end
end
end
-- Generic function to add single nodes to the right classification array of existing network
function technic.add_network_node(pos, network)
add_network_node(
network.PR_nodes,
network.RE_nodes,
network.BA_nodes,
network.all_nodes,
pos,
technic.machines[network.tier],
network.tier,
network.id,
{}
)
end
-- Traverse a network given a list of machines and a cable type name
local function traverse_network(PR_nodes, RE_nodes, BA_nodes, all_nodes, pos, machines, tier, network_id, queue)
local positions = {
{x=pos.x+1, y=pos.y, z=pos.z},
{x=pos.x-1, y=pos.y, z=pos.z},
{x=pos.x, y=pos.y+1, z=pos.z},
{x=pos.x, y=pos.y-1, z=pos.z},
{x=pos.x, y=pos.y, z=pos.z+1},
{x=pos.x, y=pos.y, z=pos.z-1}}
for i, cur_pos in pairs(positions) do
if not all_nodes[poshash(cur_pos)] then
add_network_node(PR_nodes, RE_nodes, BA_nodes, all_nodes, cur_pos, machines, tier, network_id, queue)
end
end
end
local function touch_nodes(list, tier)
for _, pos in ipairs(list) do
touch_node(tier, pos) -- Touch node
end
end
local function get_network(network_id, tier)
local cached = networks[network_id]
if cached and cached.tier == tier then
touch_nodes(cached.PR_nodes, tier)
touch_nodes(cached.BA_nodes, tier)
touch_nodes(cached.RE_nodes, tier)
return cached.PR_nodes, cached.BA_nodes, cached.RE_nodes
end
return technic.build_network(network_id)
end
function technic.add_network_branch(queue, network)
-- Adds whole branch to network, queue positions can be used to bypass sub branches
local PR_nodes = network.PR_nodes -- Indexed array
local BA_nodes = network.BA_nodes -- Indexed array
local RE_nodes = network.RE_nodes -- Indexed array
local all_nodes = network.all_nodes -- Hash table
local network_id = network.id
local tier = network.tier
local machines = technic.machines[tier]
local sw_pos = technic.network2sw_pos(network_id)
--print(string.format("technic.add_network_branch(%s, %s, %.17g)",queue,minetest.pos_to_string(sw_pos),network.id))
while next(queue) do
local to_visit = {}
for _, pos in ipairs(queue) do
if vector.distance(pos, sw_pos) > switch_max_range then
-- max range exceeded
return
end
traverse_network(PR_nodes, RE_nodes, BA_nodes, all_nodes, pos,
machines, tier, network_id, to_visit)
end
queue = to_visit
end
end
-- Battery charge status updates for network
local function update_battery(self, charge, max_charge, supply, demand)
self.battery_charge = self.battery_charge + charge
self.battery_charge_max = self.battery_charge_max + max_charge
self.battery_supply = self.battery_supply + supply
self.battery_demand = self.battery_demand + demand
if demand ~= 0 then
self.BA_count_active = self.BA_count_active + 1
self.BA_charge_active = self.BA_charge_active + charge
end
end
-- Moving average function generator
local function sma(period)
local values = {}
local index = 1
local sum = 0
return function(n)
-- Add new value and return average
sum = sum - (values[index] or 0) + n
values[index] = n
index = index ~= period and index + 1 or 1
return sum / #values
end
end
function technic.build_network(network_id)
technic.remove_network(network_id)
local sw_pos = technic.network2sw_pos(network_id)
local tier = technic.sw_pos2tier(sw_pos)
if not tier then
return
end
local network = {
-- Basic network data and lookup table for attached nodes (no switching stations)
id = network_id, tier = tier, all_nodes = {},
-- Indexed arrays for iteration by machine type
PR_nodes = {}, RE_nodes = {}, BA_nodes = {},
-- Power generation, usage and capacity related variables
supply = 0, demand = 0, battery_charge = 0, battery_charge_max = 0,
BA_count_active = 0, BA_charge_active = 0, battery_supply = 0, battery_demand = 0,
-- Battery status update function
update_battery = update_battery,
-- Network activation and excution control
timeout = 0, skip = 0, lag = 0, average_lag = sma(5)
}
-- Add first cable (one that is holding network id) and build network
local queue = {}
add_cable_node(network.all_nodes, technic.network2pos(network_id), network_id, queue)
technic.add_network_branch(queue, network)
network.battery_count = #network.BA_nodes
-- Add newly built network to cache array
networks[network_id] = network
-- And return producers, batteries and receivers (should this simply return network?)
return network.PR_nodes, network.BA_nodes, network.RE_nodes
end
--
-- Execute technic power network
--
local node_technic_run = {}
minetest.register_on_mods_loaded(function()
for name, _ in pairs(technic.machine_tiers) do
if type(minetest.registered_nodes[name].technic_run) == "function" then
node_technic_run[name] = minetest.registered_nodes[name].technic_run
end
end
end)
local function run_nodes(list, vm, run_stage, network)
for _, pos in ipairs(list) do
local node = minetest.get_node_or_nil(pos)
if not node then
vm:read_from_map(pos, pos)
node = minetest.get_node_or_nil(pos)
end
if node and node.name and node_technic_run[node.name] then
node_technic_run[node.name](pos, node, run_stage, network)
end
end
end
local mesecons_path = minetest.get_modpath("mesecons")
local digilines_path = minetest.get_modpath("digilines")
function technic.network_run(network_id)
--
-- !!! !!! !!! !!! !!! !!! !!! !!! !!! !!! !!!
-- TODO: This function requires a lot of cleanup
-- It is moved here from switching_station.lua and still
-- contain a lot of switching station specific stuff which
-- should be removed and/or refactored.
--
local pos = technic.network2sw_pos(network_id)
local t0 = minetest.get_us_time()
local PR_nodes
local BA_nodes
local RE_nodes
local tier = technic.sw_pos2tier(pos)
local network
if tier then
PR_nodes, BA_nodes, RE_nodes = get_network(network_id, tier)
if technic.is_overloaded(network_id) then return end
network = networks[network_id]
else
--dprint("Not connected to a network")
technic.network_infotext(network_id, S("%s Has No Network"):format(S("Switching Station")))
return
end
-- Reset battery data for updates
network.battery_charge = 0
network.battery_charge_max = 0
network.battery_supply = 0
network.battery_demand = 0
network.BA_count_active = 0
network.BA_charge_active = 0
local vm = VoxelManip()
run_nodes(PR_nodes, vm, technic.producer)
run_nodes(RE_nodes, vm, technic.receiver)
run_nodes(BA_nodes, vm, technic.battery, network)
-- Strings for the meta data
local eu_demand_str = tier.."_EU_demand"
local eu_input_str = tier.."_EU_input"
local eu_supply_str = tier.."_EU_supply"
-- Distribute charge equally across multiple batteries.
local charge_distributed = math.floor(network.BA_charge_active / network.BA_count_active)
for n, pos1 in pairs(BA_nodes) do
local meta1 = minetest.get_meta(pos1)
if (meta1:get_int(eu_demand_str) ~= 0) then
meta1:set_int("internal_EU_charge", charge_distributed)
end
end
-- Get all the power from the PR nodes
local PR_eu_supply = 0 -- Total power
for _, pos1 in pairs(PR_nodes) do
local meta1 = minetest.get_meta(pos1)
PR_eu_supply = PR_eu_supply + meta1:get_int(eu_supply_str)
end
--dprint("Total PR supply:"..PR_eu_supply)
-- Get all the demand from the RE nodes
local RE_eu_demand = 0
for _, pos1 in pairs(RE_nodes) do
local meta1 = minetest.get_meta(pos1)
RE_eu_demand = RE_eu_demand + meta1:get_int(eu_demand_str)
end
--dprint("Total RE demand:"..RE_eu_demand)
technic.network_infotext(network_id, S("@1. Supply: @2 Demand: @3",
S("Switching Station"), technic.EU_string(PR_eu_supply),
technic.EU_string(RE_eu_demand)))
-- If mesecon signal and power supply or demand changed then
-- send them via digilines.
if mesecons_path and digilines_path and mesecon.is_powered(pos) then
if PR_eu_supply ~= network.supply or
RE_eu_demand ~= network.demand then
local meta = minetest.get_meta(pos)
local channel = meta:get_string("channel")
digilines.receptor_send(pos, technic.digilines.rules, channel, {
supply = PR_eu_supply,
demand = RE_eu_demand
})
end
end
-- Data that will be used by the power monitor
network.supply = PR_eu_supply
network.demand = RE_eu_demand
network.battery_count = #BA_nodes
-- If the PR supply is enough for the RE demand supply them all
local BA_eu_demand = network.battery_demand
if PR_eu_supply >= RE_eu_demand then
--dprint("PR_eu_supply"..PR_eu_supply.." >= RE_eu_demand"..RE_eu_demand)
for _, pos1 in pairs(RE_nodes) do
local meta1 = minetest.get_meta(pos1)
local eu_demand = meta1:get_int(eu_demand_str)
meta1:set_int(eu_input_str, eu_demand)
end
-- We have a surplus, so distribute the rest equally to the BA nodes
-- Let's calculate the factor of the demand
PR_eu_supply = PR_eu_supply - RE_eu_demand
local charge_factor = 0 -- Assume all batteries fully charged
if BA_eu_demand > 0 then
charge_factor = PR_eu_supply / BA_eu_demand
end
-- TODO: EU input for all batteries: math.floor(BA_eu_demand * charge_factor * #BA_nodes)
for n, pos1 in pairs(BA_nodes) do
local meta1 = minetest.get_meta(pos1)
local eu_demand = meta1:get_int(eu_demand_str)
meta1:set_int(eu_input_str, math.floor(eu_demand * charge_factor))
--dprint("Charging battery:"..math.floor(eu_demand*charge_factor))
end
local t1 = minetest.get_us_time()
local diff = t1 - t0
if diff > 50000 then
minetest.log("warning", "[technic] [+supply] technic_run took " .. diff .. " us at " .. minetest.pos_to_string(pos))
end
return
end
-- If the PR supply is not enough for the RE demand we will discharge the batteries too
local BA_eu_supply = network.battery_supply
if PR_eu_supply + BA_eu_supply >= RE_eu_demand then
--dprint("PR_eu_supply "..PR_eu_supply.."+BA_eu_supply "..BA_eu_supply.." >= RE_eu_demand"..RE_eu_demand)
for _, pos1 in pairs(RE_nodes) do
local meta1 = minetest.get_meta(pos1)
local eu_demand = meta1:get_int(eu_demand_str)
meta1:set_int(eu_input_str, eu_demand)
end
-- We have a deficit, so distribute to the BA nodes
-- Let's calculate the factor of the supply
local charge_factor = 0 -- Assume all batteries depleted
if BA_eu_supply > 0 then
charge_factor = (PR_eu_supply - RE_eu_demand) / BA_eu_supply
end
for n,pos1 in pairs(BA_nodes) do
local meta1 = minetest.get_meta(pos1)
local eu_supply = meta1:get_int(eu_supply_str)
meta1:set_int(eu_input_str, math.floor(eu_supply * charge_factor))
--dprint("Discharging battery:"..math.floor(eu_supply*charge_factor))
end
local t1 = minetest.get_us_time()
local diff = t1 - t0
if diff > 50000 then
minetest.log("warning", "[technic] [-supply] technic_run took " .. diff .. " us at " .. minetest.pos_to_string(pos))
end
return
end
-- If the PR+BA supply is not enough for the RE demand: Power only the batteries
local charge_factor = 0 -- Assume all batteries fully charged
if BA_eu_demand > 0 then
charge_factor = PR_eu_supply / BA_eu_demand
end
for n, pos1 in pairs(BA_nodes) do
local meta1 = minetest.get_meta(pos1)
local eu_demand = meta1:get_int(eu_demand_str)
meta1:set_int(eu_input_str, math.floor(eu_demand * charge_factor))
end
for n, pos1 in pairs(RE_nodes) do
local meta1 = minetest.get_meta(pos1)
meta1:set_int(eu_input_str, 0)
end
local t1 = minetest.get_us_time()
local diff = t1 - t0
if diff > 50000 then
minetest.log("warning", "[technic] technic_run took " .. diff .. " us at " .. minetest.pos_to_string(pos))
end
end
@@ -0,0 +1,129 @@
local S = technic.getter
local desc = S("Administrative World Anchor")
local function compute_forceload_positions(pos, meta)
local radius = meta:get_int("radius")
local minpos = vector.subtract(pos, vector.new(radius, radius, radius))
local maxpos = vector.add(pos, vector.new(radius, radius, radius))
local minbpos = {}
local maxbpos = {}
for _, coord in ipairs({"x","y","z"}) do
minbpos[coord] = math.floor(minpos[coord] / 16) * 16
maxbpos[coord] = math.floor(maxpos[coord] / 16) * 16
end
local flposes = {}
for x = minbpos.x, maxbpos.x, 16 do
for y = minbpos.y, maxbpos.y, 16 do
for z = minbpos.z, maxbpos.z, 16 do
table.insert(flposes, vector.new(x, y, z))
end
end
end
return flposes
end
local function currently_forceloaded_positions(meta)
local ser = meta:get_string("forceloaded")
return ser == "" and {} or minetest.deserialize(ser)
end
local function forceload_off(meta)
local flposes = currently_forceloaded_positions(meta)
meta:set_string("forceloaded", "")
for _, p in ipairs(flposes) do
minetest.forceload_free_block(p)
end
end
local function forceload_on(pos, meta)
local want_flposes = compute_forceload_positions(pos, meta)
local have_flposes = {}
for _, p in ipairs(want_flposes) do
if minetest.forceload_block(p) then
table.insert(have_flposes, p)
end
end
meta:set_string("forceloaded", #have_flposes == 0 and "" or minetest.serialize(have_flposes))
end
local function set_display(pos, meta)
meta:set_string("infotext", S(meta:get_int("enabled") ~= 0 and "%s Enabled" or "%s Disabled"):format(desc))
meta:set_string("formspec",
"size[5,3.5]"..
"item_image[0,0;1,1;technic:admin_anchor]"..
"label[1,0;"..minetest.formspec_escape(desc).."]"..
"label[0,1;"..minetest.formspec_escape(S("Owner:").." "..meta:get_string("owner")).."]"..
(meta:get_int("locked") == 0 and
"button[3,1;2,1;lock;"..minetest.formspec_escape(S("Unlocked")).."]" or
"button[3,1;2,1;unlock;"..minetest.formspec_escape(S("Locked")).."]")..
"field[0.25,2.3;1,1;radius;"..minetest.formspec_escape(S("Radius:"))..";"..meta:get_int("radius").."]"..
(meta:get_int("enabled") == 0 and
"button[3,2;2,1;enable;"..minetest.formspec_escape(S("Disabled")).."]" or
"button[3,2;2,1;disable;"..minetest.formspec_escape(S("Enabled")).."]")..
"label[0,3;"..minetest.formspec_escape(S("Keeping %d/%d map blocks loaded"):format(
#currently_forceloaded_positions(meta), #compute_forceload_positions(pos, meta))).."]")
end
minetest.register_node("technic:admin_anchor", {
description = desc,
drawtype = "normal",
tiles = {"technic_admin_anchor.png"},
is_ground_content = true,
groups = {cracky=3, not_in_creative_inventory=1},
sounds = default.node_sound_stone_defaults(),
after_place_node = function (pos, placer)
local meta = minetest.get_meta(pos)
if placer and placer:is_player() then
meta:set_string("owner", placer:get_player_name())
end
set_display(pos, meta)
end,
can_dig = function (pos, player)
local meta = minetest.get_meta(pos)
return meta:get_int("locked") == 0 or (player and player:is_player()
and player:get_player_name() == meta:get_string("owner"))
end,
on_destruct = function (pos)
local meta = minetest.get_meta(pos)
forceload_off(meta)
end,
on_receive_fields = function (pos, formname, fields, sender)
local meta = minetest.get_meta(pos)
if (meta:get_int("locked") ~= 0 or fields.lock) and
not (sender and sender:is_player() and
sender:get_player_name() == meta:get_string("owner")) then
return
end
if fields.unlock then meta:set_int("locked", 0) end
if fields.lock then meta:set_int("locked", 1) end
if fields.disable or fields.enable or fields.radius then
forceload_off(meta)
if fields.disable then meta:set_int("enabled", 0) end
if fields.enable then meta:set_int("enabled", 1) end
if fields.radius and string.find(fields.radius, "^[0-9]+$") and tonumber(fields.radius) < 256 then
meta:set_int("radius", fields.radius)
end
if meta:get_int("enabled") ~= 0 then
forceload_on(pos, meta)
end
end
set_display(pos, meta)
end,
mesecons = {
effector = {
action_on = function(pos)
local meta = minetest.get_meta(pos)
meta:set_int("enabled", 1)
forceload_on(pos, meta)
set_display(pos, meta)
end,
action_off = function(pos)
local meta = minetest.get_meta(pos)
meta:set_int("enabled", 0)
forceload_off(meta)
set_display(pos, meta)
end
}
}
})
@@ -0,0 +1,176 @@
-- Fuel driven alloy furnace. This uses no EUs:
local S = technic.getter
minetest.register_craft({
output = 'technic:coal_alloy_furnace',
recipe = {
{'default:brick', 'default:brick', 'default:brick'},
{'default:brick', '', 'default:brick'},
{'default:brick', 'default:brick', 'default:brick'},
}
})
local machine_name = S("Fuel-Fired Alloy Furnace")
local formspec =
"size[8,9]"..
"label[0,0;"..machine_name.."]"..
"image[2,2;1,1;default_furnace_fire_bg.png]"..
"list[context;fuel;2,3;1,1;]"..
"list[context;src;2,1;2,1;]"..
"list[context;dst;5,1;2,2;]"..
"list[current_player;main;0,5;8,4;]"..
"listring[context;dst]"..
"listring[current_player;main]"..
"listring[context;src]"..
"listring[current_player;main]"..
"listring[context;fuel]"..
"listring[current_player;main]"
minetest.register_node("technic:coal_alloy_furnace", {
description = machine_name,
tiles = {"technic_coal_alloy_furnace_top.png", "technic_coal_alloy_furnace_bottom.png",
"technic_coal_alloy_furnace_side.png", "technic_coal_alloy_furnace_side.png",
"technic_coal_alloy_furnace_side.png", "technic_coal_alloy_furnace_front.png"},
paramtype2 = "facedir",
groups = {cracky=2},
legacy_facedir_simple = true,
sounds = default.node_sound_stone_defaults(),
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string("formspec", formspec)
meta:set_string("infotext", machine_name)
local inv = meta:get_inventory()
inv:set_size("fuel", 1)
inv:set_size("src", 2)
inv:set_size("dst", 4)
end,
can_dig = technic.machine_can_dig,
allow_metadata_inventory_put = technic.machine_inventory_put,
allow_metadata_inventory_take = technic.machine_inventory_take,
allow_metadata_inventory_move = technic.machine_inventory_move,
})
minetest.register_node("technic:coal_alloy_furnace_active", {
description = machine_name,
tiles = {"technic_coal_alloy_furnace_top.png", "technic_coal_alloy_furnace_bottom.png",
"technic_coal_alloy_furnace_side.png", "technic_coal_alloy_furnace_side.png",
"technic_coal_alloy_furnace_side.png", "technic_coal_alloy_furnace_front_active.png"},
paramtype2 = "facedir",
light_source = 8,
drop = "technic:coal_alloy_furnace",
groups = {cracky=2, not_in_creative_inventory=1},
legacy_facedir_simple = true,
sounds = default.node_sound_stone_defaults(),
can_dig = technic.machine_can_dig,
allow_metadata_inventory_put = technic.machine_inventory_put,
allow_metadata_inventory_take = technic.machine_inventory_take,
allow_metadata_inventory_move = technic.machine_inventory_move,
})
minetest.register_abm({
label = "Machines: run coal alloy furnace",
nodenames = {"technic:coal_alloy_furnace", "technic:coal_alloy_furnace_active"},
interval = 1,
chance = 1,
action = function(pos, node, active_object_count, active_object_count_wider)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
if inv:get_size("src") == 1 then -- Old furnace -> convert it
inv:set_size("src", 2)
inv:set_stack("src", 2, inv:get_stack("src2", 1))
inv:set_size("src2", 0)
end
for i, name in pairs({
"fuel_totaltime",
"fuel_time",
"src_totaltime",
"src_time"}) do
if not meta:get_float(name) then
meta:set_float(name, 0.0)
end
end
-- Get what to cook if anything
local result = technic.get_recipe("alloy", inv:get_list("src"))
local was_active = false
if meta:get_float("fuel_time") < meta:get_float("fuel_totaltime") then
was_active = true
meta:set_int("fuel_time", meta:get_int("fuel_time") + 1)
if result then
meta:set_int("src_time", meta:get_int("src_time") + 1)
if meta:get_int("src_time") >= result.time then
meta:set_int("src_time", 0)
local result_stack = ItemStack(result.output)
if inv:room_for_item("dst", result_stack) then
inv:set_list("src", result.new_input)
inv:add_item("dst", result_stack)
end
end
else
meta:set_int("src_time", 0)
end
end
if meta:get_float("fuel_time") < meta:get_float("fuel_totaltime") then
local percent = math.floor(meta:get_float("fuel_time") /
meta:get_float("fuel_totaltime") * 100)
meta:set_string("infotext", S("%s Active"):format(machine_name).." ("..percent.."%)")
technic.swap_node(pos, "technic:coal_alloy_furnace_active")
meta:set_string("formspec",
"size[8,9]"..
"label[0,0;"..machine_name.."]"..
"image[2,2;1,1;default_furnace_fire_bg.png^[lowpart:"..
(100 - percent)..":default_furnace_fire_fg.png]"..
"list[context;fuel;2,3;1,1;]"..
"list[context;src;2,1;2,1;]"..
"list[context;dst;5,1;2,2;]"..
"list[current_player;main;0,5;8,4;]"..
"listring[context;dst]"..
"listring[current_player;main]"..
"listring[context;src]"..
"listring[current_player;main]"..
"listring[context;fuel]"..
"listring[current_player;main]")
return
end
local recipe = technic.get_recipe("alloy", inv:get_list("src"))
if not recipe then
if was_active then
meta:set_string("infotext", S("%s is empty"):format(machine_name))
technic.swap_node(pos, "technic:coal_alloy_furnace")
meta:set_string("formspec", formspec)
end
return
end
-- Next take a hard look at the fuel situation
local fuel = nil
local afterfuel
local fuellist = inv:get_list("fuel")
if fuellist then
fuel, afterfuel = minetest.get_craft_result({method = "fuel", width = 1, items = fuellist})
end
if fuel.time <= 0 then
meta:set_string("infotext", S("%s Out Of Fuel"):format(machine_name))
technic.swap_node(pos, "technic:coal_alloy_furnace")
meta:set_string("formspec", formspec)
return
end
meta:set_string("fuel_totaltime", fuel.time)
meta:set_string("fuel_time", 0)
inv:set_stack("fuel", 1, afterfuel.items[1])
end,
})
@@ -0,0 +1,5 @@
local S = technic.getter
if minetest.registered_nodes["default:furnace"].description == "Furnace" then
minetest.override_item("default:furnace", { description = S("Fuel-Fired Furnace") })
end
@@ -0,0 +1,210 @@
local S = technic.getter
local function deploy_node(inv, slot_name, pos, node, machine_node)
if node.param2 > 3 then return end
if node.name ~= "air" then
if node.name == "ignore" or
node.name == "default:chest_open" or
node.name == "default:chest_locked_open" or
node.name == "default:lava_source" or
node.name == "default:lava_flowing" or
node.name == "default:water_source" or
node.name == "default:water_flowing" then
return
end
local drops = minetest.get_node_drops(node.name, "")
local remove_to = false
for i, item in ipairs(drops) do
if not inv:room_for_item(slot_name, item) then
remove_to = i - 1
break
end
inv:add_item(slot_name, item)
end
if remove_to then
for i = 1, remove_to do
inv:remove_item(slot_name, drops[i])
end
else
minetest.remove_node(pos)
end
return
end
if not inv:is_empty(slot_name) then
local stack = inv:get_list(slot_name)[1]
local def = stack:get_definition()
if def.type == "node" then
minetest.set_node(pos, {
name = stack:get_name(),
param2 = machine_node.param2
})
stack:take_item()
inv:set_stack(slot_name, 1, stack)
elseif def.type == "craft" then
if def.on_place then
-- Use pcall to avoid nil placer errors.
-- TODO: Do without pcall.
local ok, stk = pcall(def.on_place, stack, nil, {
-- Fake pointed_thing
type = "node",
above = pos,
under = {x=pos.x, y=pos.y-1, z=pos.z},
})
if ok then
inv:set_stack(slot_name, 1, stk or stack)
return
end
end
minetest.item_place_object(stack, nil, {
-- Fake pointed_thing
type = "node",
above = pos,
under = pos,
})
inv:set_stack(slot_name, 1, nil)
end
end
end
minetest.register_craft({
type = "shapeless",
output = 'technic:constructor_mk1_off 1',
recipe = {'technic:nodebreaker_off', 'technic:deployer_off'},
})
minetest.register_craft({
type = "shapeless",
output = 'technic:constructor_mk2_off 1',
recipe = {'technic:constructor_mk1_off', 'technic:constructor_mk1_off'},
})
minetest.register_craft({
type = "shapeless",
output = 'technic:constructor_mk3_off 1',
recipe = {'technic:constructor_mk2_off', 'technic:constructor_mk2_off'},
})
local function make_on(mark, length)
return function(pos, node)
local meta = minetest.get_meta(pos)
local owner = meta:get_string("owner")
local inv = meta:get_inventory()
local dir = vector.new()
if node.param2 == 3 then dir.x = 1 end
if node.param2 == 2 then dir.z = 1 end
if node.param2 == 1 then dir.x = -1 end
if node.param2 == 0 then dir.z = -1 end
local place_pos = vector.new(pos)
if node.name == "technic:constructor_mk"..mark.."_off" then
technic.swap_node(pos, "technic:constructor_mk"..mark.."_on")
minetest.check_for_falling(pos)
for i = 1, length do
place_pos = vector.add(place_pos, dir)
if owner ~= "" and minetest.is_protected(place_pos, owner) then
return
end
local place_node = minetest.get_node(place_pos)
deploy_node(inv, "slot"..i, place_pos, place_node, node)
end
end
end
end
local function make_off(mark)
return function(pos, node)
if node.name == "technic:constructor_mk"..mark.."_on" then
technic.swap_node(pos,"technic:constructor_mk"..mark.."_off")
minetest.check_for_falling(pos)
end
end
end
local function allow_inventory_put(pos, listname, index, stack, player)
if stack and minetest.get_item_group(stack:get_name(), "technic_constructor") == 1 then
return 0
end
return technic.machine_inventory_put(pos, listname, index, stack, player)
end
local function make_constructor(mark, length)
minetest.register_node("technic:constructor_mk"..mark.."_off", {
description = S("Constructor Mk%d"):format(mark),
tiles = {"technic_constructor_mk"..mark.."_top_off.png",
"technic_constructor_mk"..mark.."_bottom_off.png",
"technic_constructor_mk"..mark.."_side2_off.png",
"technic_constructor_mk"..mark.."_side1_off.png",
"technic_constructor_back.png",
"technic_constructor_front_off.png"},
paramtype2 = "facedir",
groups = {snappy=2, choppy=2, oddly_breakable_by_hand=2,
mesecon = 2, technic_constructor = 1},
mesecons = {effector = {action_on = make_on(mark, length)}},
sounds = default.node_sound_stone_defaults(),
on_construct = function(pos)
local meta = minetest.get_meta(pos)
local formspec = "size[8,9;]"..
"label[0,0;"..S("Constructor Mk%d"):format(mark).."]"..
"list[current_player;main;0,5;8,4;]"..
"listring[current_player;main]"
for i = 1, length do
formspec = formspec..
"label[5,"..(i - 1)..";"..S("Slot %d"):format(i).."]"..
"list[context;slot"..i..";6,"..(i - 1)..";1,1;]"..
"listring[context;slot"..i.."]"
end
meta:set_string("formspec", formspec)
meta:set_string("infotext", S("Constructor Mk%d"):format(mark))
local inv = meta:get_inventory()
for i = 1, length do
inv:set_size("slot"..i, 1)
end
meta:set_string("owner", "?")
end,
after_place_node = function(pos, placer)
local meta = minetest.get_meta(pos)
meta:set_string("owner", (placer and placer:get_player_name() or "?"))
end,
can_dig = function(pos, player)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
for i = 1, length do
if not inv:is_empty("slot"..i) then
return false
end
end
return true
end,
allow_metadata_inventory_put = allow_inventory_put,
allow_metadata_inventory_take = technic.machine_inventory_take,
allow_metadata_inventory_move = technic.machine_inventory_move,
on_rotate = screwdriver.rotate_simple
})
minetest.register_node("technic:constructor_mk"..mark.."_on", {
tiles = {"technic_constructor_mk"..mark.."_top_on.png",
"technic_constructor_mk"..mark.."_bottom_on.png",
"technic_constructor_mk"..mark.."_side2_on.png",
"technic_constructor_mk"..mark.."_side1_on.png",
"technic_constructor_back.png",
"technic_constructor_front_on.png"},
paramtype2 = "facedir",
drop = "technic:constructor_mk"..mark.."_off",
groups = {snappy=2, choppy=2, oddly_breakable_by_hand=2,
mesecon=2, not_in_creative_inventory=1, technic_constructor=1},
mesecons= {effector = {action_off = make_off(mark)}},
sounds = default.node_sound_stone_defaults(),
allow_metadata_inventory_put = allow_inventory_put,
allow_metadata_inventory_take = technic.machine_inventory_take,
allow_metadata_inventory_move = technic.machine_inventory_move,
on_rotate = false
})
end
make_constructor(1, 1)
make_constructor(2, 2)
make_constructor(3, 4)
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,15 @@
local path = technic.modpath.."/machines/other"
-- mesecons and tubes related
dofile(path.."/injector.lua")
dofile(path.."/constructor.lua")
if technic.config:get_bool("enable_frames") and minetest.get_modpath("mesecons_mvps") ~= nil then
dofile(path.."/frames.lua")
end
-- Coal-powered machines
dofile(path.."/coal_alloy_furnace.lua")
dofile(path.."/coal_furnace.lua")
dofile(path.."/anchor.lua")
@@ -0,0 +1,157 @@
local S = technic.getter
local fs_helpers = pipeworks.fs_helpers
local tube_entry = "^pipeworks_tube_connection_metallic.png"
local param2_to_under = {
[0] = {x= 0,y=-1,z= 0}, [1] = {x= 0,y= 0,z=-1},
[2] = {x= 0,y= 0,z= 1}, [3] = {x=-1,y= 0,z= 0},
[4] = {x= 1,y= 0,z= 0}, [5] = {x= 0,y= 1,z= 0}
}
local base_formspec = "size[8,9;]"..
"label[0,0;"..S("Self-Contained Injector").."]"..
"list[context;main;0,2;8,2;]"..
"list[current_player;main;0,5;8,4;]"..
"listring[]"
local function set_injector_formspec(pos)
local meta = minetest.get_meta(pos)
local formspec = base_formspec..
fs_helpers.cycling_button(
meta,
pipeworks.button_base,
"splitstacks",
{
pipeworks.button_off,
pipeworks.button_on
}
)..pipeworks.button_label
if meta:get_string("mode") == "whole stacks" then
formspec = formspec.."button[0,1;4,1;mode_item;"..S("Stackwise").."]"
else
formspec = formspec.."button[0,1;4,1;mode_stack;"..S("Itemwise").."]"
end
if minetest.get_node_timer(pos):is_started() then
formspec = formspec.."button[4,1;4,1;disable;"..S("Enabled").."]"
else
formspec = formspec.."button[4,1;4,1;enable;"..S("Disabled").."]"
end
meta:set_string("formspec", formspec)
end
minetest.register_node("technic:injector", {
description = S("Self-Contained Injector"),
tiles = {
"technic_injector_top.png"..tube_entry,
"technic_injector_bottom.png",
"technic_injector_side.png"..tube_entry,
"technic_injector_side.png"..tube_entry,
"technic_injector_side.png"..tube_entry,
"technic_injector_side.png"
},
paramtype2 = "facedir",
groups = {snappy=2, choppy=2, oddly_breakable_by_hand=2, tubedevice=1, tubedevice_receiver=1},
tube = {
can_insert = function(pos, node, stack, direction)
local meta = minetest.get_meta(pos)
if meta:get_int("splitstacks") == 1 then
stack = stack:peek_item(1)
end
return meta:get_inventory():room_for_item("main", stack)
end,
insert_object = function(pos, node, stack, direction)
return minetest.get_meta(pos):get_inventory():add_item("main", stack)
end,
connect_sides = {left=1, right=1, back=1, top=1, bottom=1},
},
sounds = default.node_sound_wood_defaults(),
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string("infotext", S("Self-Contained Injector"))
meta:set_string("mode", "single items")
meta:get_inventory():set_size("main", 16)
minetest.get_node_timer(pos):start(1)
set_injector_formspec(pos)
end,
can_dig = function(pos, player)
return minetest.get_meta(pos):get_inventory():is_empty("main")
end,
on_receive_fields = function(pos, formanme, fields, sender)
if fields.quit or not pipeworks.may_configure(pos, sender) then
return
end
local meta = minetest.get_meta(pos)
if fields.mode_item then
meta:set_string("mode", "single items")
elseif fields.mode_stack then
meta:set_string("mode", "whole stacks")
elseif fields.disable then
minetest.get_node_timer(pos):stop()
elseif fields.enable then
minetest.get_node_timer(pos):start(1)
end
fs_helpers.on_receive_fields(pos, fields)
set_injector_formspec(pos)
end,
on_timer = function(pos, elapsed)
local meta = minetest.get_meta(pos)
local node = minetest.get_node(pos)
local dir = param2_to_under[math.floor(node.param2 / 4)]
local node_under = minetest.get_node(vector.add(pos, dir))
if minetest.get_item_group(node_under.name, "tubedevice") > 0 then
local inv = meta:get_inventory()
local list = inv:get_list("main")
if not list then
return true
end
local stackwise = meta:get_string("mode") == "whole stacks"
for i,stack in ipairs(list) do
if not stack:is_empty() then
if stackwise then
technic.tube_inject_item(pos, pos, dir, stack:to_table())
stack:clear()
else
technic.tube_inject_item(pos, pos, dir, stack:take_item(1):to_table())
end
inv:set_stack("main", i, stack)
break
end
end
end
return true
end,
on_rotate = function(pos, node, user, mode, new_param2)
node.param2 = new_param2
minetest.swap_node(pos, node)
pipeworks.scan_for_tube_objects(pos)
return true
end,
allow_metadata_inventory_put = technic.machine_inventory_put,
allow_metadata_inventory_take = technic.machine_inventory_take,
allow_metadata_inventory_move = technic.machine_inventory_move,
after_place_node = pipeworks.after_place,
after_dig_node = pipeworks.after_dig
})
minetest.register_craft({
output = "technic:injector 1",
recipe = {
{"", "technic:control_logic_unit",""},
{"", "default:chest",""},
{"", "pipeworks:tube_1",""},
}
})
minetest.register_lbm({
label = "Old injector conversion",
name = "technic:old_injector_conversion",
nodenames = {"technic:injector"},
run_at_every_load = false,
action = function(pos, node)
minetest.get_node_timer(pos):start(1)
set_injector_formspec(pos)
end
})
@@ -0,0 +1,130 @@
-- POWER MONITOR
-- The power monitor can be used to monitor how much power is available on a network,
-- similarly to the old "slave" switching stations.
local S = technic.getter
local cable_entry = "^technic_cable_connection_overlay.png"
-- Get registered cable or nil, returns nil if area is not loaded
local function get_cable(pos)
local node = minetest.get_node_or_nil(pos)
return (node and technic.get_cable_tier(node.name)) and node
end
-- return the position of connected cable or nil
-- TODO: Make it support every possible orientation
local function get_connected_cable_network(pos)
local param2 = minetest.get_node(pos).param2
-- should probably also work sideways or upside down but for now it wont
if param2 > 3 then return end
-- Below?
local checkpos = {x=pos.x,y=pos.y-1,z=pos.z}
local network_id = get_cable(checkpos) and technic.pos2network(checkpos)
if network_id then
return network_id
end
-- Behind?
checkpos = vector.add(minetest.facedir_to_dir(param2),pos)
network_id = get_cable(checkpos) and technic.pos2network(checkpos)
return network_id
end
-- return the position of the associated switching station or nil
local function get_network(pos)
local network_id = get_connected_cable_network(pos)
local network = network_id and technic.networks[network_id]
local swpos = network and technic.network2sw_pos(network_id)
local is_powermonitor = swpos and minetest.get_node(swpos).name == "technic:switching_station"
return (is_powermonitor and network.all_nodes[network_id]) and network
end
minetest.register_craft({
output = "technic:power_monitor",
recipe = {
{"", "", ""},
{"", "technic:machine_casing", "default:copper_ingot"},
{"technic:lv_cable", "technic:lv_cable", "technic:lv_cable"}
}
})
minetest.register_node("technic:power_monitor",{
description = S("Power Monitor"),
tiles = {
"technic_power_monitor_sides.png",
"technic_power_monitor_sides.png"..cable_entry,
"technic_power_monitor_sides.png",
"technic_power_monitor_sides.png",
"technic_power_monitor_sides.png"..cable_entry,
"technic_power_monitor_front.png"
},
paramtype2 = "facedir",
groups = {snappy=2, choppy=2, oddly_breakable_by_hand=2, technic_all_tiers=1},
connect_sides = {"bottom", "back"},
sounds = default.node_sound_wood_defaults(),
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string("infotext", S("Power Monitor"))
meta:set_string("channel", "power_monitor"..minetest.pos_to_string(pos))
meta:set_string("formspec", "field[channel;Channel;${channel}]")
end,
on_receive_fields = function(pos, formname, fields, sender)
if not fields.channel then
return
end
local plname = sender:get_player_name()
if minetest.is_protected(pos, plname) and not minetest.check_player_privs(sender, "protection_bypass") then
minetest.record_protection_violation(pos, plname)
return
end
local meta = minetest.get_meta(pos)
meta:set_string("channel", fields.channel)
end,
digiline = {
receptor = {
rules = technic.digilines.rules,
action = function() end
},
effector = {
rules = technic.digilines.rules,
action = function(pos, node, channel, msg)
if msg ~= "GET" and msg ~= "get" then
return
end
local meta = minetest.get_meta(pos)
if channel ~= meta:get_string("channel") then
return
end
local network = get_network(pos)
if not network then return end
digilines.receptor_send(pos, technic.digilines.rules, channel, {
supply = network.supply,
demand = network.demand,
lag = network.lag,
battery_count = network.battery_count,
battery_charge = network.battery_charge,
battery_charge_max = network.battery_charge_max,
})
end
},
},
})
minetest.register_abm({
nodenames = {"technic:power_monitor"},
label = "Machines: run power monitor",
interval = 1,
chance = 1,
action = function(pos, node, active_object_count, active_object_count_wider)
local meta = minetest.get_meta(pos)
local network = get_network(pos)
if network then
meta:set_string("infotext", S("Power Monitor. Supply: @1 Demand: @2",
technic.EU_string(network.supply), technic.EU_string(network.demand)))
else
meta:set_string("infotext",S("Power Monitor Has No Network"))
end
end,
})
@@ -0,0 +1,50 @@
local S = technic.getter
function technic.insert_object_unique_stack(pos, node, incoming_stack, direction)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
local incoming_name = incoming_stack:get_name()
local stack_index = nil
for inv_index, inv_stack in pairs(inv:get_list("src")) do
if inv_stack:get_name() == incoming_name then
stack_index = inv_index
break
end
end
if stack_index == nil then
return inv:add_item("src", incoming_stack)
end
local present_stack = inv:get_stack("src", stack_index)
local leftover = present_stack:add_item(incoming_stack)
inv:set_stack("src", stack_index, present_stack)
return leftover
end
function technic.can_insert_unique_stack(pos, node, incoming_stack, direction)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
local incoming_name = incoming_stack:get_name()
if meta:get_int("splitstacks") == 0 then
-- avoid looping second time with inv:contains_item("src", incoming_stack)
for _, inv_stack in pairs(inv:get_list("src")) do
if inv_stack:get_name() == incoming_name then
return inv_stack:item_fits(incoming_stack)
end
end
end
return technic.default_can_insert(pos, node, incoming_stack, direction)
end
function technic.register_alloy_furnace(data)
data.typename = "alloy"
data.machine_name = "alloy_furnace"
data.machine_desc = S("%s Alloy Furnace")
data.insert_object = technic.insert_object_unique_stack
data.can_insert = technic.can_insert_unique_stack
technic.register_base_machine(data)
end
@@ -0,0 +1,44 @@
local S = technic.getter
technic.register_recipe_type("alloy", {
description = S("Alloying"),
input_size = 2,
})
function technic.register_alloy_recipe(data)
data.time = data.time or 6
technic.register_recipe("alloy", data)
end
local recipes = {
{"technic:copper_dust 7", "technic:tin_dust", "technic:bronze_dust 8", 12},
{"default:copper_ingot 7", "default:tin_ingot", "default:bronze_ingot 8", 12},
{"technic:wrought_iron_dust 2", "technic:coal_dust", "technic:carbon_steel_dust 2", 6},
{"technic:wrought_iron_ingot 2", "technic:coal_dust", "technic:carbon_steel_ingot 2", 6},
{"technic:carbon_steel_dust 2", "technic:coal_dust", "technic:cast_iron_dust 2", 6},
{"technic:carbon_steel_ingot 2", "technic:coal_dust", "technic:cast_iron_ingot 2", 6},
{"technic:carbon_steel_dust 4", "technic:chromium_dust", "technic:stainless_steel_dust 5", 7.5},
{"technic:carbon_steel_ingot 4", "technic:chromium_ingot", "technic:stainless_steel_ingot 5", 7.5},
{"technic:copper_dust 2", "technic:zinc_dust", "technic:brass_dust 3"},
{"default:copper_ingot 2", "technic:zinc_ingot", "basic_materials:brass_ingot 3"},
{"default:sand 2", "technic:coal_dust 2", "technic:silicon_wafer"},
{"technic:silicon_wafer", "technic:gold_dust", "technic:doped_silicon_wafer"},
-- from https://en.wikipedia.org/wiki/Carbon_black
-- The highest volume use of carbon black is as a reinforcing filler in rubber products, especially tires.
-- "[Compounding a] pure gum vulcanizate … with 50% of its weight of carbon black
-- improves its tensile strength and wear resistance …"
{"technic:raw_latex 4", "technic:coal_dust 2", "technic:rubber 6", 2},
{"default:ice", "bucket:bucket_empty", "bucket:bucket_water", 1 },
{"default:obsidian", "bucket:bucket_empty", "bucket:bucket_lava", 1 },
}
if minetest.get_modpath("ethereal") then
table.insert(recipes, {"default:clay", "dye:red", "bakedclay:red"})
table.insert(recipes, {"default:clay", "dye:orange", "bakedclay:orange"})
table.insert(recipes, {"default:clay", "dye:grey", "bakedclay:grey"})
end
for _, data in pairs(recipes) do
technic.register_alloy_recipe({input = {data[1], data[2]}, output = data[3], time = data[4]})
end
@@ -0,0 +1,431 @@
local digilines_path = minetest.get_modpath("digilines")
local S = technic.getter
local tube_entry = "^pipeworks_tube_connection_metallic.png"
local cable_entry = "^technic_cable_connection_overlay.png"
technic.register_power_tool("technic:battery", 10000)
technic.register_power_tool("technic:red_energy_crystal", 50000)
technic.register_power_tool("technic:green_energy_crystal", 150000)
technic.register_power_tool("technic:blue_energy_crystal", 450000)
-- Battery recipes:
-- Tin-copper recipe:
minetest.register_craft({
output = "technic:battery",
recipe = {
{"group:wood", "default:copper_ingot", "group:wood"},
{"group:wood", "default:tin_ingot", "group:wood"},
{"group:wood", "default:copper_ingot", "group:wood"},
}
})
-- Sulfur-lead-water recipes:
-- With sulfur lumps:
-- With water:
minetest.register_craft({
output = "technic:battery",
recipe = {
{"group:wood", "technic:sulfur_lump", "group:wood"},
{"technic:lead_ingot", "bucket:bucket_water", "technic:lead_ingot"},
{"group:wood", "technic:sulfur_lump", "group:wood"},
},
replacements = {
{"bucket:bucket_water", "bucket:bucket_empty"}
}
})
-- With oil extract:
minetest.register_craft({
output = "technic:battery",
recipe = {
{"group:wood", "technic:sulfur_lump", "group:wood"},
{"technic:lead_ingot", "homedecor:oil_extract", "technic:lead_ingot"},
{"group:wood", "technic:sulfur_lump", "group:wood"},
}
})
-- With sulfur dust:
-- With water:
minetest.register_craft({
output = "technic:battery",
recipe = {
{"group:wood", "technic:sulfur_dust", "group:wood"},
{"technic:lead_ingot", "bucket:bucket_water", "technic:lead_ingot"},
{"group:wood", "technic:sulfur_dust", "group:wood"},
},
replacements = {
{"bucket:bucket_water", "bucket:bucket_empty"}
}
})
-- With oil extract:
minetest.register_craft({
output = "technic:battery",
recipe = {
{"group:wood", "technic:sulfur_dust", "group:wood"},
{"technic:lead_ingot", "homedecor:oil_extract", "technic:lead_ingot"},
{"group:wood", "technic:sulfur_dust", "group:wood"},
}
})
minetest.register_tool("technic:battery", {
description = S("RE Battery"),
inventory_image = "technic_battery.png",
wear_represents = "technic_RE_charge",
on_refill = technic.refill_RE_charge,
tool_capabilities = {
charge = 0,
max_drop_level = 0,
groupcaps = {
fleshy = {times={}, uses=10000, maxlevel=0}
}
}
})
-- x+2 + (z+2)*2
local dirtab = {
[4] = 2,
[5] = 3,
[7] = 1,
[8] = 0
}
local tube = {
insert_object = function(pos, node, stack, direction)
if direction.y == 1
or (direction.y == 0 and dirtab[direction.x+2+(direction.z+2)*2] == node.param2) then
return stack
end
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
if direction.y == 0 then
return inv:add_item("src", stack)
else
return inv:add_item("dst", stack)
end
end,
can_insert = function(pos, node, stack, direction)
if direction.y == 1
or (direction.y == 0 and dirtab[direction.x+2+(direction.z+2)*2] == node.param2) then
return false
end
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
if direction.y == 0 then
return inv:room_for_item("src", stack)
else
return inv:room_for_item("dst", stack)
end
end,
connect_sides = {left=1, right=1, back=1, top=1},
}
function technic.register_battery_box(data)
local tier = data.tier
local ltier = string.lower(tier)
local formspec =
"size[8,9]"..
"image[1,1;1,2;technic_power_meter_bg.png]"..
"list[context;src;3,1;1,1;]"..
"image[4,1;1,1;technic_battery_reload.png]"..
"list[context;dst;5,1;1,1;]"..
"label[0,0;"..S("%s Battery Box"):format(tier).."]"..
"label[3,0;"..S("Charge").."]"..
"label[5,0;"..S("Discharge").."]"..
"label[1,3;"..S("Power level").."]"..
"list[current_player;main;0,5;8,4;]"..
"listring[context;dst]"..
"listring[current_player;main]"..
"listring[context;src]"..
"listring[current_player;main]"..
(data.upgrade and
"list[context;upgrade1;3.5,3;1,1;]"..
"list[context;upgrade2;4.5,3;1,1;]"..
"label[3.5,4;"..S("Upgrade Slots").."]"..
"listring[context;upgrade1]"..
"listring[current_player;main]"..
"listring[context;upgrade2]"..
"listring[current_player;main]"
or "")
--
-- Generate formspec with power meter
--
local function get_formspec(charge_ratio, channel)
return formspec .. "image[1,1;1,2;technic_power_meter_bg.png^[lowpart:" ..
math.floor(charge_ratio * 100) .. ":technic_power_meter_fg.png]" ..
(digilines_path and
("field[0.3,4;2.2,1;channel;Digiline channel;%s]button[2,3.7;1,1;setchannel;Set]")
:format(minetest.formspec_escape(channel))
or "")
end
--
-- Update fields not affecting internal network calculations and behavior in any way
--
local function update_node(pos, update_formspec)
-- Read metadata and calculate actual values based on upgrades
local meta = minetest.get_meta(pos)
local current_charge = meta:get_int("internal_EU_charge")
local EU_upgrade = 0
if data.upgrade then
EU_upgrade = technic.handle_machine_upgrades(meta)
end
local max_charge = data.max_charge * (1 + EU_upgrade / 10)
-- Select node textures
local charge_ratio = current_charge / max_charge
local charge_count = math.ceil(charge_ratio * 8)
charge_count = math.min(charge_count, 8)
charge_count = math.max(charge_count, 0)
local last_count = meta:get_float("last_side_shown")
if charge_count ~= last_count then
technic.swap_node(pos, "technic:" .. ltier .. "_battery_box" .. charge_count)
meta:set_float("last_side_shown", charge_count)
end
-- Update formspec and infotext
local eu_input = meta:get_int(tier.."_EU_input")
local infotext = S("@1 Battery Box: @2 / @3", tier,
technic.EU_string(current_charge), technic.EU_string(max_charge))
if eu_input == 0 then
infotext = S("%s Idle"):format(infotext)
end
meta:set_string("infotext", infotext)
if update_formspec then
local channel = meta:get_string("channel")
meta:set_string("formspec", get_formspec(charge_ratio, channel))
end
end
local function get_tool(inventory, listname)
-- Get itemstack and check if it is registered tool
if inventory:is_empty(listname) then
return
end
local toolstack = inventory:get_stack(listname, 1)
local toolname = toolstack:get_name()
if technic.power_tools[toolname] == nil then
return
end
local tooldef = toolstack:get_definition()
if not tooldef then
return
end
return toolstack, tooldef
end
local function charge_tools(meta, batt_charge, charge_step)
if batt_charge <= 0 then
return batt_charge, false
end
-- Get tool stack and definition
local inv = meta:get_inventory()
local toolstack, tooldef = get_tool(inv, "src")
if not toolstack then return batt_charge, false end
-- Do the charging, get and check
local get_charge = tooldef.technic_get_charge or technic.get_charge
local tool_charge, max_charge = get_charge(toolstack)
if tool_charge >= max_charge then
return batt_charge, true
end
-- Do the charging, set charge and replace stack
local set_charge = tooldef.technic_set_charge or technic.set_charge
charge_step = math.min(charge_step, batt_charge)
charge_step = math.min(charge_step, max_charge - tool_charge)
tool_charge = tool_charge + charge_step
set_charge(toolstack, tool_charge)
inv:set_stack("src", 1, toolstack)
return batt_charge - charge_step, (tool_charge == max_charge)
end
local function discharge_tools(meta, batt_charge, charge_step, batt_max_charge)
if batt_charge >= batt_max_charge then
return batt_charge, false
end
-- Get tool stack and definition
local inv = meta:get_inventory()
local toolstack, tooldef = get_tool(inv, "dst")
if not toolstack then return batt_charge, false end
-- Do the discharging, get and check
local get_charge = tooldef.technic_get_charge or technic.get_charge
local tool_charge = get_charge(toolstack)
if tool_charge <= 0 then
return batt_charge, true
end
-- Do the discharging, set charge and replace stack
local set_charge = tooldef.technic_set_charge or technic.set_charge
charge_step = math.min(charge_step, batt_max_charge - batt_charge)
charge_step = math.min(charge_step, tool_charge)
tool_charge = tool_charge - charge_step
set_charge(toolstack, tool_charge)
inv:set_stack("dst", 1, toolstack)
return batt_charge + charge_step, (tool_charge == 0)
end
local function run(pos, node, run_state, network)
local meta = minetest.get_meta(pos)
local eu_input = meta:get_int(tier.."_EU_input")
local current_charge = meta:get_int("internal_EU_charge")
local EU_upgrade, tube_upgrade = 0, 0
if data.upgrade then
EU_upgrade, tube_upgrade = technic.handle_machine_upgrades(meta)
end
local max_charge = data.max_charge * (1 + EU_upgrade / 10)
-- Charge/discharge the battery with the input EUs
if eu_input >= 0 then
current_charge = math.min(current_charge + eu_input, max_charge)
else
current_charge = math.max(current_charge + eu_input, 0)
end
-- Charging/discharging tools here
local tool_full, tool_empty
current_charge, tool_full = charge_tools(meta, current_charge, data.charge_step)
current_charge, tool_empty = discharge_tools(meta, current_charge, data.discharge_step, max_charge)
if data.tube and (tool_full or tool_empty) then
technic.handle_machine_pipeworks(pos, tube_upgrade, function(pos2, x_velocity, z_velocity)
if tool_full then
technic.send_items(pos2, x_velocity, z_velocity, "src")
elseif tool_empty then
technic.send_items(pos2, x_velocity, z_velocity, "dst")
end
end)
end
-- We allow batteries to charge on less than the demand
local supply = math.min(data.discharge_rate, current_charge)
local demand = math.min(data.charge_rate, max_charge - current_charge)
network:update_battery(current_charge, max_charge, supply, demand)
meta:set_int(tier.."_EU_demand", demand)
meta:set_int(tier.."_EU_supply", supply)
meta:set_int("internal_EU_charge", current_charge)
meta:set_int("internal_EU_charge_max", max_charge)
local timer = minetest.get_node_timer(pos)
if not timer:is_started() then
timer:start(2)
end
end
local function on_timer(pos, elapsed)
if not technic.pos2network(pos) then return end
update_node(pos)
return true
end
for i = 0, 8 do
local groups = {snappy=2, choppy=2, oddly_breakable_by_hand=2,
technic_machine=1, ["technic_"..ltier]=1}
if i ~= 0 then
groups.not_in_creative_inventory = 1
end
if data.tube then
groups.tubedevice = 1
groups.tubedevice_receiver = 1
end
local top_tex = "technic_"..ltier.."_battery_box_top.png"..tube_entry
local front_tex = "technic_"..ltier.."_battery_box_front.png^technic_power_meter"..i..".png"
local side_tex = "technic_"..ltier.."_battery_box_side.png"..tube_entry
local bottom_tex = "technic_"..ltier.."_battery_box_bottom.png"..cable_entry
if ltier == "lv" then
top_tex = "technic_"..ltier.."_battery_box_top.png"
front_tex = "technic_"..ltier.."_battery_box_side.png^technic_power_meter"..i..".png"
side_tex = "technic_"..ltier.."_battery_box_side.png^technic_power_meter"..i..".png"
end
minetest.register_node("technic:"..ltier.."_battery_box"..i, {
description = S("%s Battery Box"):format(tier),
tiles = {
top_tex,
bottom_tex,
side_tex,
side_tex,
side_tex,
front_tex},
groups = groups,
connect_sides = {"bottom"},
tube = data.tube and tube or nil,
paramtype2 = "facedir",
sounds = default.node_sound_wood_defaults(),
drop = "technic:"..ltier.."_battery_box0",
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string("infotext", S("%s Battery Box"):format(tier))
meta:set_int(tier.."_EU_demand", 0)
meta:set_int(tier.."_EU_supply", 0)
meta:set_int(tier.."_EU_input", 0)
meta:set_float("internal_EU_charge", 0)
local inv = meta:get_inventory()
inv:set_size("src", 1)
inv:set_size("dst", 1)
inv:set_size("upgrade1", 1)
inv:set_size("upgrade2", 1)
update_node(pos, true)
end,
can_dig = technic.machine_can_dig,
allow_metadata_inventory_put = technic.machine_inventory_put,
allow_metadata_inventory_take = technic.machine_inventory_take,
allow_metadata_inventory_move = technic.machine_inventory_move,
technic_run = run,
on_timer = on_timer,
on_rightclick = function(pos) update_node(pos, true) end,
on_rotate = screwdriver.rotate_simple,
after_place_node = data.tube and pipeworks.after_place,
after_dig_node = technic.machine_after_dig_node,
on_receive_fields = function(pos, formname, fields, player)
local name = player:get_player_name()
if minetest.is_protected(pos, name) then
minetest.record_protection_violation(pos, name)
return
elseif fields.setchannel then
local meta = minetest.get_meta(pos)
meta:set_string("channel", fields.channel or "")
update_node(pos, true)
end
end,
digiline = {
receptor = {
rules = technic.digilines.rules,
action = function() end
},
effector = {
rules = technic.digilines.rules,
action = function(pos, node, channel, msg)
if msg ~= "GET" and msg ~= "get" then
return
end
local meta = minetest.get_meta(pos)
if channel ~= meta:get_string("channel") then
return
end
local inv = meta:get_inventory()
digilines.receptor_send(pos, technic.digilines.rules, channel, {
demand = meta:get_int(tier.."_EU_demand"),
supply = meta:get_int(tier.."_EU_supply"),
input = meta:get_int(tier.."_EU_input"),
charge = meta:get_int("internal_EU_charge"),
max_charge = data.max_charge * (1 + technic.handle_machine_upgrades(meta) / 10),
src = inv:get_stack("src", 1):to_table(),
dst = inv:get_stack("dst", 1):to_table(),
upgrade1 = inv:get_stack("upgrade1", 1):to_table(),
upgrade2 = inv:get_stack("upgrade2", 1):to_table()
})
end
},
},
})
end
-- Register as a battery type
-- Battery type machines function as power reservoirs and can both receive and give back power
for i = 0, 8 do
technic.register_machine(tier, "technic:"..ltier.."_battery_box"..i, technic.battery)
end
end -- End registration
@@ -0,0 +1,355 @@
local S = technic.getter
local cable_tier = {}
function technic.is_tier_cable(name, tier)
return cable_tier[name] == tier
end
function technic.get_cable_tier(name)
return cable_tier[name]
end
local function match_cable_tier_filter(name, tiers)
-- Helper to check for set of cable tiers
if tiers then
for _, tier in ipairs(tiers) do if cable_tier[name] == tier then return true end end
return false
end
return cable_tier[name] ~= nil
end
local function get_neighbors(pos, tiers)
-- TODO: Move this to network.lua
local tier_machines = tiers and technic.machines[tiers[1]]
local is_cable = match_cable_tier_filter(minetest.get_node(pos).name, tiers)
local network = is_cable and technic.networks[technic.pos2network(pos)]
local cables = {}
local machines = {}
local positions = {
{x=pos.x+1, y=pos.y, z=pos.z},
{x=pos.x-1, y=pos.y, z=pos.z},
{x=pos.x, y=pos.y+1, z=pos.z},
{x=pos.x, y=pos.y-1, z=pos.z},
{x=pos.x, y=pos.y, z=pos.z+1},
{x=pos.x, y=pos.y, z=pos.z-1},
}
for _,connected_pos in ipairs(positions) do
local name = minetest.get_node(connected_pos).name
if tier_machines and tier_machines[name] then
table.insert(machines, connected_pos)
elseif match_cable_tier_filter(name, tiers) then
local cable_network = technic.networks[technic.pos2network(connected_pos)]
table.insert(cables,{
pos = connected_pos,
network = cable_network,
})
if not network then network = cable_network end
end
end
return network, cables, machines
end
local function place_network_node(pos, tiers, name)
-- Get connections and primary network if there's any
local network, cables, machines = get_neighbors(pos, tiers)
if not network then
-- We're evidently not on a network, nothing to add ourselves to
return
end
-- Attach to primary network, this must be done before building branches from this position
technic.add_network_node(pos, network)
if not match_cable_tier_filter(name, tiers) then
if technic.machines[tiers[1]][name] == technic.producer_receiver then
-- FIXME: Multi tier machine like supply converter should also attach to other networks around pos.
-- Preferably also with connection rules defined for machine.
-- nodedef.connect_sides could be used to generate these rules.
-- For now, assume that all multi network machines belong to technic.producer_receiver group:
-- Get cables and networks around PR_RE machine
local _, machine_cables, _ = get_neighbors(pos)
for _,connection in ipairs(machine_cables) do
if connection.network and connection.network.id ~= network.id then
-- Attach PR_RE machine to secondary networks (last added is primary until above note is resolved)
technic.add_network_node(pos, connection.network)
end
end
else
-- Check connected cables for foreign networks, overload if machine was connected to multiple networks
for _, connection in ipairs(cables) do
if connection.network and connection.network.id ~= network.id then
technic.overload_network(connection.network.id)
technic.overload_network(network.id)
end
end
end
-- Machine added, skip all network building
return
end
-- Attach neighbor machines if cable was added
for _,machine_pos in ipairs(machines) do
technic.add_network_node(machine_pos, network)
end
-- Attach neighbor cables
for _,connection in ipairs(cables) do
if connection.network then
if connection.network.id ~= network.id then
-- Remove network if position belongs to another network
-- FIXME: Network requires partial rebuild but avoid doing it here if possible.
-- This might cause problems when merging two active networks into one
technic.remove_network(network.id)
technic.remove_network(connection.network.id)
connection.network = nil
end
else
-- There's cable that does not belong to any network, attach whole branch
technic.add_network_node(connection.pos, network)
technic.add_network_branch({connection.pos}, network)
end
end
end
-- NOTE: Exported for tests but should probably be moved to network.lua
technic.network_node_on_placenode = place_network_node
local function remove_network_node(pos, tiers, name)
-- Get the network and neighbors
local network, cables, machines = get_neighbors(pos, tiers)
if not network then return end
if not match_cable_tier_filter(name, tiers) then
-- Machine removed, skip cable checks to prevent unnecessary network cleanups
for _,connection in ipairs(cables) do
if connection.network then
-- Remove machine from all networks around it
technic.remove_network_node(connection.network.id, pos)
end
end
return
end
if #cables == 1 then
-- Dead end cable removed, remove it from the network
technic.remove_network_node(network.id, pos)
-- Remove neighbor machines from network if cable was removed
if match_cable_tier_filter(name, tiers) then
for _,machine_pos in ipairs(machines) do
local net, _, _ = get_neighbors(machine_pos, tiers)
if not net then
-- Remove machine from network if it does not have other connected cables
technic.remove_network_node(network.id, machine_pos)
end
end
end
else
-- TODO: Check branches around and switching stations for branches:
-- remove branches that do not have switching station. Switching stations not tracked but could be easily tracked.
-- remove branches not connected to another branch. Individual branches not tracked, requires simple AI heuristics.
-- move branches that have switching station to new networks without checking or loading actual nodes in world.
-- To do all this network must be aware of individual branches and switching stations, might not be worth it...
-- For now remove whole network and let ABM rebuild it
technic.remove_network(network.id)
end
end
-- NOTE: Exported for tests but should probably be moved to network.lua
technic.network_node_on_dignode = remove_network_node
local function item_place_override_node(itemstack, placer, pointed, node)
-- Call the default on_place function with a fake itemstack
local temp_itemstack = ItemStack(itemstack)
temp_itemstack:set_name(node.name)
local original_count = temp_itemstack:get_count()
temp_itemstack =
minetest.item_place(temp_itemstack, placer, pointed, node.param2) or
temp_itemstack
-- Remove the same number of items from the real itemstack
itemstack:take_item(original_count - temp_itemstack:get_count())
return itemstack
end
local function override_table(target, source)
if target and source then
for k,v in pairs(source) do
target[k] = v
end
end
return target
end
function technic.register_cable(tier, size, description, prefix, override_cable, override_cable_plate)
prefix = prefix or ""
override_cable_plate = override_cable_plate or override_cable
local ltier = string.lower(tier)
local node_name = "technic:"..ltier..prefix.."_cable"
cable_tier[node_name] = tier
local groups = {snappy=2, choppy=2, oddly_breakable_by_hand=2,
["technic_"..ltier.."_cable"] = 1}
local node_box = {
type = "connected",
fixed = {-size, -size, -size, size, size, size},
connect_top = {-size, -size, -size, size, 0.5, size}, -- y+
connect_bottom = {-size, -0.5, -size, size, size, size}, -- y-
connect_front = {-size, -size, -0.5, size, size, size}, -- z-
connect_back = {-size, -size, size, size, size, 0.5 }, -- z+
connect_left = {-0.5, -size, -size, size, size, size}, -- x-
connect_right = {-size, -size, -size, 0.5, size, size}, -- x+
}
minetest.register_node(node_name, override_table({
description = S("%s Cable"):format(tier),
tiles = {"technic_"..ltier..prefix.."_cable.png"},
inventory_image = "technic_"..ltier..prefix.."_cable_wield.png",
wield_image = "technic_"..ltier..prefix.."_cable_wield.png",
groups = groups,
sounds = default.node_sound_wood_defaults(),
drop = node_name,
paramtype = "light",
sunlight_propagates = true,
drawtype = "nodebox",
node_box = node_box,
connects_to = {"group:technic_"..ltier.."_cable",
"group:technic_"..ltier, "group:technic_all_tiers"},
on_construct = function(pos) place_network_node(pos, {tier}, node_name) end,
on_destruct = function(pos) remove_network_node(pos, {tier}, node_name) end,
}, override_cable))
local xyz = {
["-x"] = 1,
["-y"] = 2,
["-z"] = 3,
["x"] = 4,
["y"] = 5,
["z"] = 6,
}
local notconnects = {
[1] = "left",
[2] = "bottom",
[3] = "front",
[4] = "right",
[5] = "top",
[6] = "back",
}
local function s(p)
if p:find("-") then
return p:sub(2)
else
return "-"..p
end
end
-- TODO: Does this really need 6 different nodes? Use single node for cable plate if possible.
for p, i in pairs(xyz) do
local def = {
description = S("%s Cable Plate"):format(tier),
tiles = {"technic_"..ltier..prefix.."_cable.png"},
groups = table.copy(groups),
sounds = default.node_sound_wood_defaults(),
drop = node_name .. "_plate_1",
paramtype = "light",
sunlight_propagates = true,
drawtype = "nodebox",
node_box = table.copy(node_box),
connects_to = {"group:technic_"..ltier.."_cable",
"group:technic_"..ltier, "group:technic_all_tiers"},
on_construct = function(pos) place_network_node(pos, {tier}, node_name.."_plate_"..i) end,
on_destruct = function(pos) remove_network_node(pos, {tier}, node_name.."_plate_"..i) end,
}
def.node_box.fixed = {
{-size, -size, -size, size, size, size},
{-0.5, -0.5, -0.5, 0.5, 0.5, 0.5}
}
def.node_box.fixed[1][xyz[p]] = 7/16 * (i-3.5)/math.abs(i-3.5)
def.node_box.fixed[2][xyz[s(p)]] = 3/8 * (i-3.5)/math.abs(i-3.5)
def.node_box["connect_"..notconnects[i]] = nil
if i == 1 then
def.on_place = function(itemstack, placer, pointed_thing)
local pointed_thing_diff = vector.subtract(pointed_thing.above, pointed_thing.under)
local num = 1
local changed
for k, v in pairs(pointed_thing_diff) do
if v ~= 0 then
changed = k
num = xyz[s(tostring(v):sub(-2, -2)..k)]
break
end
end
local crtl = placer:get_player_control()
if (crtl.aux1 or crtl.sneak) and not (crtl.aux1 and crtl.sneak) and changed then
local fine_pointed = minetest.pointed_thing_to_face_pos(placer, pointed_thing)
fine_pointed = vector.subtract(fine_pointed, pointed_thing.above)
fine_pointed[changed] = nil
local ps = {}
for p2, _ in pairs(fine_pointed) do
ps[#ps+1] = p2
end
local bigger = (math.abs(fine_pointed[ps[1]]) > math.abs(fine_pointed[ps[2]]) and ps[1]) or ps[2]
if math.abs(fine_pointed[bigger]) < 0.3 then
num = num + 3
num = (num <= 6 and num) or num - 6
else
num = xyz[((fine_pointed[bigger] < 0 and "-") or "") .. bigger]
end
end
if num == nil then num = 1 end
return item_place_override_node(
itemstack, placer, pointed_thing,
{name = node_name.."_plate_"..num}
)
end
else
def.groups.not_in_creative_inventory = 1
end
def.on_rotate = function(pos, node, user, mode, new_param2)
local dir = 0
if mode == screwdriver.ROTATE_FACE then -- left-click
dir = 1
elseif mode == screwdriver.ROTATE_AXIS then -- right-click
dir = -1
end
local num = tonumber(node.name:sub(-1))
num = num + dir
num = (num >= 1 and num) or num + 6
num = (num <= 6 and num) or num - 6
minetest.swap_node(pos, {name = node_name.."_plate_"..num})
end
minetest.register_node(node_name.."_plate_"..i, override_table(def, override_cable_plate))
cable_tier[node_name.."_plate_"..i] = tier
end
minetest.register_craft({
output = node_name.."_plate_1 5",
recipe = {
{"" , "" , node_name},
{node_name, node_name, node_name},
{"" , "" , node_name},
}
})
minetest.register_craft({
output = node_name,
recipe = {
{node_name.."_plate_1"},
}
})
end
minetest.register_on_mods_loaded(function()
-- FIXME: Move this to register.lua or somewhere else where register_on_mods_loaded is not required.
-- Possible better option would be to inject these when machine is registered in register.lua.
for name, tiers in pairs(technic.machine_tiers) do
local nodedef = minetest.registered_nodes[name]
local on_construct = type(nodedef.on_construct) == "function" and nodedef.on_construct
local on_destruct = type(nodedef.on_destruct) == "function" and nodedef.on_destruct
minetest.override_item(name,{
on_construct = on_construct
and function(pos) on_construct(pos) place_network_node(pos, tiers, name) end
or function(pos) place_network_node(pos, tiers, name) end,
on_destruct = on_destruct
and function(pos) on_destruct(pos) remove_network_node(pos, tiers, name) end
or function(pos) remove_network_node(pos, tiers, name) end,
})
end
end)
@@ -0,0 +1,8 @@
local S = technic.getter
function technic.register_centrifuge(data)
data.typename = "separating"
data.machine_name = "centrifuge"
data.machine_desc = S("%s Centrifuge")
technic.register_base_machine(data)
end
@@ -0,0 +1,48 @@
local S = technic.getter
technic.register_recipe_type("separating", {
description = S("Separating"),
output_size = 2,
})
function technic.register_separating_recipe(data)
data.time = data.time or 10
technic.register_recipe("separating", data)
end
local recipes = {
{ "technic:bronze_dust 8", "technic:copper_dust 7", "technic:tin_dust" },
{ "technic:stainless_steel_dust 5", "technic:wrought_iron_dust 4", "technic:chromium_dust" },
{ "technic:brass_dust 3", "technic:copper_dust 2", "technic:zinc_dust" },
{ "technic:chernobylite_dust", "default:sand", "technic:uranium3_dust" },
{ "default:dirt 4", "default:sand", "default:gravel", "default:clay_lump 4" },
}
local function uranium_dust(p)
return "technic:uranium"..(p == 7 and "" or p).."_dust"
end
for p = 1, 34 do
table.insert(recipes, { uranium_dust(p).." 2", uranium_dust(p-1), uranium_dust(p+1) })
end
if minetest.get_modpath("bushes_classic") then
for _, berry in ipairs({ "blackberry", "blueberry", "gooseberry", "raspberry", "strawberry" }) do
table.insert(recipes, { "bushes:"..berry.."_bush", "default:stick 20", "bushes:"..berry.." 4" })
end
end
if minetest.get_modpath("farming") then
if minetest.get_modpath("cottages") then
-- work as a mechanized threshing floor
table.insert(recipes, { "farming:wheat", "farming:seed_wheat", "cottages:straw_mat" })
table.insert(recipes, { "farming:barley", "farming:seed_barley", "cottages:straw_mat" })
else
-- work in a less fancy and less efficient manner
table.insert(recipes, { "farming:wheat 4", "farming:seed_wheat 3", "default:dry_shrub 1" })
table.insert(recipes, { "farming:barley 4", "farming:seed_barley 3", "default:dry_shrub 1" })
end
end
for _, data in pairs(recipes) do
technic.register_separating_recipe({ input = { data[1] }, output = { data[2], data[3], data[4] } })
end
@@ -0,0 +1,211 @@
local S = technic.getter
-- handles the machine upgrades every tick
function technic.handle_machine_upgrades(meta)
-- Get the names of the upgrades
local inv = meta:get_inventory()
local srcstack = inv:get_stack("upgrade1", 1)
local upg_item1 = srcstack and srcstack:get_name()
srcstack = inv:get_stack("upgrade2", 1)
local upg_item2 = srcstack and srcstack:get_name()
-- Save some power by installing battery upgrades.
-- Tube loading speed can be upgraded using control logic units.
local EU_upgrade = 0
local tube_upgrade = 0
if upg_item1 == "technic:control_logic_unit" then
tube_upgrade = tube_upgrade + 1
elseif upg_item1 == "technic:battery" then
EU_upgrade = EU_upgrade + 1
end
if upg_item2 == "technic:control_logic_unit" then
tube_upgrade = tube_upgrade + 1
elseif upg_item2 == "technic:battery" then
EU_upgrade = EU_upgrade + 1
end
return EU_upgrade, tube_upgrade
end
-- handles the machine upgrades when set or removed
local function on_machine_upgrade(meta, stack)
local stack_name = stack:get_name()
if stack_name == "default:chest" then
meta:set_int("public", 1)
return 1
elseif stack_name ~= "technic:control_logic_unit"
and stack_name ~= "technic:battery" then
return 0
end
return 1
end
-- something is about to be removed
local function on_machine_downgrade(meta, stack, list)
if stack:get_name() == "default:chest" then
local inv = meta:get_inventory()
local upg1, upg2 = inv:get_stack("upgrade1", 1), inv:get_stack("upgrade2", 1)
-- only set 0 if theres not a nother chest in the other list too
if (not upg1 or not upg2 or upg1:get_name() ~= upg2:get_name()) then
meta:set_int("public", 0)
end
end
return 1
end
function technic.send_items(pos, x_velocity, z_velocity, output_name)
-- Send items on their way in the pipe system.
if output_name == nil then
output_name = "dst"
end
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
local i = 0
for _, stack in ipairs(inv:get_list(output_name)) do
i = i + 1
if stack then
local item0 = stack:to_table()
if item0 then
item0["count"] = 1
technic.tube_inject_item(pos, pos, vector.new(x_velocity, 0, z_velocity), item0)
stack:take_item(1)
inv:set_stack(output_name, i, stack)
return
end
end
end
end
-- FIXME: This funtion is never used anywhere, what is it for?
function technic.smelt_item(meta, result, speed)
local inv = meta:get_inventory()
meta:set_int("cook_time", meta:get_int("cook_time") + 1)
if meta:get_int("cook_time") < result.time / speed then
return
end
local afterfuel
result, afterfuel = minetest.get_craft_result({method = "cooking", width = 1, items = inv:get_list("src")})
if result and result.item then
meta:set_int("cook_time", 0)
-- check if there's room for output in "dst" list
if inv:room_for_item("dst", result.item) then
inv:set_stack("src", 1, afterfuel.items[1])
inv:add_item("dst", result.item)
end
end
end
function technic.handle_machine_pipeworks(pos, tube_upgrade, send_function)
if send_function == nil then
send_function = technic.send_items
end
local node = minetest.get_node(pos)
local meta = minetest.get_meta(pos)
local pos1 = vector.new(pos)
local x_velocity = 0
local z_velocity = 0
-- Output is on the left side of the furnace
if node.param2 == 3 then pos1.z = pos1.z - 1 z_velocity = -1 end
if node.param2 == 2 then pos1.x = pos1.x - 1 x_velocity = -1 end
if node.param2 == 1 then pos1.z = pos1.z + 1 z_velocity = 1 end
if node.param2 == 0 then pos1.x = pos1.x + 1 x_velocity = 1 end
local output_tube_connected = false
local node1 = minetest.get_node(pos1)
if minetest.get_item_group(node1.name, "tubedevice") > 0 then
output_tube_connected = true
end
local tube_time = meta:get_int("tube_time") + tube_upgrade
if tube_time >= 2 then
tube_time = 0
if output_tube_connected then
send_function(pos, x_velocity, z_velocity)
end
end
meta:set_int("tube_time", tube_time)
end
function technic.machine_can_dig(pos, player)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
if not inv:is_empty("src") or not inv:is_empty("dst") then
if player then
minetest.chat_send_player(player:get_player_name(),
S("Machine cannot be removed because it is not empty"))
end
return false
end
return true
end
function technic.machine_after_dig_node(pos, oldnode, oldmetadata, player)
if oldmetadata.inventory then
if oldmetadata.inventory.upgrade1 and oldmetadata.inventory.upgrade1[1] then
local stack = ItemStack(oldmetadata.inventory.upgrade1[1])
if not stack:is_empty() then
minetest.add_item(pos, stack)
end
end
if oldmetadata.inventory.upgrade2 and oldmetadata.inventory.upgrade2[1] then
local stack = ItemStack(oldmetadata.inventory.upgrade2[1])
if not stack:is_empty() then
minetest.add_item(pos, stack)
end
end
end
if minetest.registered_nodes[oldnode.name].tube then
pipeworks.after_dig(pos, oldnode, oldmetadata, player)
end
end
local function inv_change(pos, player, count, from_list, to_list, stack)
local playername = player:get_player_name()
local meta = minetest.get_meta(pos);
local public = (meta:get_int("public") == 1)
local to_upgrade = to_list == "upgrade1" or to_list == "upgrade2"
local from_upgrade = from_list == "upgrade1" or from_list == "upgrade2"
if (not public or to_upgrade or from_upgrade) and minetest.is_protected(pos, playername) then
minetest.chat_send_player(playername, S("Inventory move disallowed due to protection"))
return 0
end
if to_upgrade then
-- only place a single item into it, if it's empty
local empty = meta:get_inventory():is_empty(to_list)
if empty then
return on_machine_upgrade(meta, stack)
end
return 0
elseif from_upgrade then
-- only called on take (not move)
on_machine_downgrade(meta, stack, from_list)
end
return count
end
function technic.machine_inventory_put(pos, listname, index, stack, player)
return inv_change(pos, player, stack:get_count(), nil, listname, stack)
end
function technic.machine_inventory_take(pos, listname, index, stack, player)
return inv_change(pos, player, stack:get_count(), listname, nil, stack)
end
function technic.machine_inventory_move(pos, from_list, from_index,
to_list, to_index, count, player)
local stack = minetest.get_meta(pos):get_inventory():get_stack(from_list, from_index)
return inv_change(pos, player, count, from_list, to_list, stack)
end
@@ -0,0 +1,9 @@
local S = technic.getter
function technic.register_compressor(data)
data.typename = "compressing"
data.machine_name = "compressor"
data.machine_desc = S("%s Compressor")
technic.register_base_machine(data)
end
@@ -0,0 +1,54 @@
local S = technic.getter
technic.register_recipe_type("compressing", { description = S("Compressing") })
function technic.register_compressor_recipe(data)
data.time = data.time or 4
technic.register_recipe("compressing", data)
end
local recipes = {
{"default:snowblock", "default:ice"},
{"default:sand 2", "default:sandstone"},
{"default:desert_sand 2", "default:desert_sandstone"},
{"default:silver_sand 2", "default:silver_sandstone"},
{"default:desert_sandstone", "default:desert_stone"},
{"technic:mixed_metal_ingot", "technic:composite_plate"},
{"default:copper_ingot 5", "technic:copper_plate"},
{"technic:coal_dust 4", "technic:graphite"},
{"technic:carbon_cloth", "technic:carbon_plate"},
{"technic:uranium35_ingot 5", "technic:uranium_fuel"},
{"technic:graphite 25", "default:diamond"}
}
if minetest.get_modpath("ethereal") then
-- the density of charcoal is ~1/10 of coal, otherwise it's pure carbon
table.insert(recipes, {"ethereal:charcoal_lump 10", "default:coal_lump 1"})
end
-- defuse the default sandstone recipe, since we have the compressor to take over in a more realistic manner
minetest.clear_craft({
recipe = {
{"default:sand", "default:sand"},
{"default:sand", "default:sand"},
},
})
minetest.clear_craft({
recipe = {
{"default:desert_sand", "default:desert_sand"},
{"default:desert_sand", "default:desert_sand"},
},
})
minetest.clear_craft({
recipe = {
{"default:silver_sand", "default:silver_sand"},
{"default:silver_sand", "default:silver_sand"},
},
})
for _, data in pairs(recipes) do
technic.register_compressor_recipe({input = {data[1]}, output = data[2]})
end
@@ -0,0 +1,9 @@
local S = technic.getter
function technic.register_electric_furnace(data)
data.typename = "cooking"
data.machine_name = "electric_furnace"
data.machine_desc = S("%s Furnace")
technic.register_base_machine(data)
end
@@ -0,0 +1,9 @@
local S = technic.getter
function technic.register_extractor(data)
data.typename = "extracting"
data.machine_name = "extractor"
data.machine_desc = S("%s Extractor")
technic.register_base_machine(data)
end
Loaded 100 of 4811 files, more files were not shown because too many files have changed in this diff. Show more