mods that hopefully work for a TechAge-based pack

This commit is contained in:
2026-09-09 06:27:51 -06:00
parent 4e6b08d194
commit c27db2a537
6234 changed files with 375578 additions and 0 deletions
@@ -0,0 +1,10 @@
name: luacheck
on: [push, pull_request]
jobs:
luacheck:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@main
- name: Luacheck
uses: lunarmodules/luacheck@master
@@ -0,0 +1,28 @@
unused_args = false
ignore = {
"131", -- Unused global variable
"432", -- Shadowing an upvalue argument
}
read_globals = {
"core",
"minetest",
"default",
"worldedit",
"tubelib2",
"intllib",
"DIR_DELIM",
"techage",
string = {fields = {"split", "trim"}},
vector = {fields = {"add", "equals", "multiply"}},
table = {fields = {"copy", ""}},
}
globals = {
"hyperloop",
"ItemStack",
"screwdriver",
}
@@ -0,0 +1,13 @@
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+91
View File
@@ -0,0 +1,91 @@
# Hyperloop v2
**A new evolution in the voxel word:**
## Minetest goes Hyperloop!
Hyperloop is passenger transportation system for travelling through evacuated tubes my means of passenger pods.
It is the fast and modern way of travelling.
* Hyperloop allows travelling from point to point in seconds (900 km/h) :-)
* The tubes system with all stations and pods have to be build by players
* It can be used even on small servers without lagging
* No configuration or programming of the tube network is necessary (only the station names have to be entered)
**[See Wiki Page for more info](https://github.com/joe7575/Minetest-Hyperloop/wiki)**
![screenshot](https://github.com/joe7575/Minetest-Hyperloop/blob/master/screenshot.png)
The mod includes many different kind of blocks:
- Hyperloop Stations Block to automatically build the pod/car
- Hyperloop Booking Machine for the station to select the destination
- Hyperloop Tube to connect two stations
- Hyperloop Junction Block to connect up to 6 tubes for complex network structures
- Hyperloop Stations Signs
- Hyperloop Promo Poster for station advertisement
- Hyperloop Elevator to reach other levels
- Hyperloop Elevator Shaft to connect two elevator cars
- Hyperloop Station Book with all available stations (for builders/engineers)
- Hyperloop Tube Crowbar to crack/repair tube lines (for admins)
- Hyperloop WiFi Tubes for very large distances (optional)
- chat command to repair WorldEdit placed tubes
..and more.
Browse on: [GitHub](https://github.com/joe7575/Minetest-Hyperloop)
Download: [GitHub](https://github.com/joe7575/Minetest-Hyperloop/archive/master.zip)
## Migration from v1 to v2
The logic behind the tubes/shafts has changed. Hyperloop now uses tubelib2 as tube library.
That means, available worlds have to be migrated for the new tubes. This is done automatically but
has some risks. Therefore:
**I recommend to backup your world or test the migration from v1 to v2 on a copy of your world!!!**
## What is new in v2
- some textures changed
- the Elevator Shafts can now be used as ladder/climbing shafts
- the Crowbar is public available, but cracking a tube line need 'hyperloop' privs
- the Station Book is improved and simplified to find stations, junctions, and open tube ends
- a Waypoint plate is added to mark and easier find the tube destination
- Elevator shafts can be build in all directions (optional)
- WiFi Tubes can be crafted and placed by players (optional)
- intllib support added (German translation available)
## Introduction
**[See Wiki Page for more info](https://github.com/joe7575/Minetest-Hyperloop/wiki)**
## Configuration
The following can be changed in the minetest menu (Settings -> Advanced Settings -> Mods -> hyperloop) or directly in 'minetest.conf'
* "WiFi block enabled" - To enable the usage of WiFi blocks (default: false)
* "WiFi block crafting enabled" - To enable the crafting of WiFi blocks (default: false)
* "free tube placement enabled" - If enabled Hyperloop Tubes and Elevator Shafts can be build in all directions (default: true)
When this option is disabled, Hyperloop tubes can only be built in the horizontal direction and elevator shafts in the vertical direction.
* "enable building of subnets" - If enabled the ticket block has an additional field for specifying a subnet name. Stations with the same subnet name (optional) represent an isolated subnet within the Hyperloop network.
Example for 'minetest.conf':
```LUA
hyperloop_wifi_enabled = true -- WiFi block enabled
hyperloop_wifi_crafting_enabled = false -- WiFi block crafting enabled
hyperloop_free_tube_placement_enabled = true -- free tube placement enabled
hyperloop_subnet_enabled = true -- enable building of subnets
```
## Dependencies
tubelib2 ([GitHub](https://github.com/joe7575/tubelib2))
default
intllib
optional: worldedit, techage
# License
Copyright (C) 2017,2021 Joachim Stolberg
Code: Licensed under the GNU LGPL version 2.1 or later. See LICENSE.txt and http://www.gnu.org/licenses/lgpl-2.1.txt
Textures: CC0
Display: Derived from the work of kaeza, sofar and others (digilines) LGPLv2.1+
@@ -0,0 +1,84 @@
--[[
Hyperloop Mod
=============
Copyright (C) 2017-2019 Joachim Stolberg
LGPLv2.1+
See LICENSE.txt for more information
Station reservation/blocking and trip booking
]]--
-- for lazy programmers
local SP = function(pos) if pos then return minetest.pos_to_string(pos) end end
--local P = minetest.string_to_pos
--local M = minetest.get_meta
local S = hyperloop.S
--local NS = hyperloop.NS
local tBlockingTime = {}
local tBookings = {} -- open bookings: tBookings[SP(departure_pos)] = arrival_pos
local Stations = hyperloop.Stations
-- Reserve departure and arrival stations for some time
function hyperloop.reserve(departure_pos, arrival_pos, player)
if Stations:get(departure_pos) == nil then
hyperloop.chat(player, S("Station data is corrupted. Please rebuild the station!"))
return false
elseif Stations:get(arrival_pos) == nil then
hyperloop.chat(player, S("Station data is corrupted. Please rebuild the station!"))
return false
end
if (tBlockingTime[SP(departure_pos)] or 0) > minetest.get_gametime() then
hyperloop.chat(player, S("Station is still blocked. Please try again in a few seconds!"))
return false
elseif (tBlockingTime[SP(arrival_pos)] or 0) > minetest.get_gametime() then
hyperloop.chat(player, S("Station is still blocked. Please try again in a few seconds!"))
return false
end
-- place a reservation for 20 seconds to start the trip
tBlockingTime[SP(departure_pos)] = minetest.get_gametime() + 20
tBlockingTime[SP(arrival_pos)] = minetest.get_gametime() + 20
return true
end
-- block the already reserved stations
function hyperloop.block(departure_pos, arrival_pos, seconds)
if Stations:get(departure_pos) == nil then
return false
elseif Stations:get(arrival_pos) == nil then
return false
end
tBlockingTime[SP(departure_pos)] = minetest.get_gametime() + seconds
tBlockingTime[SP(arrival_pos)] = minetest.get_gametime() + seconds
return true
end
-- check if station is blocked
function hyperloop.is_blocked(pos)
if not pos then return false end
if Stations:get(pos) == nil then
return false
end
return (tBlockingTime[SP(pos)] or 0) > minetest.get_gametime()
end
function hyperloop.set_arrival(departure_pos, arrival_pos)
tBookings[SP(departure_pos)] = arrival_pos
end
function hyperloop.get_arrival(departure_pos)
-- Return and delete the arrival pos
local arrival_pos = tBookings[SP(departure_pos)]
tBookings[SP(departure_pos)] = nil
return arrival_pos
end
@@ -0,0 +1,321 @@
--[[
Hyperloop Mod
=============
Copyright (C) 2017-2019 Joachim Stolberg
LGPLv2.1+
See LICENSE.txt for more information
Booking/ticket machine
]]--
-- for lazy programmers
local SP = function(pos) if pos then return minetest.pos_to_string(pos) end end
local P = minetest.string_to_pos
local M = minetest.get_meta
-- Load support for intllib.
local S = hyperloop.S
-- Used to store the Station list for each booking machine:
-- tStationList[SP(pos)] = {pos1, pos2, ...}
local tStationList = {}
local Stations = hyperloop.Stations
-- Form spec for the station list
local function generate_string(sortedList)
local tRes = {"size[12,8]"..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
"item_image[0,0;1,1;hyperloop:booking]"..
"label[4,0; "..S("Select your destination").."]"}
tRes[2] = "tablecolumns[text,width=20;text,width=6,align=right;text]"
local stations = {}
for idx,tDest in ipairs(sortedList) do
local name = tDest.name or S("<unknown>")
local distance = tDest.distance or 0
local info = tDest.booking_info or ""
stations[#stations+1] = minetest.formspec_escape(string.sub(name, 1, 28))
stations[#stations+1] = distance.."m"
stations[#stations+1] = minetest.formspec_escape(info)
end
if #stations>0 then
tRes[#tRes+1] = "table[0,1;11.8,7.2;button;"..table.concat(stations, ",").."]"
else
tRes[#tRes+1] = "button_exit[4,4;3,1;button;Update]"
end
return table.concat(tRes)
end
local function store_station_list(pos, sortedList)
local tbl = {}
for idx,item in ipairs(sortedList) do
tbl[#tbl+1] = item.pos
end
tStationList[SP(pos)] = tbl
end
local function remove_junctions(sortedList)
local tbl = {}
for idx,item in ipairs(sortedList) do
if not item.junction and item.booking_pos then
tbl[#tbl+1] = item
end
end
return tbl
end
local function filter_subnet(sortedList, subnet)
if hyperloop.subnet_enabled then
if subnet == "" then
subnet = nil
end
local tbl = {}
for idx,item in ipairs(sortedList) do
if item.subnet == subnet then
tbl[#tbl+1] = item
end
end
return tbl
end
return sortedList
end
-- Used to update the station list for booking machine
-- and teleport list.
local function station_list_as_string(pos, subnet)
-- Generate a name sorted list of all connected stations
local sortedList = Stations:station_list(pos, pos, "name")
-- remove all junctions from the list
sortedList = remove_junctions(sortedList)
-- use subnet pattern to reduce the list
sortedList = filter_subnet(sortedList, subnet)
-- store the list for later use
store_station_list(pos, sortedList)
-- Generate the formspec string
return generate_string(sortedList)
end
local naming_formspec
if hyperloop.subnet_enabled then
naming_formspec = function(pos)
local meta = M(pos)
local formspec = "size[7,5.4]"..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
"label[0,0;"..S("Please enter the station name to\nwhich this booking machine belongs.").."]" ..
"field[0.2,1.5;7.1,1;name;"..S("Station name")..";MyTown]" ..
"field[0.2,2.7;7.1,1;info;"..S("Additional station information")..";]" ..
"field[0.2,3.9;7.1,1;subnet;"..S("Subnet name (optional)")..";]" ..
"button_exit[2.5,4.7;2,1;exit;Save]"
meta:set_string("formspec", formspec)
meta:set_int("change_counter", 0)
end
else
naming_formspec = function(pos)
local meta = M(pos)
local formspec = "size[7,4.4]"..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
"label[0,0;"..S("Please enter the station name to\nwhich this booking machine belongs.").."]" ..
"field[0.2,1.5;7.1,1;name;"..S("Station name")..";MyTown]" ..
"field[0.2,2.7;7.1,1;info;"..S("Additional station information")..";]" ..
"button_exit[2.5,3.7;2,1;exit;Save]"
meta:set_string("formspec", formspec)
meta:set_int("change_counter", 0)
end
end
local function booking_machine_update(pos)
local meta = M(pos)
local sStationPos = meta:get_string("sStationPos")
if sStationPos ~= "" then
local station_pos = P(sStationPos)
local counter = meta:get_int("change_counter") or 0
local changed, newcounter = Stations:changed(counter)
if changed or not tStationList[sStationPos] then
local subnet = meta:get_string("subnet")
meta:set_string("formspec", station_list_as_string(station_pos, subnet))
meta:set_int("change_counter", newcounter)
end
end
end
local function on_rightclick(pos)
booking_machine_update(pos)
end
local function on_receive_fields(pos, formname, fields, player)
-- station name entered?
if fields.name ~= nil then
local station_name = string.trim(fields.name)
if station_name == "" then
return
end
local stationPos = Stations:get_next_station(pos)
if stationPos then
if Stations:get(stationPos).booking_pos then
hyperloop.chat(player, S("Station has already a booking machine!"))
return
end
-- add subnet name if available
local subnet = string.trim(fields.subnet or "")
if subnet == "" then
subnet = nil
end
-- store meta and generate station formspec
Stations:update(stationPos, {
name = station_name,
booking_pos = pos,
booking_info = string.trim(fields.info),
subnet = subnet,
})
local meta = M(pos)
meta:set_string("sStationPos", SP(stationPos))
meta:set_string("infotext", "Station: "..station_name)
meta:set_string("subnet", string.trim(fields.subnet or ""))
meta:set_int("change_counter", 0) -- force update
booking_machine_update(pos)
else
hyperloop.chat(player, S("Invalid station name!"))
end
elseif fields.button ~= nil then -- destination selected?
local te = minetest.explode_table_event(fields.button)
local idx = tonumber(te.row)
if idx and te.type=="CHG" then
local tStation, src_pos = hyperloop.get_base_station(pos)
local dest_pos = tStationList[SP(src_pos)] and tStationList[SP(src_pos)][idx]
if dest_pos and tStation then
-- place booking if not already blocked
if hyperloop.reserve(src_pos, dest_pos, player) then
hyperloop.set_arrival(src_pos, dest_pos)
-- open the pod door
hyperloop.open_pod_door(tStation)
end
else
-- data is corrupt, try an update
M(pos):set_int("change_counter", 0)
end
minetest.close_formspec(player:get_player_name(), formname)
end
end
end
local function on_destruct(pos)
local sStationPos = M(pos):get_string("sStationPos")
if sStationPos ~= "" then
Stations:update(P(sStationPos), {
booking_pos = "nil",
booking_info = "nil",
name = "Station",
})
end
end
-- wap from wall to ground
local function swap_node(pos, placer)
pos.y = pos.y - 1
if minetest.get_node_or_nil(pos).name ~= "air" then
local node = minetest.get_node(pos)
node.name = "hyperloop:booking_ground"
node.param2 = hyperloop.get_facedir(placer)
pos.y = pos.y + 1
minetest.swap_node(pos, node)
else
pos.y = pos.y + 1
end
end
-- wall mounted booking machine
minetest.register_node("hyperloop:booking", {
description = S("Hyperloop Booking Machine"),
tiles = {
-- up, down, right, left, back, front
"hyperloop_booking.png",
"hyperloop_booking.png",
"hyperloop_booking.png",
"hyperloop_booking.png",
"hyperloop_booking.png",
"hyperloop_booking_front.png",
},
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{ -8/16, -8/16, 2/16, 8/16, 8/16, 8/16},
},
},
after_place_node = function(pos, placer, itemstack, pointed_thing)
naming_formspec(pos)
swap_node(pos, placer)
end,
on_rotate = screwdriver.disallow,
on_receive_fields = on_receive_fields,
on_destruct = on_destruct,
on_rightclick = on_rightclick,
paramtype = 'light',
light_source = 2,
paramtype2 = "facedir",
groups = {cracky=2},
is_ground_content = false,
})
-- ground mounted booking machine
minetest.register_node("hyperloop:booking_ground", {
description = S("Hyperloop Booking Machine"),
tiles = {
-- up, down, right, left, back, front
"hyperloop_booking.png",
"hyperloop_booking.png",
"hyperloop_booking.png",
"hyperloop_booking.png",
"hyperloop_booking.png",
"hyperloop_booking_front.png",
},
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{ -8/16, -8/16, -3/16, 8/16, 8/16, 3/16},
},
},
after_place_node = function(pos, placer, itemstack, pointed_thing)
naming_formspec(pos)
end,
on_receive_fields = on_receive_fields,
on_destruct = on_destruct,
on_rightclick = on_rightclick,
on_rotate = screwdriver.disallow,
drop = "hyperloop:booking",
light_source = 2,
paramtype = 'light',
paramtype2 = "facedir",
groups = {cracky=2, not_in_creative_inventory=1},
is_ground_content = false,
})
@@ -0,0 +1,270 @@
A
_a_
7
B
_b_
5
C
_c_
6
D
_d_
6
E
_e_
5
F
_f_
5
G
_g_
6
H
_h_
6
I
_i_
1
J
_j_
4
K
_k_
5
L
_l_
4
M
_m_
7
N
_n_
6
O
_o_
6
P
_p_
5
Q
_q_
7
R
_r_
5
S
_s_
5
T
_t_
5
U
_u_
6
V
_v_
7
W
_w_
9
X
_x_
5
Y
_y_
7
Z
_z_
5
a
_a
5
b
_b
5
c
_c
4
d
_d
5
e
_e
4
f
_f
4
g
_g
5
h
_h
5
i
_i
1
j
_j
1
k
_k
4
l
_l
1
m
_m
7
n
_n
5
o
_o
5
p
_p
5
q
_q
5
r
_r
3
s
_s
4
t
_t
3
u
_u
4
v
_v
5
w
_w
7
x
_x
5
y
_y
4
z
_z
4
_sp
2
0
_0
4
1
_1
2
2
_2
4
3
_3
4
4
_4
4
5
_5
4
6
_6
4
7
_7
4
8
_8
4
9
_9
4
(
_bl
2
)
_br
2
{
_cl
3
}
_cr
3
[
_sl
2
]
_sr
2
!
_ex
1
?
_qu
4
#
_hs
5
$
_dl
4
%
_pr
5
^
_ca
3
_
_un
3
+
_ps
3
-
_mn
3
=
_eq
3
;
_sm
1
:
_co
1
,
_cm
2
"
_qo
3
/
_dv
5
~
_tl
4
<
_lt
3
>
_gt
3
\
_re
5
|
_vb
1
.
_dt
1
@@ -0,0 +1,42 @@
--[[
Hyperloop Mod
=============
Copyright (C) 2017-2019 Joachim Stolberg
LGPLv2.1+
See LICENSE.txt for more information
mod storage and data integrity
]]--
-- for lazy programmers
--local P = minetest.string_to_pos
--local M = minetest.get_meta
hyperloop.Stations = hyperloop.Network:new()
hyperloop.Elevators = hyperloop.Network:new()
local storage = minetest.get_mod_storage()
hyperloop.Stations:deserialize(storage:get_string("Stations"))
hyperloop.Elevators:deserialize(storage:get_string("Elevators"))
local function update_mod_storage()
minetest.log("action", "[Hyperloop] Store data...")
storage:set_string("Stations", hyperloop.Stations:serialize())
storage:set_string("Elevators", hyperloop.Elevators:serialize())
-- store data each hour
minetest.after(60*60, update_mod_storage)
minetest.log("action", "[Hyperloop] Data stored")
end
minetest.register_on_shutdown(function()
update_mod_storage()
end)
-- delete data base entries without corresponding nodes
--minetest.after(5, check_data_base)
-- store data after one hour
minetest.after(60*60, update_mod_storage)
+241
View File
@@ -0,0 +1,241 @@
--[[
Hyperloop Mod
=============
Copyright (C) 2017 Joachim Stolberg
LGPLv2.1+
See LICENSE.txt for more information
History:
see init.lua
]]--
-- Load support for intllib.
local S = hyperloop.S
local tilesL = {"hyperloop_alpsL.png", "hyperloop_seaL.png", "hyperloop_agyptL.png"}
local tilesR = {"hyperloop_alpsR.png", "hyperloop_seaR.png", "hyperloop_agyptR.png"}
-- determine facedir and pos on the right hand side from the given pos
local function right_hand_side(pos, placer)
local facedir = hyperloop.get_facedir(placer)
pos = hyperloop.new_pos(pos, facedir, "1R", 0)
return facedir,pos
end
for idx = 1,3 do
minetest.register_node("hyperloop:poster"..idx.."L", {
description = S("Hyperloop Promo Poster ")..idx,
tiles = {
-- up, down, right, left, back, front
"hyperloop_skin2.png",
"hyperloop_skin2.png",
"hyperloop_skin2.png",
"hyperloop_skin2.png",
"hyperloop_skin2.png",
tilesL[idx],
},
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{ -8/16, -8/16, -6/16, 8/16, 8/16, 8/16},
},
},
selection_box = {
type = "fixed",
fixed = { -8/16, -8/16, -6/16, 24/16, 8/16, 8/16},
},
after_place_node = function(pos, placer)
local meta = minetest.get_meta(pos)
local facedir
facedir, pos = right_hand_side(pos, placer)
meta:set_string("pos", minetest.pos_to_string(pos))
if minetest.get_node_or_nil(pos).name == "air" then
minetest.add_node(pos, {name="hyperloop:poster"..idx.."R", param2=facedir})
end
end,
on_destruct = function(pos)
local meta = minetest.get_meta(pos)
pos = minetest.string_to_pos(meta:get_string("pos"))
if pos ~= nil and minetest.get_node_or_nil(pos).name == "hyperloop:poster"..idx.."R" then
minetest.remove_node(pos)
end
end,
paramtype2 = "facedir",
light_source = 4,
is_ground_content = false,
groups = {cracky = 2, stone = 2},
})
minetest.register_node("hyperloop:poster"..idx.."R", {
description = S("Hyperloop Promo Poster ")..idx,
tiles = {
-- up, down, right, left, back, front
"hyperloop_skin2.png",
"hyperloop_skin2.png",
"hyperloop_skin2.png",
"hyperloop_skin2.png",
"hyperloop_skin2.png",
tilesR[idx],
},
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{ -8/16, -8/16, -6/16, 8/16, 8/16, 8/16},
},
},
paramtype2 = "facedir",
light_source = 4,
is_ground_content = false,
groups = {cracky = 2, stone = 2, not_in_creative_inventory=1},
})
end
minetest.register_node("hyperloop:sign", {
description = S("Hyperloop Station Sign"),
tiles = {
-- up, down, right, left, back, front
"hyperloop_sign_top.png",
"hyperloop_sign.png",
},
light_source = 4,
is_ground_content = false,
groups = {cracky = 2, stone = 2},
})
minetest.register_node("hyperloop:signR", {
description = S("Hyperloop Station Sign Right"),
tiles = {
-- up, down, right, left, back, front
"hyperloop_sign3.png^[transformR90]",
"hyperloop_sign3.png^[transformR90]",
"hyperloop_sign3.png",
"hyperloop_sign3.png",
"hyperloop_sign2.png^[transformFX",
"hyperloop_sign2.png",
},
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{ -8/16, -5/16, 6/16, 8/16, 5/16, 8/16},
},
},
after_place_node = function(pos, placer, itemstack, pointed_thing)
pos.y = pos.y - 1
if minetest.get_node_or_nil(pos).name ~= "air" then
local node = minetest.get_node(pos)
node.name = "hyperloop:signR_ground"
node.param2 = hyperloop.get_facedir(placer)
pos.y = pos.y + 1
minetest.swap_node(pos, node)
end
end,
paramtype2 = "facedir",
paramtype = 'light',
light_source = 4,
is_ground_content = false,
groups = {cracky = 2, stone = 2},
})
minetest.register_node("hyperloop:signL", {
description = S("Hyperloop Station Sign Left"),
tiles = {
-- up, down, right, left, back, front
"hyperloop_sign3.png^[transformR90]",
"hyperloop_sign3.png^[transformR90]",
"hyperloop_sign3.png",
"hyperloop_sign3.png",
"hyperloop_sign2.png",
"hyperloop_sign2.png^[transformFX",
},
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{ -8/16, -5/16, 6/16, 8/16, 5/16, 8/16},
},
},
after_place_node = function(pos, placer, itemstack, pointed_thing)
pos.y = pos.y - 1
if minetest.get_node_or_nil(pos).name ~= "air" then
local node = minetest.get_node(pos)
node.name = "hyperloop:signL_ground"
node.param2 = hyperloop.get_facedir(placer)
pos.y = pos.y + 1
minetest.swap_node(pos, node)
end
end,
paramtype2 = "facedir",
paramtype = 'light',
light_source = 4,
is_ground_content = false,
groups = {cracky = 2, stone = 2},
})
minetest.register_node("hyperloop:signR_ground", {
description = S("Hyperloop Station Sign Right"),
tiles = {
-- up, down, right, left, back, front
"hyperloop_sign3.png^[transformR90]",
"hyperloop_sign3.png^[transformR90]",
"hyperloop_sign3.png",
"hyperloop_sign3.png",
"hyperloop_sign2_ground.png^[transformFX",
"hyperloop_sign2_ground.png",
},
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{ -8/16, -8/16, -1/16, 8/16, 2/16, 1/16},
},
},
paramtype2 = "facedir",
drop = "hyperloop:signR",
paramtype = 'light',
light_source = 4,
is_ground_content = false,
groups = {cracky = 2, stone = 2, not_in_creative_inventory=1},
})
minetest.register_node("hyperloop:signL_ground", {
description = S("Hyperloop Station Sign Left"),
tiles = {
-- up, down, right, left, back, front
"hyperloop_sign3.png^[transformR90]",
"hyperloop_sign3.png^[transformR90]",
"hyperloop_sign3.png",
"hyperloop_sign3.png",
"hyperloop_sign2_ground.png",
"hyperloop_sign2_ground.png^[transformFX",
},
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{ -8/16, -8/16, -1/16, 8/16, 2/16, 1/16},
},
},
paramtype2 = "facedir",
drop = "hyperloop:signL",
paramtype = 'light',
light_source = 4,
is_ground_content = false,
groups = {cracky = 2, stone = 2, not_in_creative_inventory=1},
})
+202
View File
@@ -0,0 +1,202 @@
--[[
Hyperloop Mod
=============
Copyright (C) 2017-2019 Joachim Stolberg
LGPLv2.1+
See LICENSE.txt for more information
]]--
-- for lazy programmers
--local SP = function(pos) if pos then return minetest.pos_to_string(pos) end end
--local P = minetest.string_to_pos
local M = minetest.get_meta
--- Load support for intllib.
local S = hyperloop.S
-- Open the door for an emergency
local function door_on_punch(pos, node, puncher, pointed_thing)
local station = hyperloop.get_base_station(pos)
if station then
if station.name == "Station" then
hyperloop.chat(puncher, S("The Booking Machine for this station is missing!"))
elseif not hyperloop.is_blocked(station.pos) then
hyperloop.open_pod_door(station)
end
end
end
-- Open/close/animate the pod door
-- door_pos1: position of the bottom door
-- cmnd: "close", "open", or "animate"
local function door_command(door_pos1, facedir, cmnd)
-- one step up
local door_pos2 = vector.add(door_pos1, {x=0, y=1, z=0})
local node1 = minetest.get_node(door_pos1)
local node2 = minetest.get_node(door_pos2)
if cmnd == "open" then
minetest.sound_play("door", {
pos = door_pos1,
gain = 0.5,
max_hear_distance = 10,
})
node1.name = "air"
minetest.swap_node(door_pos1, node1)
node2.name = "air"
minetest.swap_node(door_pos2, node2)
elseif cmnd == "close" then
minetest.sound_play("door", {
pos = door_pos1,
gain = 0.5,
max_hear_distance = 10,
})
node1.name = "hyperloop:doorBottom"
node1.param2 = facedir
minetest.swap_node(door_pos1, node1)
node2.name = "hyperloop:doorTopPassive"
node2.param2 = facedir
minetest.swap_node(door_pos2, node2)
elseif cmnd == "animate" then
node2.name = "hyperloop:doorTopActive"
node2.param2 = facedir
minetest.swap_node(door_pos2, node2)
end
end
-- door command based on the station data table
function hyperloop.open_pod_door(tStation)
if tStation ~= nil then
tStation.facedir = tStation.facedir or 0
local door_pos = hyperloop.new_pos(tStation.pos, tStation.facedir, "1F1L", 1)
local door_facedir = (tStation.facedir + 1) % 4
door_command(door_pos, door_facedir, "open")
end
end
-- door command based on the station data table
function hyperloop.close_pod_door(tStation)
if tStation ~= nil then
tStation.facedir = tStation.facedir or 0
local door_pos = hyperloop.new_pos(tStation.pos, tStation.facedir, "1F1L", 1)
local door_facedir = (tStation.facedir + 1) % 4
door_command(door_pos, door_facedir, "close")
end
end
-- door command based on the station data table
function hyperloop.animate_pod_door(tStation)
if tStation ~= nil then
tStation.facedir = tStation.facedir or 0
local door_pos = hyperloop.new_pos(tStation.pos, tStation.facedir, "1F1L", 1)
local door_facedir = (tStation.facedir + 1) % 4
door_command(door_pos, door_facedir, "animate")
end
end
minetest.register_node("hyperloop:doorTopPassive", {
description = S("Hyperloop Door Top"),
tiles = {
-- up, down, right, left, back, front
"hyperloop_skin_door.png",
"hyperloop_skin_door.png",
"hyperloop_skin_door.png",
"hyperloop_skin_door.png",
"hyperloop_door1OUT.png",
"hyperloop_door1OUT.png",
},
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {-8/16, -8/16, -5/16, 8/16, 8/16, 5/16},
},
on_punch = door_on_punch,
auto_place_node = function(pos, facedir, sStationPos)
M(pos):set_int("facedir", facedir)
M(pos):set_string("sStationPos", sStationPos)
end,
on_rotate = screwdriver.disallow,
paramtype = 'light',
light_source = 1,
paramtype2 = "facedir",
drop = "",
sounds = default.node_sound_metal_defaults(),
groups = {cracky=1, not_in_creative_inventory=1},
is_ground_content = false,
})
minetest.register_node("hyperloop:doorTopActive", {
description = S("Hyperloop Door Top"),
tiles = {
-- up, down, right, left, back, front
"hyperloop_skin_door.png",
"hyperloop_skin_door.png",
"hyperloop_skin_door.png",
"hyperloop_skin_door.png",
{
name = "hyperloop_door1IN.png",
animation = {
type = "vertical_frames",
aspect_w = 32,
aspect_h = 32,
length = 1.0,
},
},
"hyperloop_door1OUT.png",
},
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {-8/16, -8/16, -5/16, 8/16, 8/16, 5/16},
},
on_rotate = screwdriver.disallow,
paramtype2 = "facedir",
drop = "",
light_source = 2,
sounds = default.node_sound_metal_defaults(),
groups = {cracky=1, not_in_creative_inventory=1},
is_ground_content = false,
})
minetest.register_node("hyperloop:doorBottom", {
description = S("Hyperloop Door Bottom"),
tiles = {
-- up, down, right, left, back, front
"hyperloop_skin_door.png",
"hyperloop_skin_door.png",
"hyperloop_skin_door.png",
"hyperloop_skin_door.png",
"hyperloop_door2IN.png",
"hyperloop_door2OUT.png",
},
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {-8/16, -8/16, -5/16, 8/16, 8/16, 5/16},
},
on_punch = door_on_punch,
auto_place_node = function(pos, facedir, sStationPos)
M(pos):set_int("facedir", facedir)
M(pos):set_string("sStationPos", sStationPos)
end,
on_rotate = screwdriver.disallow,
paramtype = 'light',
light_source = 1,
paramtype2 = "facedir",
drop = "",
sounds = default.node_sound_metal_defaults(),
groups = {cracky=1, not_in_creative_inventory=1},
is_ground_content = false,
})
+682
View File
@@ -0,0 +1,682 @@
--[[
Hyperloop Mod
=============
Copyright (C) 2017-2019 Joachim Stolberg
LGPLv2.1+
See LICENSE.txt for more information
]]--
-- for lazy programmers
local SP = function(pos) if pos then return minetest.pos_to_string(pos) end end
local P = minetest.string_to_pos
local M = minetest.get_meta
-- Load support for intllib.
local S = hyperloop.S
-- To store elevator floors and formspecs
local Cache = {}
local PlayerNameTags = {}
local kPLAYER_OVER_GROUND = 0.5
-------------------------------------------------------------------------------
-- Elevator Shaft
-------------------------------------------------------------------------------
-- Down, Up
local dirs_to_check = {5,6} -- vertical only
if hyperloop.free_tube_placement_enabled then
dirs_to_check = {1,2,3,4,5,6} -- all directions
end
local Shaft = tubelib2.Tube:new({
dirs_to_check = dirs_to_check,
max_tube_length = 1000,
show_infotext = true,
primary_node_names = {"hyperloop:shaft", "hyperloop:shaft2", "hyperloop:shaftA", "hyperloop:shaftA2"},
secondary_node_names = {"hyperloop:elevator_bottom", "hyperloop:elevator_top"},
after_place_tube = function(pos, param2, tube_type, num_tubes)
if tube_type == "S" then
if num_tubes == 2 then
minetest.swap_node(pos, {name = "hyperloop:shaft2", param2 = param2})
else
minetest.swap_node(pos, {name = "hyperloop:shaft", param2 = param2})
end
else
if num_tubes == 2 then
minetest.swap_node(pos, {name = "hyperloop:shaftA2", param2 = param2})
else
minetest.swap_node(pos, {name = "hyperloop:shaftA", param2 = param2})
end
end
end,
})
hyperloop.Shaft = Shaft
local Elevators = hyperloop.Elevators
Shaft:register_on_tube_update(function(node, pos, out_dir, peer_pos, peer_in_dir)
if out_dir == 6 then -- to the top?
-- switch to elevator_bottom node
pos = Shaft:get_pos(pos, 5)
elseif peer_pos then
local _,node1 = Shaft:get_node(peer_pos)
if node1.name == "hyperloop:elevator_top" then
peer_pos = Shaft:get_pos(peer_pos, 5)
end
end
Elevators:update_connections(pos, out_dir, peer_pos)
end)
minetest.register_node("hyperloop:shaft", {
description = S("Hyperloop Elevator Shaft"),
inventory_image = 'hyperloop_shaft_inv.png',
tiles = {
-- up, down, right, left, back, front
"hyperloop_tube_closed.png^[transformR90]",
"hyperloop_tube_closed.png^[transformR90]",
"hyperloop_tube_closed.png",
"hyperloop_tube_closed.png",
'hyperloop_tube.png',
'hyperloop_tube.png',
},
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{-8/16, -8/16, -8/16, -7/16, 8/16, 8/16},
{ 7/16, -8/16, -8/16, 8/16, 8/16, 8/16},
{-8/16, 7/16, -8/16, 8/16, 8/16, 8/16},
{-8/16, -8/16, -8/16, 8/16, -7/16, 8/16},
},
},
selection_box = {
type = "fixed",
fixed = {-8/16, -8/16, -8/16, 8/16, 8/16, 8/16},
},
after_place_node = function(pos, placer, itemstack, pointed_thing)
if not Shaft:after_place_tube(pos, placer, pointed_thing) then
minetest.remove_node(pos)
return true
end
return false
end,
after_dig_node = function(pos, oldnode, oldmetadata, digger)
Shaft:after_dig_tube(pos, oldnode, oldmetadata)
end,
climbable = true,
paramtype2 = "facedir",
on_rotate = screwdriver.disallow,
paramtype = "light",
light_source = 2,
sunlight_propagates = true,
is_ground_content = false,
groups = {cracky = 1},
sounds = default.node_sound_metal_defaults(),
})
minetest.register_node("hyperloop:shaftA", {
description = S("Hyperloop Elevator Shaft"),
tiles = {
-- up, down, right, left, back, front
"hyperloop_tube_closed.png^[transformR90]",
'hyperloop_tube.png',
"hyperloop_tube_closed.png",
"hyperloop_tube_closed.png",
"hyperloop_tube_closed.png",
'hyperloop_tube.png',
},
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{-8/16, -8/16, -8/16, -7/16, 8/16, 8/16},
{ 7/16, -8/16, -8/16, 8/16, 8/16, 8/16},
{-8/16, 7/16, -8/16, 8/16, 8/16, 8/16},
{-8/16, -8/16, 7/16, 8/16, 8/16, 8/16},
{-8/16, -8/16, -8/16, 8/16, -7/16, -7/16},
},
},
selection_box = {
type = "fixed",
fixed = {-8/16, -8/16, -8/16, 8/16, 8/16, 8/16},
},
after_place_node = function(pos, placer, itemstack, pointed_thing)
if not Shaft:after_place_tube(pos, placer, pointed_thing) then
minetest.remove_node(pos)
return true
end
return false
end,
after_dig_node = function(pos, oldnode, oldmetadata, digger)
Shaft:after_dig_tube(pos, oldnode, oldmetadata)
end,
climbable = true,
paramtype2 = "facedir",
on_rotate = screwdriver.disallow,
paramtype = "light",
light_source = 2,
sunlight_propagates = true,
is_ground_content = false,
groups = {cracky = 1, not_in_creative_inventory=1},
drop = "hyperloop:shaft",
sounds = default.node_sound_metal_defaults(),
})
minetest.register_node("hyperloop:shaft2", {
description = S("Hyperloop Elevator Shaft"),
tiles = {
-- up, down, right, left, back, front
"hyperloop_tube_locked.png^hyperloop_elogo.png^[transformR270]",
"hyperloop_tube_locked.png^hyperloop_elogo.png^[transformR90]",
"hyperloop_tube_locked.png^hyperloop_elogo.png^[transformR180]",
"hyperloop_tube_locked.png^hyperloop_elogo.png",
'hyperloop_tube.png',
'hyperloop_tube.png',
},
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{-8/16, -8/16, -8/16, -7/16, 8/16, 8/16},
{ 7/16, -8/16, -8/16, 8/16, 8/16, 8/16},
{-8/16, 7/16, -8/16, 8/16, 8/16, 8/16},
{-8/16, -8/16, -8/16, 8/16, -7/16, 8/16},
},
},
selection_box = {
type = "fixed",
fixed = {-8/16, -8/16, -8/16, 8/16, 8/16, 8/16},
},
after_dig_node = function(pos, oldnode, oldmetadata, digger)
Shaft:after_dig_tube(pos, oldnode, oldmetadata)
end,
climbable = true,
paramtype2 = "facedir",
on_rotate = screwdriver.disallow,
paramtype = "light",
light_source = 2,
sunlight_propagates = true,
is_ground_content = false,
----- To be unbreakable -----
on_blast = function() end,
on_destruct = function () end,
can_dig = function() return false end,
diggable = false,
groups = {cracky = 1, not_in_creative_inventory=1, unbreakable=1},
sounds = default.node_sound_metal_defaults(),
})
minetest.register_node("hyperloop:shaftA2", {
description = S("Hyperloop Elevator Shaft"),
tiles = {
-- up, down, right, left, back, front
"hyperloop_tube_locked.png^hyperloop_elogo.png^[transformR270]",
'hyperloop_tube.png',
"hyperloop_tube_locked.png^hyperloop_elogo.png^[transformR180]",
"hyperloop_tube_locked.png^hyperloop_elogo.png^[transformR180]",
"hyperloop_tube_locked.png^hyperloop_elogo.png^[transformR90]",
'hyperloop_tube.png',
},
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{-8/16, -8/16, -8/16, -7/16, 8/16, 8/16},
{ 7/16, -8/16, -8/16, 8/16, 8/16, 8/16},
{-8/16, 7/16, -8/16, 8/16, 8/16, 8/16},
{-8/16, -8/16, 7/16, 8/16, 8/16, 8/16},
{-8/16, -8/16, -8/16, 8/16, -7/16, -7/16},
},
},
selection_box = {
type = "fixed",
fixed = {-8/16, -8/16, -8/16, 8/16, 8/16, 8/16},
},
after_dig_node = function(pos, oldnode, oldmetadata, digger)
Shaft:after_dig_tube(pos, oldnode, oldmetadata)
end,
climbable = true,
paramtype2 = "facedir",
on_rotate = screwdriver.disallow,
paramtype = "light",
light_source = 2,
sunlight_propagates = true,
is_ground_content = false,
----- To be unbreakable -----
on_blast = function() end,
on_destruct = function () end,
can_dig = function() return false end,
diggable = false,
groups = {cracky = 1, not_in_creative_inventory=1, unbreakable=1},
sounds = default.node_sound_metal_defaults(),
})
-------------------------------------------------------------------------------
-- Elevator Car
-------------------------------------------------------------------------------
-- Form spec for the floor list
local function formspec(pos, lFloors)
local tRes = {"size[6,10]label[0.5,0; "..S("Select your destination").."]"}
tRes[2] = "label[0.5,0.6;"..S("Destination").."]label[2,0.6;"..S("Floor").."]"
if #lFloors == 0 then
tRes[#tRes+1] = "button_exit[1,3;3,1;button;Update]"
elseif #lFloors < 10 then
for idx,floor in ipairs(lFloors) do
local ypos = 0.5 + idx*0.8
local ypos2 = ypos - 0.2
tRes[#tRes+1] = "button_exit[0.5,"..ypos2..";1,1;button;"..(#lFloors-idx).."]"
if vector.equals(floor.pos, pos) then
tRes[#tRes+1] = "label[2,"..ypos..";"..S("(current position)").."]"
else
tRes[#tRes+1] = "label[2,"..ypos..";"..(floor.name or "<unknown>").."]"
end
end
else
tRes[3] = "scrollbaroptions[smallstep=100;largestep=200]"
tRes[4] = "scrollbar[5.3,1.5;0.4,8.2;vertical;floors;0]"
tRes[5] = "scroll_container[0.5,2;5,9.5;floors;vertical;0.02]"
for idx,floor in ipairs(lFloors) do
local ypos = idx*0.8 - 0.5
local ypos2 = ypos - 0.2
tRes[#tRes+1] = "button_exit[0,"..ypos2..";1,1;button;"..(#lFloors-idx).."]"
if vector.equals(floor.pos, pos) then
tRes[#tRes+1] = "label[1.5,"..ypos..";"..S("(current position)").."]"
else
tRes[#tRes+1] = "label[1.5,"..ypos..";"..(floor.name or "<unknown>").."]"
end
end
tRes[#tRes+1] = "scroll_container_end[]"
end
return table.concat(tRes)
end
local function update_formspec(pos)
local meta = M(pos)
local counter = meta:get_int("change_counter") or 0
local changed, newcounter = Elevators:changed(counter)
local sKey = SP(pos)
if changed or not Cache[sKey] then
local lFloors = Elevators:station_list(pos, pos, "level")
Cache[sKey] = {}
Cache[sKey].lFloors = lFloors
Cache[sKey].formspec = formspec(pos, lFloors)
meta:set_int("change_counter", newcounter)
end
M(pos):set_string("formspec", Cache[sKey].formspec)
end
-- Open/close/darken the elevator door
-- floor_pos: position of elevator floor
-- cmnd: "close", "open", or "darken"
local function door_command(floor_pos, facedir, cmnd, sound)
-- one step up
local door_pos1 = hyperloop.new_pos(floor_pos, facedir, "1B", 0)
local door_pos2 = hyperloop.new_pos(floor_pos, facedir, "1B", 1)
local meta = M(floor_pos)
local owner = meta:contains("owner") and meta:get_string("owner")
if owner and (minetest.is_protected(door_pos1, owner) or minetest.is_protected(door_pos2, owner)) then
return
end
local node1 = minetest.get_node(door_pos1)
local node2 = minetest.get_node(door_pos2)
if sound then
minetest.sound_play("ele_door", {
pos = floor_pos,
gain = 0.8,
max_hear_distance = 10,
})
end
if cmnd == "open" then
node1.name = "air"
minetest.swap_node(door_pos1, node1)
node2.name = "air"
minetest.swap_node(door_pos2, node2)
elseif cmnd == "close" then
M(door_pos1):set_string("floor_pos", SP(floor_pos))
M(door_pos2):set_string("floor_pos", SP(floor_pos))
node1.name = "hyperloop:elevator_door"
node1.param2 = facedir
minetest.swap_node(door_pos1, node1)
node2.name = "hyperloop:elevator_door_top"
node2.param2 = facedir
minetest.swap_node(door_pos2, node2)
elseif cmnd == "darken" then
node1.name = "hyperloop:elevator_door_dark"
node1.param2 = facedir
minetest.swap_node(door_pos1, node1)
node2.name = "hyperloop:elevator_door_dark_top"
node2.param2 = facedir
minetest.swap_node(door_pos2, node2)
end
end
local function on_final_close_door(tArrival)
-- close the door and play sound if no player is around
if hyperloop.is_player_around(tArrival.pos) then
-- try again later
minetest.after(3.0, on_final_close_door, tArrival)
else
door_command(tArrival.pos, tArrival.facedir, "close", true)
end
end
local function on_open_door(tArrival)
door_command(tArrival.pos, tArrival.facedir, "open", true)
minetest.after(5.0, on_final_close_door, tArrival)
tArrival.busy = false
end
local function on_arrival_floor(tDeparture, tArrival, player_name, snd)
local player = minetest.get_player_by_name(player_name)
door_command(tDeparture.pos, tDeparture.facedir, "close", false)
door_command(tArrival.pos, tArrival.facedir, "close", false)
tDeparture.busy = false
if player ~= nil then
tArrival.pos.y = tArrival.pos.y - kPLAYER_OVER_GROUND
player:set_pos(tArrival.pos)
if PlayerNameTags[player_name] then
player:set_nametag_attributes(PlayerNameTags[player_name])
PlayerNameTags[player_name] = nil
end
tArrival.pos.y = tArrival.pos.y + kPLAYER_OVER_GROUND
end
minetest.sound_stop(snd)
minetest.after(1.0, on_open_door, tArrival)
end
local function on_travel(tDeparture, tArrival, player_name, seconds)
local player = minetest.get_player_by_name(player_name)
door_command(tDeparture.pos, tDeparture.facedir, "darken", false)
door_command(tArrival.pos, tArrival.facedir, "darken", false)
if player ~= nil then
PlayerNameTags[player_name] = player:get_nametag_attributes()
player:set_nametag_attributes({text = " "})
end
local snd = minetest.sound_play("ele_norm", {
pos = tDeparture.pos,
gain = 0.5,
max_hear_distance = 3,
loop = true,
})
minetest.after(seconds, on_arrival_floor, tDeparture, tArrival, player_name, snd)
end
minetest.register_node("hyperloop:elevator_bottom", {
description = S("Hyperloop Elevator"),
tiles = {
"hyperloop_elevator_bottom.png",
"hyperloop_elevator_bottom.png",
"hyperloop_elevator.png",
"hyperloop_elevator.png",
"hyperloop_elevator.png",
"hyperloop_elevator.png",
},
node_box = {
type = "fixed",
fixed = {
{ -8/16, -8/16, -8/16, -7/16, 8/16, 8/16},
{ 7/16, -8/16, -8/16, 8/16, 8/16, 8/16},
{ -7/16, -8/16, 7/16, 7/16, 8/16, 8/16},
{ -8/16, -8/16, -8/16, 8/16, -7/16, 8/16},
},
},
selection_box = {
type = "fixed",
fixed = { -8/16, -8/16, -8/16, 8/16, 23/16, 8/16 },
},
inventory_image = "hyperloop_elevator_inventory.png",
on_rotate = screwdriver.disallow,
drawtype = "nodebox",
paramtype = 'light',
light_source = 6,
paramtype2 = "facedir",
is_ground_content = false,
groups = {snappy = 3},
after_place_node = function(pos, placer, itemstack, pointed_thing)
local _,node = Shaft:get_node(pos, 6)
if node.name == "air" then
local facedir = hyperloop.get_facedir(placer)
Elevators:set(pos, "<unknown>", {facedir=facedir, busy=false})
Shaft:after_place_node(pos, {5})
-- formspec
local meta = minetest.get_meta(pos)
local fs = "size[6,4]"..
"label[0,0;"..S("Please insert floor name").."]" ..
"field[0.5,1.5;5,1;floor;"..S("Floor name")..";"..S("Base").."]" ..
"button_exit[2,3;2,1;exit;"..S("Save").."]"
meta:set_string("formspec", fs)
meta:set_string("owner", placer:get_player_name())
-- add upper part of the car
pos = Shaft:get_pos(pos, 6)
minetest.add_node(pos, {name="hyperloop:elevator_top", param2=facedir})
Shaft:after_place_node(pos, {6})
else
minetest.remove_node(pos)
return true
end
end,
on_receive_fields = function(pos, formname, fields, player)
-- floor name entered?
if fields.floor ~= nil then
local floor = string.trim(fields.floor)
if floor == "" then
return
end
Elevators:update(pos, {name=floor})
update_formspec(pos)
elseif fields.button ~= nil then -- destination selected?
update_formspec(pos)
local floor = Elevators:get(pos)
if floor then
floor = table.copy(floor)
floor.pos = pos
local sKey = SP(pos)
local idx = tonumber(fields.button)
if idx then
local lFloors = Cache[sKey].lFloors
local dest = lFloors[#lFloors-idx]
if dest and dest.pos and floor.pos then
local dist = hyperloop.distance(dest.pos, floor.pos)
if dist ~= 0 and floor.busy ~= true then
if player ~= nil then
pos.y = pos.y - kPLAYER_OVER_GROUND
player:set_pos(pos)
pos.y = pos.y + kPLAYER_OVER_GROUND
end
-- due to the missing display, a trip needs 20 sec maximum
local seconds = math.min(1 + math.floor(dist/30), 20)
floor.busy = true
door_command(floor.pos, floor.facedir, "close", true)
door_command(dest.pos, dest.facedir, "close", true)
minetest.after(1.0, on_travel, floor, dest, player:get_player_name(), seconds)
end
end
end
end
end
end,
on_punch = function(pos, node, puncher, pointed_thing)
update_formspec(pos)
local floor = Elevators:get(pos)
if floor and floor.busy ~= true then
door_command(pos, floor.facedir, "open", true)
end
end,
after_dig_node = function(pos, oldnode, oldmetadata, digger)
Shaft:after_dig_node(pos, {5})
Elevators:delete(pos)
-- remove the bottom also
pos = Shaft:get_pos(pos, 6)
minetest.remove_node(pos)
Shaft:after_dig_node(pos, {6})
end,
})
minetest.register_node("hyperloop:elevator_top", {
description = S("Hyperloop Elevator"),
tiles = {
-- up, down, right, left, back, front
"hyperloop_elevator_bottom.png",
"hyperloop_elevator_bottom.png",
"hyperloop_elevator_top.png",
"hyperloop_elevator.png",
"hyperloop_elevator.png",
"hyperloop_elevator.png",
},
node_box = {
type = "fixed",
fixed = {
{ -8/16, 7/16, -8/16, 8/16, 8/16, 8/16},
{ -8/16, -8/16, -8/16, -7/16, 8/16, 8/16},
{ 7/16, -8/16, -8/16, 8/16, 8/16, 8/16},
{ -7/16, -8/16, 7/16, 7/16, 8/16, 8/16},
},
},
on_rotate = screwdriver.disallow,
drawtype = "nodebox",
paramtype = 'light',
light_source = 6,
paramtype2 = "facedir",
is_ground_content = false,
diggable = false,
groups = {not_in_creative_inventory=1},
drop = "",
})
minetest.register_node("hyperloop:elevator_door_top", {
description = "Hyperloop Elevator Door",
tiles = {
-- up, down, right, left, back, front
"hyperloop_elevator_door_top.png",
},
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{ -8/16, -8/16, 7/16, 8/16, 8/16, 8/16},
},
},
on_rotate = screwdriver.disallow,
drop = "",
paramtype = 'light',
paramtype2 = "facedir",
use_texture_alpha = hyperloop.CLIP,
is_ground_content = false,
groups = {snappy = 3, not_in_creative_inventory=1},
})
minetest.register_node("hyperloop:elevator_door", {
description = "Hyperloop Elevator Door",
tiles = {
-- up, down, right, left, back, front
"hyperloop_elevator_door.png",
},
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{ -8/16, -8/16, 7/16, 8/16, 8/16, 8/16},
},
},
selection_box = {
type = "fixed",
fixed = { -8/16, -8/16, 6.5/16, 8/16, 24/16, 8/16 },
},
on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
local floor_pos = P(M(pos):get_string("floor_pos"))
if floor_pos ~= nil then
update_formspec(floor_pos)
local floor = Elevators:get(floor_pos)
if floor and floor.busy ~= true then
door_command(floor_pos, floor.facedir, "open", true)
end
end
end,
on_rotate = screwdriver.disallow,
drop = "",
paramtype = 'light',
paramtype2 = "facedir",
use_texture_alpha = hyperloop.CLIP,
is_ground_content = false,
groups = {snappy = 3, not_in_creative_inventory=1},
})
minetest.register_node("hyperloop:elevator_door_dark_top", {
description = "Hyperloop Elevator Door",
tiles = {
-- up, down, right, left, back, front
"hyperloop_elevator_dark_top.png",
},
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{ -8/16, -8/16, 7/16, 8/16, 8/16, 8/16},
},
},
on_rotate = screwdriver.disallow,
drop = "",
paramtype = 'light',
paramtype2 = "facedir",
is_ground_content = false,
groups = {snappy = 3, not_in_creative_inventory=1},
})
minetest.register_node("hyperloop:elevator_door_dark", {
description = "Hyperloop Elevator Door",
tiles = {
-- up, down, right, left, back, front
"hyperloop_elevator_dark.png",
},
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{ -8/16, -8/16, 7/16, 8/16, 8/16, 8/16},
},
},
selection_box = {
type = "fixed",
fixed = { -8/16, -8/16, 7/16, 8/16, 24/16, 8/16 },
},
on_rotate = screwdriver.disallow,
drop = "",
paramtype = 'light',
paramtype2 = "facedir",
is_ground_content = false,
groups = {snappy = 3, not_in_creative_inventory=1},
})
+458
View File
@@ -0,0 +1,458 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Script to generate the template file and update the translation files.
# Copy the script into the mod or modpack root folder and run it there.
#
# Copyright (C) 2019 Joachim Stolberg, 2020 FaceDeer, 2020 Louis Royer
# LGPLv2.1+
#
# See https://github.com/minetest-tools/update_translations for
# potential future updates to this script.
from __future__ import print_function
import os, fnmatch, re, shutil, errno
from sys import argv as _argv
from sys import stderr as _stderr
# Running params
params = {"recursive": False,
"help": False,
"mods": False,
"verbose": False,
"folders": [],
"no-old-file": False,
"break-long-lines": False,
"sort": False
}
# Available CLI options
options = {"recursive": ['--recursive', '-r'],
"help": ['--help', '-h'],
"mods": ['--installed-mods', '-m'],
"verbose": ['--verbose', '-v'],
"no-old-file": ['--no-old-file', '-O'],
"break-long-lines": ['--break-long-lines', '-b'],
"sort": ['--sort', '-s']
}
# Strings longer than this will have extra space added between
# them in the translation files to make it easier to distinguish their
# beginnings and endings at a glance
doublespace_threshold = 80
def set_params_folders(tab: list):
'''Initialize params["folders"] from CLI arguments.'''
# Discarding argument 0 (tool name)
for param in tab[1:]:
stop_param = False
for option in options:
if param in options[option]:
stop_param = True
break
if not stop_param:
params["folders"].append(os.path.abspath(param))
def set_params(tab: list):
'''Initialize params from CLI arguments.'''
for option in options:
for option_name in options[option]:
if option_name in tab:
params[option] = True
break
def print_help(name):
'''Prints some help message.'''
print(f'''SYNOPSIS
{name} [OPTIONS] [PATHS...]
DESCRIPTION
{', '.join(options["help"])}
prints this help message
{', '.join(options["recursive"])}
run on all subfolders of paths given
{', '.join(options["mods"])}
run on locally installed modules
{', '.join(options["no-old-file"])}
do not create *.old files
{', '.join(options["sort"])}
sort output strings alphabetically
{', '.join(options["break-long-lines"])}
add extra line breaks before and after long strings
{', '.join(options["verbose"])}
add output information
''')
def main():
'''Main function'''
set_params(_argv)
set_params_folders(_argv)
if params["help"]:
print_help(_argv[0])
elif params["recursive"] and params["mods"]:
print("Option --installed-mods is incompatible with --recursive")
else:
# Add recursivity message
print("Running ", end='')
if params["recursive"]:
print("recursively ", end='')
# Running
if params["mods"]:
print(f"on all locally installed modules in {os.path.abspath('~/.minetest/mods/')}")
run_all_subfolders("~/.minetest/mods")
elif len(params["folders"]) >= 2:
print("on folder list:", params["folders"])
for f in params["folders"]:
if params["recursive"]:
run_all_subfolders(f)
else:
update_folder(f)
elif len(params["folders"]) == 1:
print("on folder", params["folders"][0])
if params["recursive"]:
run_all_subfolders(params["folders"][0])
else:
update_folder(params["folders"][0])
else:
print("on folder", os.path.abspath("./"))
if params["recursive"]:
run_all_subfolders(os.path.abspath("./"))
else:
update_folder(os.path.abspath("./"))
#group 2 will be the string, groups 1 and 3 will be the delimiters (" or ')
#See https://stackoverflow.com/questions/46967465/regex-match-text-in-either-single-or-double-quote
pattern_lua_s = re.compile(r'[\.=^\t,{\(\s]N?S\(\s*(["\'])((?:\\\1|(?:(?!\1)).)*)(\1)[\s,\)]', re.DOTALL)
pattern_lua_fs = re.compile(r'[\.=^\t,{\(\s]N?FS\(\s*(["\'])((?:\\\1|(?:(?!\1)).)*)(\1)[\s,\)]', re.DOTALL)
pattern_lua_bracketed_s = re.compile(r'[\.=^\t,{\(\s]N?S\(\s*\[\[(.*?)\]\][\s,\)]', re.DOTALL)
pattern_lua_bracketed_fs = re.compile(r'[\.=^\t,{\(\s]N?FS\(\s*\[\[(.*?)\]\][\s,\)]', re.DOTALL)
# Handles "concatenation" .. " of strings"
pattern_concat = re.compile(r'["\'][\s]*\.\.[\s]*["\']', re.DOTALL)
pattern_tr = re.compile(r'(.*?[^@])=(.*)')
pattern_name = re.compile(r'^name[ ]*=[ ]*([^ \n]*)')
pattern_tr_filename = re.compile(r'\.tr$')
pattern_po_language_code = re.compile(r'(.*)\.po$')
#attempt to read the mod's name from the mod.conf file. Returns None on failure
def get_modname(folder):
try:
with open(os.path.join(folder, "mod.conf"), "r", encoding='utf-8') as mod_conf:
for line in mod_conf:
match = pattern_name.match(line)
if match:
return match.group(1)
except FileNotFoundError:
pass
return None
#If there are already .tr files in /locale, returns a list of their names
def get_existing_tr_files(folder):
out = []
for root, dirs, files in os.walk(os.path.join(folder, 'locale/')):
for name in files:
if pattern_tr_filename.search(name):
out.append(name)
return out
# A series of search and replaces that massage a .po file's contents into
# a .tr file's equivalent
def process_po_file(text):
# The first three items are for unused matches
text = re.sub(r'#~ msgid "', "", text)
text = re.sub(r'"\n#~ msgstr ""\n"', "=", text)
text = re.sub(r'"\n#~ msgstr "', "=", text)
# comment lines
text = re.sub(r'#.*\n', "", text)
# converting msg pairs into "=" pairs
text = re.sub(r'msgid "', "", text)
text = re.sub(r'"\nmsgstr ""\n"', "=", text)
text = re.sub(r'"\nmsgstr "', "=", text)
# various line breaks and escape codes
text = re.sub(r'"\n"', "", text)
text = re.sub(r'"\n', "\n", text)
text = re.sub(r'\\"', '"', text)
text = re.sub(r'\\n', '@n', text)
# remove header text
text = re.sub(r'=Project-Id-Version:.*\n', "", text)
# remove double-spaced lines
text = re.sub(r'\n\n', '\n', text)
return text
# Go through existing .po files and, if a .tr file for that language
# *doesn't* exist, convert it and create it.
# The .tr file that results will subsequently be reprocessed so
# any "no longer used" strings will be preserved.
# Note that "fuzzy" tags will be lost in this process.
def process_po_files(folder, modname):
for root, dirs, files in os.walk(os.path.join(folder, 'locale/')):
for name in files:
code_match = pattern_po_language_code.match(name)
if code_match == None:
continue
language_code = code_match.group(1)
tr_name = modname + "." + language_code + ".tr"
tr_file = os.path.join(root, tr_name)
if os.path.exists(tr_file):
if params["verbose"]:
print(f"{tr_name} already exists, ignoring {name}")
continue
fname = os.path.join(root, name)
with open(fname, "r", encoding='utf-8') as po_file:
if params["verbose"]:
print(f"Importing translations from {name}")
text = process_po_file(po_file.read())
with open(tr_file, "wt", encoding='utf-8') as tr_out:
tr_out.write(text)
# from https://stackoverflow.com/questions/600268/mkdir-p-functionality-in-python/600612#600612
# Creates a directory if it doesn't exist, silently does
# nothing if it already exists
def mkdir_p(path):
try:
os.makedirs(path)
except OSError as exc: # Python >2.5
if exc.errno == errno.EEXIST and os.path.isdir(path):
pass
else: raise
# Converts the template dictionary to a text to be written as a file
# dKeyStrings is a dictionary of localized string to source file sets
# dOld is a dictionary of existing translations and comments from
# the previous version of this text
def strings_to_text(dkeyStrings, dOld, mod_name, header_comments):
lOut = [f"# textdomain: {mod_name}\n"]
if header_comments is not None:
lOut.append(header_comments)
dGroupedBySource = {}
for key in dkeyStrings:
sourceList = list(dkeyStrings[key])
if params["sort"]:
sourceList.sort()
sourceString = "\n".join(sourceList)
listForSource = dGroupedBySource.get(sourceString, [])
listForSource.append(key)
dGroupedBySource[sourceString] = listForSource
lSourceKeys = list(dGroupedBySource.keys())
lSourceKeys.sort()
for source in lSourceKeys:
localizedStrings = dGroupedBySource[source]
if params["sort"]:
localizedStrings.sort()
lOut.append("")
lOut.append(source)
lOut.append("")
for localizedString in localizedStrings:
val = dOld.get(localizedString, {})
translation = val.get("translation", "")
comment = val.get("comment")
if params["break-long-lines"] and len(localizedString) > doublespace_threshold and not lOut[-1] == "":
lOut.append("")
if comment != None:
lOut.append(comment)
lOut.append(f"{localizedString}={translation}")
if params["break-long-lines"] and len(localizedString) > doublespace_threshold:
lOut.append("")
unusedExist = False
for key in dOld:
if key not in dkeyStrings:
val = dOld[key]
translation = val.get("translation")
comment = val.get("comment")
# only keep an unused translation if there was translated
# text or a comment associated with it
if translation != None and (translation != "" or comment):
if not unusedExist:
unusedExist = True
lOut.append("\n\n##### not used anymore #####\n")
if params["break-long-lines"] and len(key) > doublespace_threshold and not lOut[-1] == "":
lOut.append("")
if comment != None:
lOut.append(comment)
lOut.append(f"{key}={translation}")
if params["break-long-lines"] and len(key) > doublespace_threshold:
lOut.append("")
return "\n".join(lOut) + '\n'
# Writes a template.txt file
# dkeyStrings is the dictionary returned by generate_template
def write_template(templ_file, dkeyStrings, mod_name):
# read existing template file to preserve comments
existing_template = import_tr_file(templ_file)
text = strings_to_text(dkeyStrings, existing_template[0], mod_name, existing_template[2])
mkdir_p(os.path.dirname(templ_file))
with open(templ_file, "wt", encoding='utf-8') as template_file:
template_file.write(text)
# Gets all translatable strings from a lua file
def read_lua_file_strings(lua_file):
lOut = []
with open(lua_file, encoding='utf-8') as text_file:
text = text_file.read()
#TODO remove comments here
text = re.sub(pattern_concat, "", text)
strings = []
for s in pattern_lua_s.findall(text):
strings.append(s[1])
for s in pattern_lua_bracketed_s.findall(text):
strings.append(s)
for s in pattern_lua_fs.findall(text):
strings.append(s[1])
for s in pattern_lua_bracketed_fs.findall(text):
strings.append(s)
for s in strings:
s = re.sub(r'"\.\.\s+"', "", s)
s = re.sub("@[^@=0-9]", "@@", s)
s = s.replace('\\"', '"')
s = s.replace("\\'", "'")
s = s.replace("\n", "@n")
s = s.replace("\\n", "@n")
s = s.replace("=", "@=")
lOut.append(s)
return lOut
# Gets strings from an existing translation file
# returns both a dictionary of translations
# and the full original source text so that the new text
# can be compared to it for changes.
# Returns also header comments in the third return value.
def import_tr_file(tr_file):
dOut = {}
text = None
header_comment = None
if os.path.exists(tr_file):
with open(tr_file, "r", encoding='utf-8') as existing_file :
# save the full text to allow for comparison
# of the old version with the new output
text = existing_file.read()
existing_file.seek(0)
# a running record of the current comment block
# we're inside, to allow preceeding multi-line comments
# to be retained for a translation line
latest_comment_block = None
for line in existing_file.readlines():
line = line.rstrip('\n')
if line[:3] == "###":
if header_comment is None:
# Save header comments
header_comment = latest_comment_block
# Stip textdomain line
tmp_h_c = ""
for l in header_comment.split('\n'):
if not l.startswith("# textdomain:"):
tmp_h_c += l + '\n'
header_comment = tmp_h_c
# Reset comment block if we hit a header
latest_comment_block = None
continue
if line[:1] == "#":
# Save the comment we're inside
if not latest_comment_block:
latest_comment_block = line
else:
latest_comment_block = latest_comment_block + "\n" + line
continue
match = pattern_tr.match(line)
if match:
# this line is a translated line
outval = {}
outval["translation"] = match.group(2)
if latest_comment_block:
# if there was a comment, record that.
outval["comment"] = latest_comment_block
latest_comment_block = None
dOut[match.group(1)] = outval
return (dOut, text, header_comment)
# Walks all lua files in the mod folder, collects translatable strings,
# and writes it to a template.txt file
# Returns a dictionary of localized strings to source file sets
# that can be used with the strings_to_text function.
def generate_template(folder, mod_name):
dOut = {}
for root, dirs, files in os.walk(folder):
for name in files:
if fnmatch.fnmatch(name, "*.lua"):
fname = os.path.join(root, name)
found = read_lua_file_strings(fname)
if params["verbose"]:
print(f"{fname}: {str(len(found))} translatable strings")
for s in found:
sources = dOut.get(s, set())
sources.add(f"### {os.path.basename(fname)} ###")
dOut[s] = sources
if len(dOut) == 0:
return None
templ_file = os.path.join(folder, "locale/template.txt")
write_template(templ_file, dOut, mod_name)
return dOut
# Updates an existing .tr file, copying the old one to a ".old" file
# if any changes have happened
# dNew is the data used to generate the template, it has all the
# currently-existing localized strings
def update_tr_file(dNew, mod_name, tr_file):
if params["verbose"]:
print(f"updating {tr_file}")
tr_import = import_tr_file(tr_file)
dOld = tr_import[0]
textOld = tr_import[1]
textNew = strings_to_text(dNew, dOld, mod_name, tr_import[2])
if textOld and textOld != textNew:
print(f"{tr_file} has changed.")
if not params["no-old-file"]:
shutil.copyfile(tr_file, f"{tr_file}.old")
with open(tr_file, "w", encoding='utf-8') as new_tr_file:
new_tr_file.write(textNew)
# Updates translation files for the mod in the given folder
def update_mod(folder):
modname = get_modname(folder)
if modname is not None:
process_po_files(folder, modname)
print(f"Updating translations for {modname}")
data = generate_template(folder, modname)
if data == None:
print(f"No translatable strings found in {modname}")
else:
for tr_file in get_existing_tr_files(folder):
update_tr_file(data, modname, os.path.join(folder, "locale/", tr_file))
else:
print(f"\033[31mUnable to find modname in folder {folder}.\033[0m", file=_stderr)
exit(1)
# Determines if the folder being pointed to is a mod or a mod pack
# and then runs update_mod accordingly
def update_folder(folder):
is_modpack = os.path.exists(os.path.join(folder, "modpack.txt")) or os.path.exists(os.path.join(folder, "modpack.conf"))
if is_modpack:
subfolders = [f.path for f in os.scandir(folder) if f.is_dir()]
for subfolder in subfolders:
update_mod(subfolder + "/")
else:
update_mod(folder)
print("Done.")
def run_all_subfolders(folder):
for modfolder in [f.path for f in os.scandir(folder) if f.is_dir()]:
update_folder(modfolder + "/")
main()
+92
View File
@@ -0,0 +1,92 @@
--[[
Hyperloop Mod
=============
v2.06 by JoSt
Copyright (C) 2017-2021 Joachim Stolberg
LGPLv2.1+
See LICENSE.txt for more information
History:
2017-06-18 v0.01 First version
2017-07-06 v0.02 Version on GitHub
2017-07-07 v0.03 Recipes added, settingstypes added
2017-07-08 v0.04 Door removal issue fixed
2017-07-16 v0.05 Doors can be opened manually
2017-07-24 v0.06 Tubes with limited slope, elevator and deco blocks added
2017-07-28 v0.07 Slope removed, Station auto-builder added
2017-07-30 v0.08 Signs added, tube robot added, crowbar added
2017-07-31 v0.09 Some bug fixes on the Bocking Machine
2017-08-01 v0.10 Elevator now with sound and travel animation plus minor bug fixes
2017-08-06 v0.11 Crowbar now allows repairing of illegally detroyed tubes
2018-03-27 v0.12 Some minor improvements with player position, arrival time,
Wifi node improvements, Podshell cheating bugfix,
forceload_block removed.
2018-04-13 v0.13 Potential "Never Store ObjectRefs" bug fixed
2018-10-27 v1.00 Release
2018-10-27 v2.00 Switched to lubelib2 for shafts and tubes
2018-12-16 v2.01 Adapted to lubelib2 v0.5
2018-12-20 v2.02 Intllib support added, bugs fixed
2019-03-23 v2.03 Elevator duplication bugfix
2020-01-03 v2.04 Elevator door bugfix (MT 5+)
2020-03-12 v2.05 minetest translator added (thanks to acmgit/Clyde)
2020-06-14 v2.06 The default value for `hyperloop_free_tube_placement_enabled` is now true
2021-02-07 v2.07 tube_crowbar: Add tube length check
2021-11-01 v2.08 Enable the use of hyperloop networks for other mods
]]--
hyperloop = {}
-- Version for compatibility checks, see history
hyperloop.version = 2.08
if minetest.global_exists("techage") and techage.version < 0.06 then
error("[hyperloop] Hyperloop requires techage version 0.06 or newer!")
return
end
hyperloop.S = minetest.get_translator(minetest.get_current_modname())
-- MT 5.4 new string mode
hyperloop.CLIP = minetest.features.use_texture_alpha_string_modes and "clip" or false
hyperloop.BLEND = minetest.features.use_texture_alpha_string_modes and "blend" or true
if tubelib2.version < 1.7 then
error("Hyperloop requires tubelib2 version 1.7 or newer!!!")
else
-- Configuration settings
hyperloop.wifi_enabled = minetest.settings:get_bool("hyperloop_wifi_enabled")
hyperloop.wifi_crafting_enabled = minetest.settings:get_bool("hyperloop_wifi_crafting_enabled")
hyperloop.free_tube_placement_enabled = minetest.settings:get_bool("hyperloop_free_tube_placement_enabled", true)
hyperloop.subnet_enabled = minetest.settings:get_bool("hyperloop_subnet_enabled", true)
dofile(minetest.get_modpath("hyperloop") .. "/network.lua")
dofile(minetest.get_modpath("hyperloop") .. "/data_base.lua")
dofile(minetest.get_modpath("hyperloop") .. "/booking.lua")
dofile(minetest.get_modpath("hyperloop") .. "/utils.lua")
dofile(minetest.get_modpath("hyperloop") .. "/elevator.lua")
dofile(minetest.get_modpath("hyperloop") .. "/tube.lua")
dofile(minetest.get_modpath("hyperloop") .. "/tubecrowbar.lua")
dofile(minetest.get_modpath("hyperloop") .. "/junction.lua")
dofile(minetest.get_modpath("hyperloop") .. "/station.lua")
dofile(minetest.get_modpath("hyperloop") .. "/booking_node.lua")
if hyperloop.wifi_enabled then
dofile(minetest.get_modpath("hyperloop") .. "/wifi.lua")
end
dofile(minetest.get_modpath("hyperloop") .. "/map.lua")
dofile(minetest.get_modpath("hyperloop") .. "/door.lua")
dofile(minetest.get_modpath("hyperloop") .. "/seat.lua")
dofile(minetest.get_modpath("hyperloop") .. "/lcd.lua")
dofile(minetest.get_modpath("hyperloop") .. "/waypoint.lua")
dofile(minetest.get_modpath("hyperloop") .. "/deco.lua")
dofile(minetest.get_modpath("hyperloop") .. "/recipes.lua")
---- Migration from v1 to v2
dofile(minetest.get_modpath("hyperloop") .. "/migrate.lua")
print ("[MOD] Hyperloop loaded")
end
@@ -0,0 +1,45 @@
-- Fallback functions for when `intllib` is not installed.
-- Code released under Unlicense <http://unlicense.org>.
-- Get the latest version of this file at:
-- https://raw.githubusercontent.com/minetest-mods/intllib/master/lib/intllib.lua
local function format(str, ...)
local args = { ... }
local function repl(escape, open, num, close)
if escape == "" then
local replacement = tostring(args[tonumber(num)])
if open == "" then
replacement = replacement..close
end
return replacement
else
return "@"..open..num..close
end
end
return (str:gsub("(@?)@(%(?)(%d+)(%)?)", repl))
end
local gettext, ngettext
if minetest.get_modpath("intllib") then
if intllib.make_gettext_pair then
-- New method using gettext.
gettext, ngettext = intllib.make_gettext_pair()
else
-- Old method using text files.
gettext = intllib.Getter()
end
end
-- Fill in missing functions.
gettext = gettext or function(msgid, ...)
return format(msgid, ...)
end
ngettext = ngettext or function(msgid, msgid_plural, n, ...)
return format(n==1 and msgid or msgid_plural, ...)
end
return gettext, ngettext
@@ -0,0 +1,92 @@
--[[
Hyperloop Mod
=============
Copyright (C) 2017-2019 Joachim Stolberg
LGPLv2.1+
See LICENSE.txt for more information
]]--
-- for lazy programmers
--local SP = function(pos) if pos then return minetest.pos_to_string(pos) end end
--local P = minetest.string_to_pos
local M = minetest.get_meta
-- Load support for intllib.
local S = hyperloop.S
local Tube = hyperloop.Tube
local Stations = hyperloop.Stations
Tube:register_on_tube_update(function(node, pos, out_dir, peer_pos, peer_in_dir)
if node.name == "hyperloop:station" then
if out_dir <= 5 then
Stations:update_connections(pos, out_dir, peer_pos)
local s = hyperloop.get_connection_string(pos)
M(pos):set_string("infotext", S("Station connected to ")..s)
end
elseif node.name == "hyperloop:junction" then
Stations:update_connections(pos, out_dir, peer_pos)
local s = hyperloop.get_connection_string(pos)
M(pos):set_string("infotext", S("Junction connected to ")..s)
elseif Tube.secondary_node_names[node.name] then
if out_dir == 5 then
Stations:update_connections(pos, out_dir, peer_pos)
local s = hyperloop.get_connection_string(pos)
M(pos):set_string("conn_to", s)
end
end
end)
minetest.register_node("hyperloop:junction", {
description = S("Hyperloop Junction Block"),
tiles = {
"hyperloop_junction_top.png",
"hyperloop_junction_top.png",
"hyperloop_station_connection.png",
},
after_place_node = function(pos, placer, itemstack, pointed_thing)
hyperloop.check_network_level(pos, placer)
M(pos):set_string("infotext", S("Junction"))
Stations:set(pos, "Junction", {
owner = placer:get_player_name(), junction = true})
Tube:after_place_node(pos)
end,
after_dig_node = function(pos, oldnode, oldmetadata, digger)
Tube:after_dig_node(pos)
Stations:delete(pos)
end,
paramtype2 = "facedir",
on_rotate = screwdriver.disallow,
paramtype = "light",
sunlight_propagates = true,
groups = {cracky = 1},
is_ground_content = false,
sounds = default.node_sound_metal_defaults(),
})
-- for tube viaducts
minetest.register_node("hyperloop:pillar", {
description = S("Hyperloop Pillar"),
tiles = {"hyperloop_tube_locked.png^[transformR90]"},
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{ -3/8, -4/8, -3/8, 3/8, 4/8, 3/8},
},
},
on_rotate = screwdriver.disallow,
paramtype = "light",
sunlight_propagates = true,
is_ground_content = false,
groups = {cracky = 2, stone = 2},
sounds = default.node_sound_metal_defaults(),
})
+202
View File
@@ -0,0 +1,202 @@
--[[
LCD
===
Derived from the work of kaeza, sofar and others (digilines)
LGPLv2.1+
See LICENSE.txt for more information
]]--
-- Load support for intllib.
local S = hyperloop.S
-- load characters map
local chars_file = io.open(minetest.get_modpath("hyperloop").."/characters.data", "r")
local charmap = {}
local max_chars = 16
if not chars_file then
print("[Hyperloop] E: LCD: character map file not found")
else
while true do
local char = chars_file:read("*l")
if char == nil then
break
end
local img = chars_file:read("*l")
chars_file:read("*l")
charmap[char] = img
end
end
-- CONSTANTS
local LCD_WITH = 112
local LINE_LENGTH = 17
local NUMBER_OF_LINES = 6
local LINE_HEIGHT = 14
local CHAR_WIDTH = 5
local create_lines = function(text)
local line = ""
local line_num = 1
local tab = {}
for word in string.gmatch(text, "%S+") do
if string.len(line)+string.len(word) < LINE_LENGTH and word ~= "|" then
if line ~= "" then
line = line.." "..word
else
line = word
end
else
table.insert(tab, line)
if word ~= "|" then
line = word
else
line = ""
end
line_num = line_num+1
if line_num > NUMBER_OF_LINES then
return tab
end
end
end
table.insert(tab, line)
return tab
end
local generate_line = function(s, ypos)
local i = 1
local parsed = {}
local width = 0
local chars = 0
while chars < max_chars and i <= #s do
local file = nil
if charmap[s:sub(i, i)] ~= nil then
file = charmap[s:sub(i, i)]
i = i + 1
elseif i < #s and charmap[s:sub(i, i + 1)] ~= nil then
file = charmap[s:sub(i, i + 1)]
i = i + 2
else
i = i + 1
end
if file ~= nil then
width = width + CHAR_WIDTH
table.insert(parsed, file)
chars = chars + 1
end
end
width = width - 1
local texture = ""
local xpos = math.floor((LCD_WITH - width) / 2) - CHAR_WIDTH
--xpos = 5
for ii = 1, #parsed do
texture = texture..":"..xpos..","..ypos.."="..parsed[ii]..".png"
xpos = xpos + CHAR_WIDTH + 1
end
return texture
end
local generate_texture = function(lines)
local texture = "[combine:"..LCD_WITH.."x"..LCD_WITH
local ypos = 8
for i = 1, #lines do
texture = texture..generate_line(lines[i], ypos)
ypos = ypos + LINE_HEIGHT
end
return texture
end
local lcds = {
[2] = {delta = {x = 0.425, y = 0, z = 0}, yaw = math.pi / -2},
[3] = {delta = {x = -0.425, y = 0, z = 0}, yaw = math.pi / 2},
[4] = {delta = {x = 0, y = 0, z = 0.425}, yaw = 0},
[5] = {delta = {x = 0, y = 0, z = -0.425}, yaw = math.pi},
}
local clearscreen = function(pos)
local objects = minetest.get_objects_inside_radius(pos, 0.5)
for _, o in ipairs(objects) do
local o_entity = o:get_luaentity()
if o_entity and o_entity.name == "hyperloop_lcd:text" then
o:remove()
end
end
end
local prepare_writing = function(pos)
local lcd_info = lcds[minetest.get_node(pos).param2]
if lcd_info == nil then return end
local text = minetest.add_entity(
{x = pos.x + lcd_info.delta.x,
y = pos.y + lcd_info.delta.y,
z = pos.z + lcd_info.delta.z}, "hyperloop_lcd:text")
text:set_yaw(lcd_info.yaw or 0)
--* text:setpitch(lcd_info.yaw or 0)
return text
end
local lcd_update = function(pos, text)
local meta = minetest.get_meta(pos)
meta:set_string("text", text)
clearscreen(pos)
prepare_writing(pos)
end
local lcd_box = {
type = "wallmounted",
wall_top = {-8/16, 15/32, -8/16, 8/16, 8/16, 8/16}
}
minetest.register_node("hyperloop:lcd", {
drawtype = "nodebox",
description = S("Hyperloop Display"),
tiles = {"hyperloop_lcd.png"},
on_rotate = screwdriver.disallow,
paramtype = "light",
sunlight_propagates = true,
paramtype2 = "wallmounted",
node_box = lcd_box,
selection_box = lcd_box,
drop = "",
groups = {cracky=1, not_in_creative_inventory=1},
auto_place_node = function(pos, placer, facedir)
local param2 = minetest.get_node(pos).param2
if param2 == 0 or param2 == 1 then
minetest.add_node(pos, {name = "hyperloop:lcd", param2 = 3})
end
lcd_update(pos, " | | << Hyperloop >> | be anywhere")
end,
on_destruct = function(pos)
clearscreen(pos)
end,
update = function(pos, text)
lcd_update(pos, text)
end,
light_source = 6,
})
minetest.register_entity(":hyperloop_lcd:text", {
initial_properties = {
collisionbox = { 0, 0, 0, 0, 0, 0 },
visual = "upright_sprite",
textures = {},
},
on_activate = function(self)
local meta = minetest.get_meta(self.object:get_pos())
local text = meta:get_string("text")
self.object:set_properties({textures={generate_texture(create_lines(text))}})
end
})
@@ -0,0 +1,142 @@
# textdomain: hyperloop
### booking.lua ###
Station data is corrupted. Please rebuild the station!=Stationsdaten sind beschädigt. Bitte die Station neu bauen!
Station is still blocked. Please try again in a few seconds!=Die Station ist noch blockiert. Bitte versuche es in ein paar Sekunden wieder!
### booking_node.lua ###
Please enter the station name to@nwhich this booking machine belongs.=Bitte gib den Stationsnamen ein@nzu dem dieser Fahrkartenautomat gehört.
Station name=Stationsname
Additional station information=Zusätzliche Stationsinformationen
Station has already a booking machine!=Station hat bereits einen Fahrkartenautomat!
Invalid station name!=Ungültiger Stationsname!
Hyperloop Booking Machine=Hyperloop Fahrkartenautomat
### booking_node.lua ###
### elevator.lua ###
Select your destination=Wähle dein Ziel
### booking_node.lua ###
### migrate.lua ###
<unknown>=<unbekannt>
### deco.lua ###
Hyperloop Promo Poster =Hyperloop Werbeposter
Hyperloop Station Sign=Hyperloop Stationszeichen
Hyperloop Station Sign Right=Hyperloop Stationszeichen rechts
Hyperloop Station Sign Left=Hyperloop Stationszeichen links
### door.lua ###
The Booking Machine for this station is missing!=Der Fahrkartenautomat für die Station fehlt!
Hyperloop Door Top=Hyperloop Tür Oberteil
Hyperloop Door Bottom=Hyperloop Tür Unterteil
### elevator.lua ###
Hyperloop Elevator Shaft=Hyperloop Aufzugsschacht
Destination=Ziel
Floor=Stockwerk
(current position)=(aktuelle Position)
Hyperloop Elevator=Hyperloop Aufzug
Please insert floor name=Gib den Stockwerknamen ein
Floor name=Stockwerkname
Base=Basis
### elevator.lua ###
### wifi.lua ###
Save=Speichern
### junction.lua ###
Station connected to =Station verbunden mit
Junction connected to =Anschlussstelle verbunden mit
Hyperloop Junction Block=Hyperloop Anschlussstelle
Hyperloop Pillar=Hyperloop Stütze
### junction.lua ###
### migrate.lua ###
Junction=Anschlussstelle
### lcd.lua ###
Hyperloop Display=Hyperloop Bildschirm
### map.lua ###
Dist.=Entf.
Station/Junction=Station/Anschlussstelle
Position=Position
Owner=Besitzer
Conn. to=Verb. mit
Close=Schließen
Hyperloop Station Book=Hyperloop Stationsbuch
### migrate.lua ###
Hyperloop Legacy Tube=Hyperloop veraltetes Rohr
unknown=unbekant
### recipes.lua ###
### station.lua ###
Hypersteel Ingot=Hyperstahl Barren
### seat.lua ###
[Hyperloop] No booking entered!=[Hyperloop] Keine Buchung eingegeben!
Hyperloop Pod Seat=Hyperloop Sitz
### station.lua ###
Station completed. Now place the Booking Machine!=Station fertig. Setze nun den Fahrkartenautomat!
Area is protected!=Die Area ist geschützt!
Not enough space to build the station!=Nicht ausreichend Platz um die Station zu errichten!
Hyperloop Station Pod Builder=Hyperloop Stations Ersteller
Hyperloop Pod Shell=Hyperloop Kabinenwand
Blue Wool=Blaue Wolle
Glass=Glas
Not enough inventory items to build the station!=Nicht ausreichend Inventory Items um die Station zu bauen!
Destroy Station=Zerstöre Station
Build Station=Baue Station
Hyperloop Station Block=Hyperloop Stations Block
Station=Station
### tube.lua ###
Junction at =Anschlussstelle bei
Station '=Station '
Station at =Station bei
Open end at =Offenes Ende bei
There is no station/junction on this level. =Es gibt keine Station/Anschlussstelle auf dieser Höhe.
Do you really want to start a new network?!=Willst du wirklich ein neues Liniennetz beginnen?!
Hyperloop Tube=Hyperloop Röhre
### tubecrowbar.lua ###
[Hyperloop] Error: Tube is too long!=[Hyperloop] Fehler: Röhre ist zu lang!
[Crowbar Help]@n=[Brecheisen Hilfe]@n
left: remove node@n= links: entferne Block@n
right: repair tube/shaft line@n= rechts: repariere Röhre/Schacht@n
You don't have the necessary privs!=Du hast nicht die notwendigen Rechte!
Hyperloop Tube Crowbar=Hyperloop Brecheisen
Rights to remove tube nodes by means of the crowbar=Rechte zur Entfernung von Rohrknoten mit Hilfe der Brechstange
Repair via WorldEdit placed Hyperloop tubes by reusing WorldEdit pos1/pos2=Reparatur über WorldEdit platzierte Hyperloop-Röhren durch Wiederverwendung von WorldEdit pos1/pos2
### waypoint.lua ###
Hyperloop Waypoint=Hyperloop Wegpunkt
### wifi.lua ###
Enter channel string=Kanalname eingeben
Hyperloop WiFi Tube=Hyperloop Wifi Röhre
@@ -0,0 +1,142 @@
# textdomain: hyperloop
### booking.lua ###
Station data is corrupted. Please rebuild the station!=Les données de la station sont corrompues. Veuillez reconstruire la station !
Station is still blocked. Please try again in a few seconds!=La station est toujours bloquée. Veuillez réessayer dans quelques secondes !
### booking_node.lua ###
Please enter the station name to@nwhich this booking machine belongs.=Veuillez entrer le nom de la station@nà laquelle appartient cette machine de réservation.
Station name=Nom de la station
Additional station information=Renseignements supplémentaires sur la station
Station has already a booking machine!=La station a déjà une machine de réservation !
Invalid station name!==Nom de station invalide !
Hyperloop Booking Machine=Machine de réservation Hyperloop
### booking_node.lua ###
### elevator.lua ###
Select your destination=Sélectionnez votre destination
### booking_node.lua ###
### migrate.lua ###
<unknown>=
### deco.lua ###
Hyperloop Promo Poster=Affiche promotionnelle Hyperloop
Hyperloop Station Sign=Panneau de station Hyperloop
Hyperloop Station Sign Right=Panneau droit de station Hyperloop
Hyperloop Station Sign Left=Panneau gauche de station Hyperloop
### door.lua ###
The Booking Machine for this station is missing!=La machine de réservation pour cette station est manquante !
Hyperloop Door Top=Haut de porte Hyperloop
Hyperloop Door Bottom=Bas de porte Hyperloop
### elevator.lua ###
Hyperloop Elevator Shaft=Cage d’ascenseur Hyperloop
Destination=Destination
Floor=Etage
(current position)=(position courante)
Hyperloop Elevator=Ascenseur Hyperloop
Please insert floor name=Veuillez insérer le nom de l’étage
Floor name=Nom de l’étage
Base=Base
### elevator.lua ###
### wifi.lua ###
Save=Enregistrer
### junction.lua ###
Station connected with =Station reliée à
Junction connected with =Jonction reliée à
Hyperloop Junction Block=Bloc de jonction Hyperloop
Hyperloop Pillar=Pillier Hyperloop
### junction.lua ###
### migrate.lua ###
Junction=Jonction
### lcd.lua ###
Hyperloop Display=Ecran Hyperloop
### map.lua ###
Dist.=Dist.
Station/Junction=Station/Jonction
Position=Position
Owner=Propriétaire
Conn. with=Conn. avec
Close=Fermer
Hyperloop Station Book=Livre des stations Hyperloop
### migrate.lua ###
Hyperloop Legacy Tube=Tube traditionnel Hyperloop
unknown=inconnu
### recipes.lua ###
### station.lua ###
Hypersteel Ingot=Lingot Hypersteel
### seat.lua ###
[Hyperloop] No booking entered!=[Hyperloop] Aucune réservation saisie !
Hyperloop Pod Seat=Siège de module Hyperloop
### station.lua ###
Station completed. Now place the Booking Machine!=Station terminée. Placez maintenant la machine de réservation !
Area is protected!=La zone est protégée
Not enough space to build the station!=Pas assez de place pour construire la station !
Hyperloop Station Pod Builder=Module de construction de station Hyperloop
Hyperloop Pod Shell=Coque de module Hyperloop
Blue Wool=Laine blue
Glass=Verre
Not enough inventory items to build the station!=Pas assez d’items en stock pour construire la station
Destroy Station=Détruire la station
Build Station=Construire la station
Hyperloop Station Block=Bloc de station Hyperloop
Station=Station
### tube.lua ###
Junction at =Jonction à
Station '=Station '
Station at =Station à
Open end at =Extrémité ouverte à
There is no station/junction on this level. =Il n’y a pas de station/jonction à ce niveau.
Do you really want to start a new network?!=Voulez-vous vraiment démarrer un nouveau réseau ?
Hyperloop Tube=Tube Hyperloop
### tubecrowbar.lua ###
[Hyperloop] Error: Tube is too long!=[Hyperloop] Erreur : Tube trop long
[Crowbar Help]@n=[Crowbar Help]@n
left: remove node@n=gauche : supprimer le nœud @n
right: repair tube/shaft line@n=droite : réparer le tube / ligne d'arbre @n
You don't have the necessary privs!=Vous n'avez pas les droits nécessaires !
Hyperloop Tube Crowbar=Pied de biche Hyperloop
Rights to remove tube nodes by means of the crowbar=Droits de suppression des nœuds de tubes au moyen du pied de biche
Repair via WorldEdit placed Hyperloop tubes by reusing WorldEdit pos1/pos2=Réparation via WorldEdit des tubes Hyperloop placés en réutilisant WorldEdit pos1/pos2
### waypoint.lua ###
Hyperloop Waypoint=Point de passage Hyperloop
### wifi.lua ###
Enter channel string=Entrer la valeur pour la chaîne
Hyperloop WiFi Tube=Tube WiFi Hyperloop
@@ -0,0 +1,142 @@
# textdomain: hyperloop
### booking.lua ###
Station data is corrupted. Please rebuild the station!=
Station is still blocked. Please try again in a few seconds!=
### booking_node.lua ###
Please enter the station name to@nwhich this booking machine belongs.=
Station name=
Additional station information=
Station has already a booking machine!=
Invalid station name!=
Hyperloop Booking Machine=
### booking_node.lua ###
### elevator.lua ###
Select your destination=
### booking_node.lua ###
### migrate.lua ###
<unknown>=
### deco.lua ###
Hyperloop Promo Poster =
Hyperloop Station Sign=
Hyperloop Station Sign Right=
Hyperloop Station Sign Left=
### door.lua ###
The Booking Machine for this station is missing!=
Hyperloop Door Top=
Hyperloop Door Bottom=
### elevator.lua ###
Hyperloop Elevator Shaft=
Destination=
Floor=
(current position)=
Hyperloop Elevator=
Please insert floor name=
Floor name=
Base=
### elevator.lua ###
### wifi.lua ###
Save=
### junction.lua ###
Station connected with =
Junction connected with =
Hyperloop Junction Block=
Hyperloop Pillar=
### junction.lua ###
### migrate.lua ###
Junction=
### lcd.lua ###
Hyperloop Display=
### map.lua ###
Dist.=
Station/Junction=
Position=
Owner=
Conn. with=
Close=
Hyperloop Station Book=
### migrate.lua ###
Hyperloop Legacy Tube=
unknown=
### recipes.lua ###
### station.lua ###
Hypersteel Ingot=
### seat.lua ###
[Hyperloop] No booking entered!=
Hyperloop Pod Seat=
### station.lua ###
Station completed. Now place the Booking Machine!=
Area is protected!=
Not enough space to build the station!=
Hyperloop Station Pod Builder=
Hyperloop Pod Shell=
Blue Wool=
Glass=
Not enough inventory items to build the station!=
Destroy Station=
Build Station=
Hyperloop Station Block=
Station=
### tube.lua ###
Junction at =
Station '=
Station at =
Open end at =
There is no station/junction on this level. =
Do you really want to start a new network?!=
Hyperloop Tube=
### tubecrowbar.lua ###
[Hyperloop] Error: Tube is too long!=
[Crowbar Help]@n=
left: remove node@n=
right: repair tube/shaft line@n=
You don't have the necessary privs!=
Hyperloop Tube Crowbar=
Rights to remove tube nodes by means of the crowbar=
Repair via WorldEdit placed Hyperloop tubes by reusing WorldEdit pos1/pos2=
### waypoint.lua ###
Hyperloop Waypoint=
### wifi.lua ###
Enter channel string=
Hyperloop WiFi Tube=
+126
View File
@@ -0,0 +1,126 @@
--[[
Hyperloop Mod
=============
Copyright (C) 2017-2019 Joachim Stolberg
LGPLv2.1+
See LICENSE.txt for more information
]]--
-- for lazy programmers
local SP = function(pos) if pos then return minetest.pos_to_string(pos) end end
--local P = minetest.string_to_pos
--local M = minetest.get_meta
-- Load support for intllib.
local S = hyperloop.S
local Stations = hyperloop.Stations
-- Return a text block with all given station names and their attributes
local function generate_string(sortedList)
-- Generate a list with lStationPositions[pos] = idx
-- used to generate the "connected with" list.
local lStationPositions = {}
for idx,item in ipairs(sortedList) do
local sKey = SP(item.pos)
lStationPositions[sKey] = idx
end
local tRes = {
"label[0,0;ID]"..
"label[0.7,0;"..S("Dist.").."]"..
"label[1.8,0;"..S("Station/Junction").."]"..
"label[5.4,0;"..S("Position").."]"..
"label[7.9,0;"..S("Owner").."]"..
"label[10,0;"..S("Conn. to").."]"}
for idx,dataSet in ipairs(sortedList) do
if idx == 23 then
break
end
local ypos = 0.2 + idx * 0.4
local owner = dataSet.owner or "<unknown>"
local name = dataSet.name or "<unknown>"
local distance = dataSet.distance or 0
tRes[#tRes+1] = "label[0,"..ypos..";"..idx.."]"
tRes[#tRes+1] = "label[0.7,"..ypos..";"..distance.." m]"
tRes[#tRes+1] = "label[1.8,"..ypos..";"..string.sub(name,1,24).."]"
tRes[#tRes+1] = "label[5.4,"..ypos..";"..SP(dataSet.pos).."]"
tRes[#tRes+1] = "label[7.9,"..ypos..";"..string.sub(owner,1,14).."]"
tRes[#tRes+1] = "label[10,"..ypos..";"
for dir,conn in pairs(dataSet.conn) do
if conn and lStationPositions[conn] then
tRes[#tRes + 1] = lStationPositions[conn]
tRes[#tRes + 1] = ", "
else
tRes[#tRes + 1] = conn
tRes[#tRes + 1] = ", "
end
end
tRes[#tRes] = "]"
end
return table.concat(tRes)
end
local function station_list_as_string(pos)
-- Generate a distance sorted list of all stations
local sortedList = Stations:station_list(pos, nil, "dist")
-- Generate the formspec string
return generate_string(sortedList)
end
local function network_list_as_string(pos)
-- Determine next station position
local next_pos = Stations:get_next_station(pos)
-- Generate a distance sorted list of all connected stations
local sortedList = Stations:station_list(pos, next_pos, "dist")
-- Generate the formspec string
return generate_string(sortedList)
end
local function map_on_use(itemstack, user)
local player_name = user:get_player_name()
local pos = user:get_pos()
local sStationList = station_list_as_string(pos)
local formspec = "size[12,10]" ..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
sStationList ..
"button_exit[5,9.5;2,1;close;"..S("Close").."]"
minetest.show_formspec(player_name, "hyperloop:station_map", formspec)
return itemstack
end
local function map_on_secondary_use(itemstack, user)
local player_name = user:get_player_name()
local pos = user:get_pos()
local sStationList = network_list_as_string(pos)
local formspec = "size[12,10]" ..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
sStationList ..
"button_exit[5,9.5;2,1;close;"..S("Close").."]"
minetest.show_formspec(player_name, "hyperloop:station_map", formspec)
return itemstack
end
-- Tool for tube workers to find the next station
minetest.register_node("hyperloop:station_map", {
description = S("Hyperloop Station Book"),
inventory_image = "hyperloop_stations_book.png",
wield_image = "hyperloop_stations_book.png",
groups = {cracky=1, book=1},
on_use = map_on_use,
on_place = map_on_secondary_use,
on_secondary_use = map_on_secondary_use,
stack_max = 1,
})
+388
View File
@@ -0,0 +1,388 @@
--[[
Hyperloop Mod
=============
Copyright (C) 2017 Joachim Stolberg
LGPLv2.1+
See LICENSE.txt for more information
History:
see init.lua
Migrate from v1 to v2
]]--
-- for lazy programmers
local SP = minetest.pos_to_string
local P = minetest.string_to_pos
local M = minetest.get_meta
-- Load support for intllib.
local S = hyperloop.S
local Tube = hyperloop.Tube
local Shaft = hyperloop.Shaft
local Elevators = hyperloop.Elevators
local Stations = hyperloop.Stations
local tLegacyNodeNames = {}
local JunctionsToBePlacedAfter = {}
local function get_tube_data(pos, dir1, dir2, num_tubes)
local param2, tube_type = tubelib2.encode_param2(dir1, dir2, num_tubes)
return pos, param2, tube_type, num_tubes
end
-- Check if node has a connection on the given dir
local function connected(self, pos, dir)
local _,node = self:get_node(pos, dir)
return self.primary_node_names[node.name]
or self.secondary_node_names[node.name]
end
-- Determine dirs via surrounding nodes
local function determine_dir1_dir2_and_num_conn(self, pos)
local dirs = {}
for dir = 1, 6 do
if connected(self, pos, dir) then
dirs[#dirs+1] = dir
end
end
if #dirs == 1 then
return dirs[1], nil, 1
elseif #dirs == 2 then
return dirs[1], dirs[2], 2
end
end
-- convert legacy tubes to current tubes
for idx = 0,2 do
minetest.register_node("hyperloop:tube"..idx, {
description = S("Hyperloop Legacy Tube"),
tiles = {
-- up, down, right, left, back, front
"hyperloop_tube_locked.png^[transformR90]",
"hyperloop_tube_locked.png^[transformR90]",
'hyperloop_tube_closed.png',
'hyperloop_tube_closed.png',
'hyperloop_tube_open.png',
'hyperloop_tube_open.png',
},
after_place_node = function(pos, placer, itemstack, pointed_thing)
local node = minetest.get_node(pos)
node.name = "hyperloop:tubeS"
minetest.swap_node(pos, node)
if not Tube:after_place_tube(pos, placer, pointed_thing) then
minetest.remove_node(pos)
return true
end
return false
end,
paramtype2 = "facedir",
node_placement_prediction = "hyperloop:tubeS",
groups = {cracky=2, not_in_creative_inventory=1},
is_ground_content = false,
sounds = default.node_sound_metal_defaults(),
})
end
local function convert_legary_nodes(self, pos, dir)
local convert_next_tube = function(self, pos, dir)
local npos, node = self:get_node(pos, dir)
if tLegacyNodeNames[node.name] then
local dir1, dir2, num = determine_dir1_dir2_and_num_conn(self, npos)
if dir1 then
self.clbk_after_place_tube(get_tube_data(npos, dir1,
dir2 or tubelib2.Turn180Deg[dir1], num))
if tubelib2.Turn180Deg[dir] == dir1 then
return npos, dir2
else
return npos, dir1
end
end
end
end
local cnt = 0
if not dir then return pos, dir, cnt end
while cnt <= 64000 do
local new_pos, new_dir = convert_next_tube(self, pos, dir)
if cnt > 0 and (cnt % self.max_tube_length) == 0 then -- border reached?
JunctionsToBePlacedAfter[#JunctionsToBePlacedAfter + 1] = pos
end
if not new_dir then break end
pos, dir = new_pos, new_dir
cnt = cnt + 1
end
return pos, dir, cnt
end
local function convert_line(self, pos, dir)
convert_legary_nodes(self, pos, dir)
self:tool_repair_tube(pos)
end
local tWifiNodes = {} -- user for pairing
local lWifiNodes = {} -- used for post processing
local function set_pairing(pos, peer_pos)
M(pos):set_int("tube_dir", Tube:get_primary_dir(pos))
M(peer_pos):set_int("tube_dir", Tube:get_primary_dir(peer_pos))
Tube:store_teleport_data(pos, peer_pos)
Tube:store_teleport_data(peer_pos, pos)
end
local function wifi_post_processing()
for _,pos in ipairs(lWifiNodes) do
local dir = Tube:get_primary_dir(pos)
local npos = Tube:get_pos(pos, dir)
Tube:tool_repair_tube(npos)
end
end
-- Wifi nodes don't know their counterpart.
-- But by means of the tube head nodes, two
-- Wifi nodes in one tube line can be determined.
local function determine_wifi_pairs(pos)
-- determine 1. tube head node
local pos1 = M(pos):get_string("peer")
if pos1 == "" then return end
-- determine 2. tube head node
local pos2 = M(P(pos1)):get_string("peer")
if pos2 == "" then return end
for k,item in pairs(tWifiNodes) do
-- entry already available
if item[1] == pos2 and item[2] == pos1 then
tWifiNodes[k] = nil
-- start paring
set_pairing(P(k), pos)
return
end
end
-- add single Wifi node to pairing table
tWifiNodes[SP(pos)] = {pos1, pos2}
end
local function next_node_on_the_way_to_a_wifi_node(pos)
local dirs = {}
for dir = 1, 6 do
local npos, node = Tube:get_node(pos, dir)
if tLegacyNodeNames[node.name] then
dirs[#dirs+1] = dir
elseif node.name == "hyperloop:tube_wifi1" then
lWifiNodes[#lWifiNodes+1] = npos
determine_wifi_pairs(npos)
end
end
if #dirs == 1 then
return dirs[1], nil, 1
elseif #dirs == 2 then
return dirs[1], dirs[2], 2
end
end
local function search_wifi_node(pos, dir)
local convert_next_tube = function(pos, dir)
local npos, _ = Tube:get_node(pos, dir)
local dir1, dir2, _ = next_node_on_the_way_to_a_wifi_node(npos)
if dir1 then
if tubelib2.Turn180Deg[dir] == dir1 then
return npos, dir2
else
return npos, dir1
end
end
end
local cnt = 0
if not dir then return pos, cnt end
while true do
local new_pos, new_dir = convert_next_tube(pos, dir)
if not new_dir then break end
pos, dir = new_pos, new_dir
cnt = cnt + 1
end
return pos, dir, cnt
end
local function search_wifi_node_in_all_dirs(pos)
-- check all positions
for dir = 1, 6 do
local _, node = Tube:get_node(pos, dir)
if node and node.name == "hyperloop:tube1" then
search_wifi_node(pos, dir)
end
end
end
local function convert_tube_line(pos)
-- check all positions
for dir = 1, 6 do
local _, node = Tube:get_node(pos, dir)
if node and node.name == "hyperloop:tube1" then
convert_line(Tube, pos, dir)
end
end
end
local function convert_shaft_line(pos)
-- check lower position
convert_line(Shaft, pos, 5)
-- check upper position
pos.y = pos.y + 1
convert_line(Shaft, pos, 6)
pos.y = pos.y - 1
end
local function station_name(item)
if item.junction == true then
return "Junction"
elseif item.station_name then
return item.station_name
else
return "Station"
end
end
local function add_to_table(tbl, tValues)
local res = table.copy(tbl)
for k,v in pairs(tValues) do
tbl[k] = v
end
return res
end
local function convert_station_data(tAllStations)
tLegacyNodeNames = {
["hyperloop:tube0"] = true,
["hyperloop:tube1"] = true,
["hyperloop:tube2"] = true,
}
local originNodeNames = add_to_table(Tube.primary_node_names, tLegacyNodeNames)
for key,item in pairs(tAllStations) do
if item.pos and Tube:is_secondary_node(item.pos) then
Stations:set(item.pos, station_name(item), {
owner = item.owner or S("<unknown>"),
junction = item.junction,
facedir = item.facedir,
booking_info = item.booking_info,
booking_pos = item.booking_pos,
})
end
end
-- First perform the Wifi node pairing
-- before all tube node loose their meta data
-- while converted.
for key,item in pairs(tAllStations) do
if item.pos and Tube:is_secondary_node(item.pos) then
search_wifi_node_in_all_dirs(item.pos)
end
end
-- Then convert all tube nodes
for key,item in pairs(tAllStations) do
if item.pos and Tube:is_secondary_node(item.pos) then
convert_tube_line(item.pos)
Tube:after_place_node(item.pos)
end
end
-- Repair the tube lines of wifi nodes
wifi_post_processing()
Tube.primary_node_names = originNodeNames
end
local function convert_elevator_data(tAllElevators)
tLegacyNodeNames = {
["hyperloop:shaft"] = true,
["hyperloop:shaft2"] = true,
}
local originNodeNames = add_to_table(Shaft.primary_node_names, tLegacyNodeNames)
local originDirsToCheck = table.copy(Shaft.dirs_to_check)
Shaft.dirs_to_check = {5,6} -- legacy elevators use up/down only
for pos,tElevator in pairs(tAllElevators) do
for _,floor in pairs(tElevator.floors) do
if floor.pos and Shaft:is_secondary_node(floor.pos) then
Elevators:set(floor.pos, floor.name, {
facedir = floor.facedir,
})
convert_shaft_line(floor.pos)
M(floor.pos):set_int("change_counter", 0)
Shaft:after_place_node(floor.pos)
end
end
end
Shaft.primary_node_names = originNodeNames
Shaft.dirs_to_check = originDirsToCheck
end
local function place_junctions()
for _,pos in ipairs(JunctionsToBePlacedAfter) do
minetest.set_node(pos, {name = "hyperloop:junction"})
M(pos):set_string("infotext", S("Junction"))
Stations:set(pos, "Junction", {owner = S("unknown"), junction = true})
Tube:after_place_node(pos)
minetest.log("action", "[Hyperloop] Junction placed at "..SP(pos))
end
end
local wpath = minetest.get_worldpath()
function hyperloop.file2table(filename)
local f = io.open(wpath..DIR_DELIM..filename, "r")
if f == nil then return nil end
local t = f:read("*all")
f:close()
if t == "" or t == nil then return nil end
return minetest.deserialize(t)
end
local function migrate()
local data = hyperloop.file2table("mod_hyperloop.data")
if data then
minetest.log("action", "[Hyperloop] Migrate data...")
hyperloop.convert = true
convert_station_data(data.tAllStations)
convert_elevator_data(data.tAllElevators)
os.remove(wpath..DIR_DELIM.."mod_hyperloop.data")
place_junctions()
hyperloop.convert = nil
minetest.log("action", "[Hyperloop] Data migrated")
end
end
minetest.after(5, migrate)
minetest.register_lbm({
label = "[Hyperloop] booking/seat/door migration",
name = "hyperloop:migrate",
nodenames = {
"hyperloop:booking", "hyperloop:booking_ground",
"hyperloop:doorTopPassive", "hyperloop:doorBottom",
"hyperloop:seat",
},
run_at_every_load = true,
action = function(pos, node)
local meta = M(pos)
if meta:get_string("key_str") ~= "" then
local s = meta:get_string("key_str")
meta:set_string("sStationPos", "("..string.sub(s, 2, -2)..")")
if node.name == "hyperloop:booking" or node.name == "hyperloop:booking_ground" then
meta:set_int("change_counter", 0)
end
end
end
})
+5
View File
@@ -0,0 +1,5 @@
name = hyperloop
depends = default, tubelib2
optional_depends = techage, worldedit
description = Hyperloop Mod, the fast and modern way of traveling.
supported_games = minetest_game
+288
View File
@@ -0,0 +1,288 @@
--[[
Hyperloop Mod
=============
Copyright (C) 2017-2019 Joachim Stolberg
LGPLv2.1+
See LICENSE.txt for more information
Station and elevator network management
]]--
-- for lazy programmers
local S = function(pos) if pos then return minetest.pos_to_string(pos) end end
local P = minetest.string_to_pos
--local M = minetest.get_meta
-- Convert to list and add pos based on key string
local function table_to_list(table)
local lRes = {}
for key,item in pairs(table) do
item.pos = P(key)
lRes[#lRes+1] = item
end
return lRes
end
local function distance(pos1, pos2)
return math.floor(math.abs(pos1.x - pos2.x) +
math.abs(pos1.y - pos2.y) + math.abs(pos1.z - pos2.z))
end
-- Add the distance to pos to each list item
local function add_distance_to_list(lStations, pos)
for _,item in ipairs(lStations) do
item.distance = distance(item.pos, pos)
end
return lStations
end
-- Add the index to each list item
local function add_index_to_list(lStations)
-- walk through the list of floors for the next connection
local get_next = function(key, idx)
for _,floor in ipairs(lStations) do
if floor.conn[6] == key then -- upward match?
floor.idx = idx
return S(floor.pos) -- return floor key
end
end
end
local key = nil
for idx = 1,#lStations do
key = get_next(key, idx)
end
return lStations
end
-- Return a table with all stations, the given station (as 'sKey') is connected with
-- tRes is used for the resulting table (recursive call)
local function get_stations(tStations, sKey, tRes)
if not tStations[sKey] or not tStations[sKey].conn then
return {}
end
for dir,dest in pairs(tStations[sKey].conn) do
-- Not already visited?
if not tRes[dest] then
-- Known station?
if tStations[dest] then
tStations[dest].name = tStations[dest].name or ""
tRes[dest] = tStations[dest]
get_stations(tStations, dest, tRes)
end
end
end
return tRes
end
-- Return a list with sorted elevators, beginning with the top car
-- with no shaft upwards
local function sort_based_on_level(tStations)
local lStations = table_to_list(table.copy(tStations))
-- to be able to sort the list, an index has to be added
lStations = add_index_to_list(lStations)
table.sort(lStations, function(a,b) return (a.idx or 9999) < (b.idx or 9999) end)
return lStations
end
-- Return a list with sorted stations
local function sort_based_on_distance(tStations, pos)
local lStations = table_to_list(table.copy(tStations))
-- to be able to sort the list, the distance to pos has to be added
lStations = add_distance_to_list(lStations, pos)
table.sort(lStations, function(a,b) return a.distance < b.distance end)
return lStations
end
-- Return a list with sorted stations
local function sort_based_on_name(tStations, pos)
local lStations = table_to_list(table.copy(tStations))
-- Add distance
lStations = add_distance_to_list(lStations, pos)
table.sort(lStations, function(a,b) return a.name < b.name end)
return lStations
end
--
-- Class Network
--
--[[
tStations["(x,y,z)"] = {
["conn"] = {
dir = "(200,0,20)",
},
}
change_counter = n,
]]--
local Network = {}
hyperloop.Network = Network
function Network:new()
local o = {
tStations = {},
change_counter = 0,
}
setmetatable(o, self)
self.__index = self
return o
end
-- Set an elevator or station entry.
-- tAttr is a table with additional attributes to be stored.
function Network:set(pos, name, tAttr)
if pos then
local sKey = S(pos)
if not self.tStations[sKey] then
self.tStations[sKey] = {
conn = {},
}
end
self.tStations[sKey].name = name or ""
for k,v in pairs(tAttr) do
self.tStations[sKey][k] = v
end
self.change_counter = self.change_counter + 1
end
end
-- Update an elevator or station entry.
-- tAttr is a table with additional attributes to be stored.
function Network:update(pos, tAttr)
if pos then
local sKey = S(pos)
if self.tStations[sKey] then
for k,v in pairs(tAttr) do
if v == "nil" then
self.tStations[sKey][k] = nil
else
self.tStations[sKey][k] = v
end
end
self.change_counter = self.change_counter + 1
end
end
end
function Network:get(pos)
return pos and self.tStations[S(pos)]
end
-- Delete an elevator or station entry.
function Network:delete(pos)
if pos then
self.tStations[S(pos)] = nil
self.change_counter = self.change_counter + 1
end
end
function Network:changed(counter)
return self.change_counter > counter, self.change_counter
end
-- Update the connection data base. The output dir information is needed
-- to be able to delete a connection, if necessary.
-- Returns true, if data base is changed.
function Network:update_connections(pos, out_dir, conn_pos)
local sKey = S(pos)
local res = false
if not self.tStations[sKey] then
self.tStations[sKey] = {}
res = true
end
if not self.tStations[sKey].conn then
self.tStations[sKey].conn = {}
res = true
end
conn_pos = S(conn_pos)
if self.tStations[sKey].conn[out_dir] ~= conn_pos then
self.tStations[sKey].conn[out_dir] = conn_pos
res = true
end
if res then
self.change_counter = self.change_counter + 1
end
return res
end
-- Return the nearest station position
function Network:get_next_station(pos)
local min_dist = 999999
local min_key = nil
local dist
for key,item in pairs(self.tStations) do
if not item.junction then
dist = distance(pos, P(key))
if dist < min_dist then
min_dist = dist
min_key = key
end
end
end
return P(min_key)
end
-- Return a sorted list of stations
-- Param pos: player pos
-- Param station_pos: next station pos or nil.
-- Used to generate list with connected stations only
-- Param sorted: either "dist" or "level"
function Network:station_list(pos, station_pos, sorted)
local tStations, lStations
if station_pos then
local tRes = {}
tStations = get_stations(self.tStations, S(station_pos), tRes) -- reduced
else
tStations = self.tStations -- all stations
end
if sorted == "dist" then
lStations = sort_based_on_distance(tStations, pos)
elseif sorted == "level" then
lStations = sort_based_on_level(tStations)
else
-- delete own station from list
tStations[S(station_pos)] = nil
lStations = sort_based_on_name(tStations, pos)
end
return lStations
end
-- Check the complete table by means of the provided callback bool = func(pos)
function Network:filter(callback)
local lKeys = {}
for key,_ in pairs(self.tStations) do
lKeys[#lKeys+1] = key
end
for _,key in ipairs(lKeys) do
if not callback(P(key)) then
self.tStations[key] = nil
end
end
end
function Network:deserialize(data)
if data ~= "" then
data = minetest.deserialize(data)
self.tStations = data.tStations
self.change_counter = data.change_counter
end
end
function Network:serialize()
return minetest.serialize(self)
end
-- Return a pos/item table with all network nodes, the node at pos is connected with
function Network:get_node_table(pos)
local tRes = {}
local key = S(pos)
get_stations(self.tStations, key, tRes)
tRes[key] = nil
return tRes
end
+243
View File
@@ -0,0 +1,243 @@
--[[
Hyperloop Mod
=============
Copyright (C) 2017 Joachim Stolberg
LGPLv2.1+
See LICENSE.txt for more information
History:
see init.lua
]]--
-- Load support for intllib.
local S = hyperloop.S
minetest.register_craftitem("hyperloop:hypersteel_ingot", {
description = S("Hypersteel Ingot"),
inventory_image = "hyperloop_hypersteel_ingot.png",
})
if minetest.global_exists("techage") then
minetest.register_craft({
output = "hyperloop:hypersteel_ingot 4",
recipe = {
{"default:steel_ingot", "default:tin_ingot"},
{"techage:aluminum", "dye:cyan"},
},
})
else
minetest.register_craft({
output = "hyperloop:hypersteel_ingot 4",
recipe = {
{"default:steel_ingot", "default:tin_ingot"},
{"default:copper_ingot", "dye:cyan"},
},
})
end
minetest.register_craft({
output = "hyperloop:tubeS 8",
recipe = {
{"", "hyperloop:hypersteel_ingot", ""},
{"hyperloop:hypersteel_ingot", "", "hyperloop:hypersteel_ingot"},
{"", "hyperloop:hypersteel_ingot", ""},
},
})
minetest.register_craft({
output = "hyperloop:pillar 8",
recipe = {
{"", "hyperloop:hypersteel_ingot", ""},
{"", "hyperloop:hypersteel_ingot", ""},
{"", "hyperloop:hypersteel_ingot", ""},
},
})
if minetest.global_exists("techage") then
minetest.register_craft({
output = "hyperloop:pod_wall 6",
recipe = {
{"basic_materials:plastic_sheet", "basic_materials:plastic_sheet", "basic_materials:plastic_sheet"},
{"basic_materials:plastic_sheet", "dye:white", "basic_materials:plastic_sheet"},
{"hyperloop:hypersteel_ingot", "hyperloop:hypersteel_ingot", "hyperloop:hypersteel_ingot"},
},
})
else
minetest.register_craft({
output = "hyperloop:pod_wall 8",
recipe = {
{"hyperloop:hypersteel_ingot", "hyperloop:hypersteel_ingot", "hyperloop:hypersteel_ingot"},
{"hyperloop:hypersteel_ingot", "dye:white", "hyperloop:hypersteel_ingot"},
{"hyperloop:hypersteel_ingot", "hyperloop:hypersteel_ingot", "hyperloop:hypersteel_ingot"},
},
})
end
minetest.register_craft({
output = "hyperloop:booking 1",
recipe = {
{"hyperloop:hypersteel_ingot", "", "hyperloop:hypersteel_ingot"},
{"", "default:paper", ""},
{"hyperloop:hypersteel_ingot", "", "hyperloop:hypersteel_ingot"},
},
})
minetest.register_craft({
output = "hyperloop:junction",
recipe = {
{"", "hyperloop:hypersteel_ingot", ""},
{"hyperloop:hypersteel_ingot", "default:mese_crystal", "hyperloop:hypersteel_ingot"},
{"", "hyperloop:hypersteel_ingot", ""},
},
})
if minetest.global_exists("techage") then
minetest.register_craft({
output = "hyperloop:station",
recipe = {
{"hyperloop:hypersteel_ingot", "default:mese_crystal", "hyperloop:hypersteel_ingot"},
{"", "techage:ta4_wlanchip", ""},
{"hyperloop:hypersteel_ingot", "default:mese_crystal", "hyperloop:hypersteel_ingot"},
},
})
else
minetest.register_craft({
output = "hyperloop:station",
recipe = {
{"hyperloop:hypersteel_ingot", "default:mese_crystal", "hyperloop:hypersteel_ingot"},
{"", "default:mese_crystal", ""},
{"hyperloop:hypersteel_ingot", "default:mese_crystal", "hyperloop:hypersteel_ingot"},
},
})
end
minetest.register_craft({
output = "hyperloop:robot",
recipe = {
{"hyperloop:hypersteel_ingot", "hyperloop:hypersteel_ingot", "hyperloop:hypersteel_ingot"},
{"hyperloop:hypersteel_ingot", "default:mese_crystal", "hyperloop:hypersteel_ingot"},
{"hyperloop:hypersteel_ingot", "hyperloop:hypersteel_ingot", "hyperloop:hypersteel_ingot"},
},
})
if hyperloop.wifi_crafting_enabled then
minetest.register_craft({
output = "hyperloop:tube_wifi1 2",
recipe = {
{"default:mese_crystal", "hyperloop:hypersteel_ingot", "default:mese_crystal"},
{"hyperloop:hypersteel_ingot", "default:mese_crystal", "hyperloop:hypersteel_ingot"},
{"default:mese_crystal", "hyperloop:hypersteel_ingot", "default:mese_crystal"},
},
})
end
minetest.register_craft({
output = "hyperloop:station_map",
recipe = {
{"default:paper", "dye:red", ""},
{"default:paper", "dye:red", ""},
{"default:paper", "dye:red", ""},
},
})
minetest.register_craft({
output = "hyperloop:shaft 8",
recipe = {
{"hyperloop:hypersteel_ingot", "", "hyperloop:hypersteel_ingot"},
{"", "", ""},
{"hyperloop:hypersteel_ingot", "", "hyperloop:hypersteel_ingot"},
},
})
minetest.register_craft({
output = "hyperloop:elevator_bottom 2",
recipe = {
{"", "default:glass", "hyperloop:hypersteel_ingot"},
{"", "dye:red", "default:mese_crystal"},
{"", "default:glass", "hyperloop:hypersteel_ingot"},
},
})
minetest.register_craft({
output = "hyperloop:sign",
recipe = {
{"", "", ""},
{"", "dye:cyan", "hyperloop:hypersteel_ingot"},
{"", "default:wood", "default:wood"},
},
})
minetest.register_craft({
output = "hyperloop:signL 4",
recipe = {
{"", "", ""},
{"", "", ""},
{"dye:cyan", "hyperloop:hypersteel_ingot", "default:wood"},
},
})
minetest.register_craft({
output = "hyperloop:signR 4",
recipe = {
{"", "", ""},
{"", "", ""},
{"default:wood", "hyperloop:hypersteel_ingot", "dye:cyan"},
},
})
minetest.register_craft({
output = "hyperloop:poster1L",
recipe = {
{"", "", ""},
{"", "dye:white", "hyperloop:hypersteel_ingot"},
{"", "dye:blue", "default:wood"},
},
})
minetest.register_craft({
output = "hyperloop:poster2L",
recipe = {
{"", "", ""},
{"", "dye:white", "hyperloop:hypersteel_ingot"},
{"", "dye:cyan", "default:wood"},
},
})
minetest.register_craft({
output = "hyperloop:poster3L",
recipe = {
{"", "", ""},
{"", "dye:white", "hyperloop:hypersteel_ingot"},
{"", "dye:brown", "default:wood"},
},
})
minetest.register_craft({
output = "hyperloop:waypoint",
recipe = {
{"", "", ""},
{"", "", ""},
{"default:steel_ingot", "hyperloop:hypersteel_ingot", "default:gold_ingot"},
},
})
minetest.register_craft({
output = "hyperloop:tube_crowbar",
recipe = {
{"", "", "dye:red"},
{"", "hyperloop:hypersteel_ingot", ""},
{"hyperloop:hypersteel_ingot", "", ""},
},
})
minetest.register_craft({
type = "cooking",
output = "dye:cyan",
recipe = "default:cactus",
cooktime = 3,
})
Binary file not shown.

After

Width:  |  Height:  |  Size: 277 KiB

+235
View File
@@ -0,0 +1,235 @@
--[[
Hyperloop Mod
=============
Copyright (C) 2017-2019 Joachim Stolberg
LGPLv2.1+
See LICENSE.txt for more information
]]--
-- for lazy programmers
--local SP = function(pos) if pos then return minetest.pos_to_string(pos) end end
--local P = minetest.string_to_pos
local M = minetest.get_meta
-- Load support for intllib.
local S = hyperloop.S
local I, _ = dofile( minetest.get_modpath("hyperloop").."/intllib.lua")
local PlayerNameTags = {}
local function enter_display(tStation, text)
-- determine position
if tStation ~= nil then
local lcd_pos = hyperloop.new_pos(tStation.pos, tStation.facedir, "1F", 2)
-- update display
minetest.registered_nodes["hyperloop:lcd"].update(lcd_pos, text)
end
end
local function on_final_close_door(tStation)
-- close the door and play sound if no player is around
if hyperloop.is_player_around(tStation.pos) then
-- try again later
minetest.after(3.0, on_final_close_door, tStation)
else
hyperloop.close_pod_door(tStation)
enter_display(tStation, I(" | | << Hyperloop >> | be anywhere"))
end
end
local function on_open_door(tArrival)
-- open the door and play sound
local meta = minetest.get_meta(tArrival.pos)
meta:set_int("arrival_time", 0) -- finished
-- open door
hyperloop.open_pod_door(tArrival)
-- prepare display for the next trip
enter_display(tArrival, I("Thank you | for | travelling | with | Hyperloop."))
minetest.after(5.0, on_final_close_door, tArrival, tArrival.facedir)
end
local function on_arrival(tDeparture, tArrival, player_name, sound)
local player = minetest.get_player_by_name(player_name)
-- activate display
local text = I(" | Welcome at | | ")..string.sub(tArrival.name, 1, 13)
enter_display(tArrival, text)
-- stop timer
minetest.get_node_timer(tDeparture.pos):stop()
-- move player to the arrival station
if player ~= nil then
local pos = table.copy(tArrival.pos)
pos.y = pos.y + 0.5
player:set_pos(pos)
-- rotate player to look in correct arrival direction
-- calculate the look correction
-- workaround to prevent server crashes
local val1 = hyperloop.facedir_to_rad(tDeparture.facedir)
local val2 = player:get_look_horizontal()
if val1 ~= nil and val2 ~= nil then
local offs = val1 - val2
local yaw = hyperloop.facedir_to_rad(tArrival.facedir) - offs
player:set_look_horizontal(yaw)
end
-- set player name again
if PlayerNameTags[player_name] then
player:set_nametag_attributes(PlayerNameTags[player_name])
PlayerNameTags[player_name] = nil
end
end
-- play arrival sound
minetest.sound_stop(sound)
minetest.sound_play("down2", {
pos = tArrival.pos,
gain = 0.5,
max_hear_distance = 2
})
minetest.after(4.0, on_open_door, tArrival)
end
local function on_travel(tDeparture, tArrival, player_name, atime)
-- play sound and switch door state
local sound = minetest.sound_play("normal2", {
pos = tDeparture.pos,
gain = 0.5,
max_hear_distance = 2,
loop = true,
})
hyperloop.animate_pod_door(tDeparture)
minetest.after(atime, on_arrival, tDeparture, tArrival, player_name, sound)
minetest.after(atime, on_final_close_door, tDeparture)
end
local function display_timer(pos, elapsed)
-- update display with trip data
local tStation = hyperloop.get_base_station(pos)
if tStation then
local meta = M(pos)
local atime = meta:get_int("arrival_time") - 1
meta:set_int("arrival_time", atime)
local text = meta:get_string("lcd_text")
if atime > 2 then
enter_display(tStation, text..atime.." sec")
return true
else
return false
end
end
return false
end
local function meter_to_km(dist)
if dist < 1000 then
return tostring(dist).." m"
elseif dist < 10000 then
return string.format("%.3f km", dist/1000)
else
return string.format("%.1f km", dist/1000)
end
end
-- place the player, close the door, activate display
local function on_start_travel(pos, node, clicker)
-- arrival data
local meta = M(pos)
local player_name = clicker:get_player_name()
local tDeparture, departure_pos = hyperloop.get_base_station(pos)
local arrival_pos = hyperloop.get_arrival(departure_pos)
if arrival_pos == nil then
minetest.chat_send_player(player_name, S("[Hyperloop] No booking entered!"))
return
end
local tArrival = hyperloop.get_station(arrival_pos)
if tDeparture == nil or tArrival == nil then
return
end
minetest.sound_play("up2", {
pos = pos,
gain = 0.5,
max_hear_distance = 2
})
-- close the door at arrival station
hyperloop.close_pod_door(tArrival)
-- place player on the seat
pos.y = pos.y - 0.5
clicker:set_pos(pos)
-- rotate player to look in move direction
clicker:set_look_horizontal(hyperloop.facedir_to_rad(tDeparture.facedir))
-- hide player name
PlayerNameTags[player_name] = clicker:get_nametag_attributes()
clicker:set_nametag_attributes({text = " "})
-- activate display
local dist = hyperloop.distance(pos, tArrival.pos)
local text = I("Destination:").." | "..string.sub(tArrival.name, 1, 13).." | "..I("Distance:").." | "..
meter_to_km(dist).." | "..I("Arrival in:").." | "
local atime
if dist < 1000 then
atime = 10 + math.floor(dist/200) -- 10..15 sec
elseif dist < 10000 then
atime = 15 + math.floor(dist/600) -- 16..32 sec
else
atime = 32 -- 32 sec is the maximum
end
enter_display(tDeparture, text..atime.." sec")
-- block departure and arrival stations
hyperloop.block(departure_pos, arrival_pos, atime+10)
-- store some data for on_timer()
meta:set_int("arrival_time", atime)
meta:set_string("lcd_text", text)
minetest.get_node_timer(pos):start(1.0)
hyperloop.close_pod_door(tDeparture)
atime = atime - 7 -- substract start/arrival time
minetest.after(4.9, on_travel, tDeparture, tArrival, player_name, atime)
end
-- Hyperloop Seat
minetest.register_node("hyperloop:seat", {
description = S("Hyperloop Pod Seat"),
tiles = {
"hyperloop_seat-top.png",
"hyperloop_seat-side.png",
"hyperloop_seat-side.png",
"hyperloop_seat-side.png",
"hyperloop_seat-side.png",
"hyperloop_seat-side.png",
},
drawtype = "nodebox",
paramtype = 'light',
light_source = 1,
paramtype2 = "facedir",
is_ground_content = false,
walkable = false,
drop = "",
groups = {not_in_creative_inventory=1, crumbly=3},
node_box = {
type = "fixed",
fixed = {
{ -6/16, -8/16, -8/16, 6/16, -2/16, 5/16},
{ -8/16, -8/16, -8/16, -6/16, 4/16, 8/16},
{ 6/16, -8/16, -8/16, 8/16, 4/16, 8/16},
{ -6/16, -8/16, 4/16, 6/16, 6/16, 8/16},
},
},
selection_box = {
type = "fixed",
fixed = { -8/16, -8/16, -8/16, 8/16, -2/16, 8/16 },
},
on_timer = display_timer,
on_rightclick = on_start_travel,
on_rotate = screwdriver.disallow,
auto_place_node = function(pos, facedir, sStationPos)
M(pos):set_string("sStationPos", sStationPos)
end,
})
@@ -0,0 +1,15 @@
# Enable WiFi blocks (exclude recipe)
hyperloop_wifi_enabled (WiFi block enabled) bool false
# Enable WiFi block crafting
hyperloop_wifi_crafting_enabled (WiFi block crafting enabled) bool false
# Enable free tube placement with no level limitation.
# If disabled, connected stations have to be on one level,
# typically underground.
hyperloop_free_tube_placement_enabled (free tube placement enabled) bool false
# The ticket block has an additional field for specifying a subnet name.
# Stations with the same subnet name (optional) represent an isolated
# subnet within the Hyperloop network.
hyperloop_subnet_enabled (enable building of subnets) bool true
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+353
View File
@@ -0,0 +1,353 @@
--[[
Hyperloop Mod
=============
Copyright (C) 2017-2019 Joachim Stolberg
LGPLv2.1+
See LICENSE.txt for more information
]]--
-- for lazy programmers
local SP = function(pos) if pos then return minetest.pos_to_string(pos) end end
--local P = minetest.string_to_pos
local M = minetest.get_meta
-- Load support for intllib.
local S = hyperloop.S
local Tube = hyperloop.Tube
local Stations = hyperloop.Stations
-- Station Pod Assembly Plan
local AssemblyPlan = {
-- y-offs, x/z-path, facedir-offs, name
-- middle slice
{ 1, "2F", 0, "hyperloop:pod_wall_ni"},
{ 1, "", 0, "hyperloop:pod_wall_ni"},
{ 1, "", 0, "hyperloop:pod_wall_ni"},
{ 0, "1B", 0, "hyperloop:pod_wall_ni"},
{ 0, "1B", 0, "hyperloop:pod_wall_ni"},
{ 0, "1B", 0, "hyperloop:pod_wall_ni"},
{-1, "", 0, "hyperloop:pod_wall_ni"},
{-1, "", 0, "hyperloop:pod_wall_ni"},
{ 0, "1F", 2, "hyperloop:seat"},
{ 0, "1F", 0, "hyperloop:pod_floor"},
{ 1, "", 0, "hyperloop:lcd"},
-- right slice
{-1, "1F1R", 0, "hyperloop:pod_wall_ni"},
{ 1, "", 0, "hyperloop:pod_wall_ni"},
{ 1, "", 0, "hyperloop:pod_wall_ni"},
{ 0, "1B", 0, "hyperloop:pod_wall_ni"},
{ 0, "1B", 0, "hyperloop:pod_wall_ni"},
{ 0, "1B", 0, "hyperloop:pod_wall_ni"},
{-1, "", 0, "hyperloop:pod_wall_ni"},
{-1, "", 0, "hyperloop:pod_wall_ni"},
{ 0, "1F", 0, "hyperloop:pod_wall_ni"},
{ 0, "1F", 0, "hyperloop:pod_wall_ni"},
{ 1, "", 0, "hyperloop:pod_wall_ni"},
{ 0, "1B", 0, "hyperloop:pod_wall_ni"},
-- left slice
{-1, "2L2R", 0, "hyperloop:pod_wall_ni"},
{ 1, "", 0, "hyperloop:pod_wall_ni"},
{ 1, "", 0, "hyperloop:pod_wall_ni"},
{ 0, "1B", 0, "hyperloop:pod_wall_ni"},
{ 0, "1B", 0, "hyperloop:pod_wall_ni"},
{ 0, "1B", 0, "hyperloop:pod_wall_ni"},
{-1, "", 0, "hyperloop:pod_wall_ni"},
{-1, "", 0, "hyperloop:pod_wall_ni"},
{ 0, "1F", 0, "hyperloop:pod_wall_ni"},
{ 1, "", 0, "hyperloop:pod_wall_ni"},
{ 0, "1F", 1, "hyperloop:doorTopPassive"},
{-1, "", 1, "hyperloop:doorBottom"},
}
local function store_station(pos, placer)
local facedir = hyperloop.get_facedir(placer)
-- do a facedir correction
facedir = (facedir + 3) % 4 -- face to LCD
Stations:set(pos, "Station", {
owner = placer:get_player_name(),
facedir = facedir,
time_blocked = 0})
end
-- Calls the node related "auto_place_node()" callback.
local function call_auto_place_node(name, pos, facedir, sKey)
local node = minetest.registered_nodes[name]
if node.auto_place_node ~= nil then
node.auto_place_node(pos, facedir, sKey)
end
end
local function place_node(pos, facedir, node_name, sKey)
if node_name == "hyperloop:lcd" then
-- wallmounted devices need a facedir correction
local tbl = {[0]=4, [1]=2, [2]=5, [3]=3}
minetest.add_node(pos, {name=node_name, paramtype2="wallmounted", param2=tbl[facedir]})
else
minetest.add_node(pos, {name=node_name, param2=facedir})
end
call_auto_place_node(node_name, pos, facedir, sKey)
end
-- timer function, called cyclically
local function construct(idx, pos, facedir, player_name, sKey)
local item = AssemblyPlan[idx]
if item ~= nil then
local y, path, fd_offs, node_name = item[1], item[2], item[3], item[4]
pos = hyperloop.new_pos(pos, facedir, path, y)
place_node(pos, (facedir + fd_offs) % 4, node_name, sKey)
minetest.after(0.5, construct, idx+1, pos, facedir, player_name, sKey)
else
hyperloop.chat(player_name, S("Station completed. Now place the Booking Machine!"))
end
end
local function check_space(pos, facedir, placer)
for _,item in ipairs(AssemblyPlan) do
local y, path, _ = item[1], item[2], item[4]
pos = hyperloop.new_pos(pos, facedir, path, y)
if minetest.is_protected(pos, placer:get_player_name()) then
hyperloop.chat(placer, S("Area is protected!"))
return false
elseif minetest.get_node_or_nil(pos).name ~= "air" then
hyperloop.chat(placer, S("Not enough space to build the station!"))
return false
end
end
return true
end
local station_formspec =
"size[8,9]"..
default.gui_bg..
default.gui_bg_img..
default.gui_slots..
"label[2,0;"..S("Hyperloop Station Pod Builder").."]" ..
"image[0.2,0.9;3,3;hyperloop_station_formspec.png]"..
"list[context;src;3,0.9;1,4;]"..
"label[4,1.2;30 x "..S("Hyperloop Pod Shell").."]" ..
"item_image[3,0.9;1,1;hyperloop:pod_wall]"..
"label[4,2.2;4 x "..S("Hypersteel Ingot").."]" ..
"item_image[3,1.9;1,1;hyperloop:hypersteel_ingot]"..
"label[4,3.2;2 x "..S("Blue Wool").."]" ..
"item_image[3,2.9;1,1;wool:blue]"..
"label[4,4.2;2 x "..S("Glass").."]" ..
"item_image[3,3.9;1,1;default:glass]"..
"list[current_player;main;0,5.3;8,4;]"..
"listring[context;src]"..
"listring[current_player;main]"
local function allow_metadata_inventory(pos, listname, index, stack, player)
if minetest.is_protected(pos, player:get_player_name()) then
return 0
end
if M(pos):get_int("busy") == 1 then
return 0
end
return stack:get_count()
end
local function check_inventory(inv, player)
local list = inv:get_list("src")
if list[1]:get_name() == "hyperloop:pod_wall" and list[1]:get_count() >= 30 then
if list[2]:get_name() == "hyperloop:hypersteel_ingot" and list[2]:get_count() >= 4 then
if list[3]:get_name() == "wool:blue" and list[3]:get_count() >= 2 then
if list[4]:get_name() == "default:glass" and list[4]:get_count() >= 2 then
return true
end
end
end
end
hyperloop.chat(player, S("Not enough inventory items to build the station!"))
return false
end
local function remove_inventory_items(inv, meta)
inv:remove_item("src", ItemStack("hyperloop:pod_wall 30"))
inv:remove_item("src", ItemStack("hyperloop:hypersteel_ingot 4"))
inv:remove_item("src", ItemStack("wool:blue 2"))
inv:remove_item("src", ItemStack("default:glass 2"))
meta:set_int("busy", 0)
end
local function add_inventory_items(inv)
inv:add_item("src", ItemStack("hyperloop:pod_wall 30"))
inv:add_item("src", ItemStack("hyperloop:hypersteel_ingot 4"))
inv:add_item("src", ItemStack("wool:blue 2"))
inv:add_item("src", ItemStack("default:glass 2"))
end
local function build_station(pos, placer)
-- check protection
if minetest.is_protected(pos, placer:get_player_name()) then
return
end
local meta = M(pos)
local inv = meta:get_inventory()
local facedir = hyperloop.get_facedir(placer)
-- do a facedir correction
facedir = (facedir + 3) % 4 -- face to LCD
if check_inventory(inv, placer) then
Stations:update(pos, {facedir = facedir})
if check_space(table.copy(pos), facedir, placer) then
construct(1, table.copy(pos), facedir, placer:get_player_name(), SP(pos))
meta:set_string("formspec", station_formspec ..
"button_exit[0,3.9;3,1;destroy;"..S("Destroy Station").."]")
meta:set_int("built", 1)
meta:set_int("busy", 1)
-- remove items aften the station is build
minetest.after(20, remove_inventory_items, inv, meta)
end
end
end
local function on_destruct(pos)
Stations:update(pos, {
booking_pos = "nil",
booking_info = "nil",
name = "Station",
})
end
local function destroy_station(pos, player_name)
-- check protection
if minetest.is_protected(pos, player_name) then
return
end
local station = Stations:get(pos)
if station then
-- remove nodes
local _pos = table.copy(pos)
for _,item in ipairs(AssemblyPlan) do
local y, path, _ = item[1], item[2], item[4]
_pos = hyperloop.new_pos(_pos, station.facedir, path, y)
minetest.remove_node(_pos)
end
on_destruct(pos)
-- maintain meta
local meta = M(pos)
meta:set_string("formspec", station_formspec ..
"button_exit[0,3.9;3,1;build;"..S("Build Station").."]")
local inv = meta:get_inventory()
add_inventory_items(inv)
meta:set_int("built", 0)
else
M(pos):set_int("built", 0)
end
end
minetest.register_node("hyperloop:station", {
description = S("Hyperloop Station Block"),
drawtype = "nodebox",
tiles = {
"hyperloop_station.png",
"hyperloop_station_connection.png",
"hyperloop_station_connection.png",
},
on_construct = function(pos)
local meta = M(pos)
meta:set_string("formspec", station_formspec ..
"button_exit[0,3.9;3,1;build;"..S("Build Station").."]")
local inv = meta:get_inventory()
inv:set_size('src', 4)
end,
after_place_node = function(pos, placer, itemstack, pointed_thing)
hyperloop.check_network_level(pos, placer)
M(pos):set_string("infotext", S("Station"))
store_station(pos, placer)
Tube:after_place_node(pos)
end,
allow_metadata_inventory_put = allow_metadata_inventory,
allow_metadata_inventory_take = allow_metadata_inventory,
on_receive_fields = function(pos, formname, fields, player)
if fields.destroy ~= nil then
destroy_station(pos, player:get_player_name())
elseif fields.build ~= nil then
build_station(pos, player)
end
end,
on_dig = function(pos, node, puncher, pointed_thing)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
if inv:is_empty("src") and meta:get_int("built") ~= 1 then
minetest.node_dig(pos, node, puncher, pointed_thing)
end
end,
after_dig_node = function(pos, oldnode, oldmetadata, digger)
Tube:after_dig_node(pos)
Stations:delete(pos)
end,
on_rotate = screwdriver.disallow,
paramtype2 = "facedir",
groups = {cracky = 1},
is_ground_content = false,
sounds = default.node_sound_metal_defaults(),
})
minetest.register_node("hyperloop:pod_wall", {
description = S("Hyperloop Pod Shell"),
tiles = {
-- up, down, right, left, back, front
"hyperloop_skin2.png",
"hyperloop_skin2.png",
"hyperloop_skin.png",
},
on_rotate = screwdriver.disallow,
paramtype2 = "facedir",
groups = {cracky=2},
is_ground_content = false,
sounds = default.node_sound_metal_defaults(),
})
minetest.register_node("hyperloop:pod_wall_ni", {
description = S("Hyperloop Pod Shell"),
tiles = {
-- up, down, right, left, back, front
"hyperloop_skin2.png",
"hyperloop_skin2.png",
"hyperloop_skin.png",
},
on_rotate = screwdriver.disallow,
paramtype2 = "facedir",
groups = {cracky=2, not_in_creative_inventory=1},
is_ground_content = false,
sounds = default.node_sound_metal_defaults(),
drop = "",
})
minetest.register_node("hyperloop:pod_floor", {
description = S("Hyperloop Pod Shell"),
tiles = {
-- up, down, right, left, back, front
"hyperloop_skin2.png",
},
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
{-8/16, -8/16, -8/16, 8/16, -7.5/16, 8/16},
},
},
on_rotate = screwdriver.disallow,
paramtype2 = "facedir",
groups = {cracky=2, not_in_creative_inventory=1},
is_ground_content = false,
sounds = default.node_sound_metal_defaults(),
drop = "",
})
Binary file not shown.

After

Width:  |  Height:  |  Size: 136 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 B

Some files were not shown because too many files have changed in this diff Show More