aboutsummaryrefslogtreecommitdiff
path: root/advtrains
diff options
context:
space:
mode:
Diffstat (limited to 'advtrains')
-rw-r--r--advtrains/copytool.lua185
-rw-r--r--advtrains/crafting.lua2
-rw-r--r--advtrains/init.lua34
-rw-r--r--advtrains/log.lua8
-rw-r--r--advtrains/misc_nodes.lua33
-rw-r--r--advtrains/models/advtrains_platform_diag.b3dbin0 -> 1970 bytes
-rw-r--r--advtrains/path.lua91
-rw-r--r--advtrains/textures/advtrains_copytool.pngbin0 -> 779 bytes
-rw-r--r--advtrains/textures/advtrains_platform_diag.pngbin0 -> 93 bytes
-rw-r--r--advtrains/trainlogic.lua4
-rw-r--r--advtrains/wagons.lua8
11 files changed, 315 insertions, 50 deletions
diff --git a/advtrains/copytool.lua b/advtrains/copytool.lua
new file mode 100644
index 0000000..dc18081
--- /dev/null
+++ b/advtrains/copytool.lua
@@ -0,0 +1,185 @@
+--clipboard = {trainlen = number, [n] = {type = string, flipped = bool, }
+
+-- Yaw is in radians. There are 2pi rad in a circle. North is the 0 point and the angle increases anticlockwise.
+-- 4.712389 = 1.5pi; sin(1.5pi) = -1
+-- 7.853981 = 2.5pi; sin(2.5pi) = 1
+
+minetest.register_tool("advtrains:copytool", {
+ description = attrans("Train copy/paste tool\n\nLeft-click: copy train\nRight-click: paste train"),
+ inventory_image = "advtrains_copytool.png",
+ wield_image = "advtrains_copytool.png",
+ stack_max = 1,
+ -- Paste: Take the clipboard and the player yaw, and attempt to place a new train in the world.
+ -- The front of the train is used as the start of the new train and it proceeds backwards from
+ -- the direction of travel.
+ on_place = function(itemstack, placer, pointed_thing)
+ return advtrains.pcall(function()
+ if ((not pointed_thing.type == "node") or (not placer.get_player_name)) then
+ return
+ end
+ local pname = placer:get_player_name()
+
+ local node=minetest.get_node_or_nil(pointed_thing.under)
+ if not node then atprint("[advtrains]Ignore at placer position") return itemstack end
+ local nodename=node.name
+ if(not advtrains.is_track_and_drives_on(nodename, {default=true})) then
+ atprint("no track here, not placing.")
+ return itemstack
+ end
+ if not minetest.check_player_privs(placer, {train_operator = true }) then
+ minetest.chat_send_player(pname, "You don't have the train_operator privilege.")
+ return itemstack
+ end
+ if not minetest.check_player_privs(placer, {train_admin = true }) and minetest.is_protected(pointed_thing.under, placer:get_player_name()) then
+ return itemstack
+ end
+ local tconns=advtrains.get_track_connections(node.name, node.param2)
+ local yaw = placer:get_look_horizontal()
+ local plconnid = advtrains.yawToClosestConn(yaw, tconns)
+
+ local prevpos = advtrains.get_adjacent_rail(pointed_thing.under, tconns, plconnid, {default=true})
+ if not prevpos then
+ minetest.chat_send_player(pname, "The track you are trying to place the wagon on is not long enough!")
+ return
+ end
+
+ local meta = itemstack:get_meta()
+ if not meta then
+ minetest.chat_send_player(pname, attrans("The clipboard couldn't access the metadata. Paste failed."))
+ return
+ end
+ local clipboard = meta:get_string("clipboard")
+ if (clipboard == "") then
+ minetest.chat_send_player(pname, "The clipboard is empty.");
+ return
+ end
+ clipboard = minetest.deserialize(clipboard)
+ if (clipboard.wagons == nil) then
+ minetest.chat_send_player(pname, "The clipboard is empty.");
+ return
+ end
+
+ local wagons = {}
+ local n = 1
+ for _, wagonProto in pairs(clipboard.wagons) do
+ local wagon = advtrains.create_wagon(wagonProto.type, pname)
+ advtrains.wagons[wagon].wagon_flipped = wagonProto.wagon_flipped
+ wagons[n] = wagon
+ n = n + 1
+ end
+
+ local id=advtrains.create_new_train_at(pointed_thing.under, plconnid, 0, wagons)
+ local train = advtrains.trains[id]
+ train.off_track = train.end_index<train.path_trk_b
+ if (train.off_track) then
+ minetest.chat_send_player(pname, "Back of train would end up off track, cancelling.")
+ advtrains.remove_train(id)
+ return
+ end
+ train.text_outside = clipboard.text_outside
+ train.text_inside = clipboard.text_inside
+ train.routingcode = clipboard.routingcode
+ train.line = clipboard.line
+
+ return
+ end)
+ end,
+ -- Copy: Take the pointed-at train and put it on the clipboard
+ on_use = function(itemstack, user, pointed_thing)
+ if not user:get_player_name() then return end
+ if (pointed_thing.type ~= "object") then return end
+
+ local le = pointed_thing.ref:get_luaentity()
+ if (le == nil) then
+ minetest.chat_send_player(user:get_player_name(), "No such lua entity!")
+ return
+ end
+
+ local wagon = advtrains.wagons[le.id]
+ if (not (le.id and advtrains.wagons[le.id])) then
+ minetest.chat_send_player(user:get_player_name(), string.format("No such wagon: %s", le.id))
+ return
+ end
+
+ local train = advtrains.trains[wagon.train_id]
+ if (not train) then
+ minetest.chat_send_player(user:get_player_name(), string.format("No such train: %s", wagon.train_id))
+ return
+ end
+
+ -- Record the train length. The paste operation should require this much free space.
+ local clipboard = {
+ trainlen = math.ceil(train.trainlen),
+ text_outside = train.text_outside,
+ text_inside = train.text_inside,
+ routingcode = train.routingcode,
+ line = train.line,
+ wagons = {}
+ }
+ local trainLength = math.ceil(train.trainlen)
+
+ local n = 1
+ for _, wagonid in pairs(train.trainparts) do
+ local wagon = advtrains.wagons[wagonid]
+ clipboard.wagons[n] = {
+ wagon_flipped = wagon.wagon_flipped,
+ type = wagon.type
+ }
+ n = n + 1
+ end
+
+
+ local function flip_clipboard(wagon_clipboard)
+ local flipped = {}
+ local numWagons = #wagon_clipboard
+ for k, v in ipairs(wagon_clipboard) do
+ local otherSide = (numWagons-k)+1
+ flipped[otherSide] = v
+ local wagon = flipped[otherSide]
+ wagon.wagon_flipped = not wagon.wagon_flipped
+ end
+ return flipped
+ end
+
+ local function is_loco(wagon_id)
+ local wagon = advtrains.wagons[wagon_id]
+ if (not wagon) then return false end
+ local wagon_proto = advtrains.wagon_prototypes[wagon.type or wagon.entity_name]
+ if wagon_proto and wagon_proto.is_locomotive then
+ return true
+ end
+ return false
+ end
+
+ -- Decide on a new 'front of train' and possibly flip the train.
+ -- Locomotive on one end = loco-hauled, that end is front;
+ -- if (advtrains.wagons[train.trainparts[1]].is_locomotive) then -- do nothing, train is already in right order
+ local numWagons = #train.trainparts
+ local backLoco = train.trainparts[numWagons]
+ backLoco = is_loco(backLoco)
+ local frontLoco = train.trainparts[1]
+ frontLoco = is_loco(frontLoco)
+ if ((backLoco) and (not frontLoco)) then
+ clipboard.wagons = flip_clipboard(clipboard.wagons)
+ --minetest.chat_send_player(user:get_player_name(), "Flipped train: Loco-hauled")
+ end
+ -- locomotives on both ends = train is push-pull / multi-unit, has no front, do nothing
+ -- no locomotives on ends = rake of wagons, front is end closest to where player copied.
+ if ((not frontLoco) and (not backLoco)) then
+
+ if (wagon.pos_in_trainparts / numWagons > 0.5) then -- towards the end of the rain
+ clipboard.wagons = flip_clipboard(clipboard.wagons)
+ --minetest.chat_send_player(user:get_player_name(), "Flipped train: Rake")
+ end
+ end
+
+ local meta = itemstack:get_meta()
+ if not meta then
+ minetest.chat_send_player(pname, attrans("The clipboard couldn't access the metadata. Copy failed."))
+ return
+ end
+ meta:set_string("clipboard", minetest.serialize(clipboard))
+ minetest.chat_send_player(user:get_player_name(), attrans("Train copied!"))
+ return itemstack
+ end
+}) \ No newline at end of file
diff --git a/advtrains/crafting.lua b/advtrains/crafting.lua
index 72cd6da..d7e7343 100644
--- a/advtrains/crafting.lua
+++ b/advtrains/crafting.lua
@@ -24,7 +24,7 @@ minetest.register_craft({
minetest.register_craft({
output = 'advtrains:dtrack_bumper_placer 2',
recipe = {
- {'default:wood', 'dye:red'},
+ {'group:wood', 'dye:red'},
{'default:steel_ingot', 'default:steel_ingot'},
{'advtrains:dtrack_placer', 'advtrains:dtrack_placer'},
},
diff --git a/advtrains/init.lua b/advtrains/init.lua
index c3298a2..828502a 100644
--- a/advtrains/init.lua
+++ b/advtrains/init.lua
@@ -1,4 +1,24 @@
+--[[
+Advanced Trains - Minetest Mod
+
+Copyright (C) 2016-2020 Moritz Blei (orwell96) and contributors
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+]]
+
local lot = os.clock()
minetest.log("action", "[advtrains] Loading...")
@@ -177,6 +197,7 @@ dofile(advtrains.modpath.."/path.lua")
dofile(advtrains.modpath.."/trainlogic.lua")
dofile(advtrains.modpath.."/trainhud.lua")
dofile(advtrains.modpath.."/trackplacer.lua")
+dofile(advtrains.modpath.."/copytool.lua")
dofile(advtrains.modpath.."/tracks.lua")
dofile(advtrains.modpath.."/occupation.lua")
dofile(advtrains.modpath.."/atc.lua")
@@ -343,15 +364,12 @@ advtrains.save_component = function (tbl, name)
minetest.log("error", " Failed to serialize advtrains save data!")
return
end
- local temppath = advtrains.fpath.."_"..name.."~"
- local file, err = io.open(temppath, "w")
- if err then
- minetest.log("error", " Failed to write advtrains save data to file "..temppath..": "..(err or "Unknown Error"))
- return
+ local path = advtrains.fpath.."_"..name
+ local success = minetest.safe_file_write(path, datastr)
+
+ if not success then
+ minetest.log("error", " Failed to write advtrains save data to file "..path)
end
- file:write(datastr)
- file:close()
- os.rename(temppath, advtrains.fpath.."_"..name)
end
diff --git a/advtrains/log.lua b/advtrains/log.lua
index d2b71d3..d7053a2 100644
--- a/advtrains/log.lua
+++ b/advtrains/log.lua
@@ -5,9 +5,13 @@ advtrains.log = function() end
if minetest.settings:get_bool("advtrains_enable_logging") then
advtrains.logfile = advtrains.fpath .. "_log"
+ local log = io.open(advtrains.logfile, "a+")
+
function advtrains.log (event, player, pos, data)
- local log = io.open(advtrains.logfile, "a+")
log:write(os.date()..": "..event.." by "..player.." at "..minetest.pos_to_string(pos).." -- "..(data or "").."\n")
- log:close()
end
+
+ minetest.register_on_shutdown(function()
+ log:close()
+ end)
end
diff --git a/advtrains/misc_nodes.lua b/advtrains/misc_nodes.lua
index 85fb0ef..f1fb030 100644
--- a/advtrains/misc_nodes.lua
+++ b/advtrains/misc_nodes.lua
@@ -38,14 +38,36 @@ function advtrains.register_platform(modprefix, preset)
node_box = {
type = "fixed",
fixed = {
- {-0.5, 0.3, -0.1, 0.5, 0.5, 0.5},
- {-0.5, -0.5, 0 , 0.5, 0.3, 0.5}
+ {-0.5, 0.3, 0, 0.5, 0.5, 0.5},
+ {-0.5, -0.5, 0.1 , 0.5, 0.3, 0.5}
},
},
paramtype2="facedir",
paramtype = "light",
sunlight_propagates = true,
})
+ local diagonalbox = {
+ type = "fixed",
+ fixed = {
+ {-0.5, -0.5, 0.5, -0.25, 0.5, -0.8 },
+ {-0.25, -0.5, 0.5 , 0, 0.5, -0.55},
+ {0, -0.5, 0.5 , 0.25, 0.5, -0.3 },
+ {0.25 , -0.5, 0.5, 0.5, 0.5, -0.05}
+ }
+ }
+ minetest.register_node(modprefix..":platform_45_"..nodename, {
+ description = attrans("@1 Platform (45 degree)", desc),
+ groups = {cracky = 1, not_blocking_trains = 1, platform=2},
+ sounds = default.node_sound_stone_defaults(),
+ drawtype = "mesh",
+ mesh = "advtrains_platform_diag.b3d",
+ selection_box = diagonalbox,
+ collision_box = diagonalbox,
+ tiles = {btex, btex.."^advtrains_platform_diag.png"},
+ paramtype2 = "facedir",
+ paramtype = "light",
+ sunlight_propagates = true,
+ })
minetest.register_craft({
type="shapeless",
output = modprefix .. ":platform_high_"..nodename.." 4",
@@ -60,6 +82,13 @@ function advtrains.register_platform(modprefix, preset)
"dye:yellow", preset
},
})
+ minetest.register_craft({
+ type="shapeless",
+ output = modprefix .. ":platform_45_"..nodename.." 2",
+ recipe = {
+ "dye:yellow", preset, preset, preset
+ }
+ })
end
diff --git a/advtrains/models/advtrains_platform_diag.b3d b/advtrains/models/advtrains_platform_diag.b3d
new file mode 100644
index 0000000..46c8bd9
--- /dev/null
+++ b/advtrains/models/advtrains_platform_diag.b3d
Binary files differ
diff --git a/advtrains/path.lua b/advtrains/path.lua
index 825e59b..ff034c9 100644
--- a/advtrains/path.lua
+++ b/advtrains/path.lua
@@ -17,12 +17,18 @@
-- path_cn - Connid of the current node that points towards path[i+1]
-- path_cp - Connid of the current node that points towards path[i-1]
-- When the day comes on that path!=node, these will only be set if this index represents a transition between rail nodes
--- path_dist - The distance (in meters) between this (path[i]) and the next (path[i+1]) item of the path
+-- path_dist - The total distance of this path element from path element 0
-- path_dir - The direction of this path item's transition to the next path item, which is the angle of conns[path_cn[i]].c
--Variables:
-- path_ext_f/b - how far path[i] is set
-- path_trk_f/b - how far the path extends along a track. beyond those values, paths are generated in a straight line.
-- path_req_f/b - how far path items were requested in the last step
+--
+--Distance and index:
+-- There is an important difference between the path index and the actual distance on the track: The distance between two path items can be larger than 1,
+-- but the corresponding index increment is still 1.
+-- Indexes in advtrains can be fractional values. If they are, it means that the actual position is interpolated between the 2 adjacent path items.
+-- If you need to proceed along the path by a specific actual distance, it does NOT work to simply add it to the index. You should use the path_get_index_by_offset() function.
-- creates the path data structure, reconstructing the train from a position and a connid
-- Important! train.drives_on must exist while calling this method
@@ -40,7 +46,7 @@ function advtrains.path_create(train, pos, connid, rel_index)
train.path = { [0] = { x=posr.x, y=posr.y+rhe, z=posr.z } }
train.path_cn = { [0] = connid }
train.path_cp = { [0] = mconnid }
- train.path_dist = {}
+ train.path_dist = { [0] = 0 }
train.path_dir = {
[0] = advtrains.conn_angle_median(conns[mconnid].c, conns[connid].c)
@@ -135,12 +141,12 @@ function advtrains.path_print(train, printf)
printf("path_print: Path is invalidated/inexistant.")
return
end
- printf("i: CP Position Dir CN ->Dist->")
+ printf("i: CP Position Dir CN Dist")
for i = train.path_ext_b, train.path_ext_f do
if i==train.path_trk_b then
printf("--Back on-track border here--")
end
- printf(i,": ",train.path_cp[i]," ",train.path[i]," ",train.path_dir[i]," ",train.path_cn[i]," ->",train.path_dist[i],"->")
+ printf(i,": ",train.path_cp[i]," ",train.path[i]," ",train.path_dir[i]," ",train.path_cn[i]," ",train.path_dist[i],"")
if i==train.path_trk_f then
printf("--Front on-track border here--")
end
@@ -156,7 +162,9 @@ function advtrains.path_get(train, index)
if index ~= atfloor(index) then
error("For train "..train.id..": Called path_get() but index="..index.." is not a round number")
end
+
local pef = train.path_ext_f
+ -- generate forward (front of train, positive)
while index > pef do
local pos = train.path[pef]
local connid = train.path_cn[pef]
@@ -183,10 +191,13 @@ function advtrains.path_get(train, index)
train.path_dir[pef] = train.path_dir[pef-1]
end
train.path[pef] = adj_pos
- train.path_dist[pef - 1] = vector.distance(pos, adj_pos)
+ train.path_dist[pef] = train.path_dist[pef-1] + vector.distance(pos, adj_pos)
end
train.path_ext_f = pef
+
+
local peb = train.path_ext_b
+ -- generate backward (back of train, negative)
while index < peb do
local pos = train.path[peb]
local connid = train.path_cp[peb]
@@ -213,7 +224,7 @@ function advtrains.path_get(train, index)
train.path_dir[peb] = train.path_dir[peb+1]
end
train.path[peb] = adj_pos
- train.path_dist[peb] = vector.distance(pos, adj_pos)
+ train.path_dist[peb] = train.path_dist[peb+1] - vector.distance(pos, adj_pos)
end
train.path_ext_b = peb
@@ -256,37 +267,53 @@ function advtrains.path_get_adjacent(train, index)
return p_floor, p_ceil, frac
end
+local function n_interpolate(s, e, f)
+ return s + (e-s)*f
+end
+
+-- This function determines the index resulting from moving along the path by 'offset' meters
+-- starting from 'index'. See also the comment on the top of the file.
function advtrains.path_get_index_by_offset(train, index, offset)
- local off = offset
- local idx = atfloor(index)
- -- go down to floor. Calculate required path_dist
- advtrains.path_get_adjacent(train, idx)
- off = off + ((index-idx) * train.path_dist[idx])
- --atdebug("pibo: 1 off=",off,"idx=",idx," index=",index)
+ -- Step 1: determine my current absolute pos on the path
+ local start_index_f = math.floor(index)
+ local _, _, frac = advtrains.path_get_adjacent(train, index)
+ local dist1, dist2 = train.path_dist[start_index_f], train.path_dist[start_index_f+1]
+ local start_dist = n_interpolate(dist1, dist2, frac)
+
+ -- Step 2: determine the total end distance and estimate the index we'd come out
+ local end_dist = start_dist + offset
+
+ local c_idx = math.floor(index + offset)
+
+ -- Step 3: move forward/backward to find real index
+ -- We assume here that the distance between 2 path items is never smaller than 1.
+ -- Our estimated index is therefore either exact or too far over, and we're going to go back
+ -- towards the origin. It is therefore sufficient to query path_get a single time
- -- then walk the path back until we overshoot (off becomes >=0)
- while off<0 do
- idx = idx - 1
- advtrains.path_get_adjacent(train, idx)
- off = off + train.path_dist[idx]
+ -- How we'll adjust c_idx
+ -- Desired position: -------#------
+ -- Path items : --|--|--|--|--
+ -- c_idx : ^
+
+ advtrains.path_get_adjacent(train, c_idx)
+
+ while train.path_dist[c_idx] < end_dist do
+ c_idx = c_idx + 1
end
- --atdebug("pibo: 2 off=",off,"idx=",idx)
- -- then walk the path forward until we would overshoot
- while off - train.path_dist[idx] >= 0 do
- idx = idx + 1
- advtrains.path_get_adjacent(train, idx)
- if not train.path_dist[idx] then
- for i=-5,5 do
- atdebug(idx+i,train.path_dist[idx+i])
- end
- end
- off = off - train.path_dist[idx]
+
+ while train.path_dist[c_idx] > end_dist do
+ c_idx = c_idx - 1
end
- --atdebug("pibo: 3 off=",off,"idx=",idx," returns:",idx + (off / train.path_dist[idx]))
- -- we should now be on the floor of the index we actually want.
- -- give them the rest!
- return idx + (off / train.path_dist[idx])
+ -- Step 4: now c_idx points to the place shown above. Find out the fractional part.
+
+ dist1, dist2 = train.path_dist[c_idx], train.path_dist[c_idx+1]
+
+ frac = (end_dist - dist1) / (dist2 - dist1)
+
+ assert(frac>=0 and frac<1, frac)
+
+ return c_idx + frac
end
local PATH_CLEAR_KEEP = 4
diff --git a/advtrains/textures/advtrains_copytool.png b/advtrains/textures/advtrains_copytool.png
new file mode 100644
index 0000000..a8ea557
--- /dev/null
+++ b/advtrains/textures/advtrains_copytool.png
Binary files differ
diff --git a/advtrains/textures/advtrains_platform_diag.png b/advtrains/textures/advtrains_platform_diag.png
new file mode 100644
index 0000000..6e262e2
--- /dev/null
+++ b/advtrains/textures/advtrains_platform_diag.png
Binary files differ
diff --git a/advtrains/trainlogic.lua b/advtrains/trainlogic.lua
index 886abe8..1b90464 100644
--- a/advtrains/trainlogic.lua
+++ b/advtrains/trainlogic.lua
@@ -118,8 +118,10 @@ function advtrains.tp_player_to_train(player)
player:setpos(train.last_pos)
end
end
-minetest.register_on_joinplayer(function()
+minetest.register_on_joinplayer(function(player)
return advtrains.pcall(function()
+ advtrains.hud[player:get_player_name()] = nil
+ advtrains.hhud[player:get_player_name()] = nil
--independent of this, cause all wagons of the train which are loaded to reattach their players
--needed because already loaded wagons won't call reattach_all()
for _,wagon in pairs(minetest.luaentities) do
diff --git a/advtrains/wagons.lua b/advtrains/wagons.lua
index 040c1e4..b13b8d8 100644
--- a/advtrains/wagons.lua
+++ b/advtrains/wagons.lua
@@ -804,10 +804,10 @@ function wagon:show_bordcom(pname)
local data = advtrains.wagons[self.id]
local form = "size[11,9]label[0.5,0;AdvTrains Boardcom v0.1]"
- form=form.."textarea[0.5,1.5;7,1;text_outside;"..attrans("Text displayed outside on train")..";"..(train.text_outside or "").."]"
- form=form.."textarea[0.5,3;7,1;text_inside;"..attrans("Text displayed inside train")..";"..(train.text_inside or "").."]"
- form=form.."field[7.5,1.75;3,1;line;"..attrans("Line")..";"..(train.line or "").."]"
- form=form.."field[7.5,3.25;3,1;routingcode;"..attrans("Routingcode")..";"..(train.routingcode or "").."]"
+ form=form.."textarea[0.5,1.5;7,1;text_outside;"..attrans("Text displayed outside on train")..";"..(minetest.formspec_escape(train.text_outside or "")).."]"
+ form=form.."textarea[0.5,3;7,1;text_inside;"..attrans("Text displayed inside train")..";"..(minetest.formspec_escape(train.text_inside or "")).."]"
+ form=form.."field[7.5,1.75;3,1;line;"..attrans("Line")..";"..(minetest.formspec_escape(train.line or "")).."]"
+ form=form.."field[7.5,3.25;3,1;routingcode;"..attrans("Routingcode")..";"..(minetest.formspec_escape(train.routingcode or "")).."]"
--row 5 : train overview and autocoupling
if train.velocity==0 then
form=form.."label[0.5,4.5;Train overview /coupling control:]"