diff options
Diffstat (limited to 'advtrains')
-rw-r--r-- | advtrains/copytool.lua | 185 | ||||
-rw-r--r-- | advtrains/crafting.lua | 2 | ||||
-rw-r--r-- | advtrains/init.lua | 34 | ||||
-rw-r--r-- | advtrains/log.lua | 8 | ||||
-rw-r--r-- | advtrains/misc_nodes.lua | 33 | ||||
-rw-r--r-- | advtrains/models/advtrains_platform_diag.b3d | bin | 0 -> 1970 bytes | |||
-rw-r--r-- | advtrains/path.lua | 91 | ||||
-rw-r--r-- | advtrains/textures/advtrains_copytool.png | bin | 0 -> 779 bytes | |||
-rw-r--r-- | advtrains/textures/advtrains_platform_diag.png | bin | 0 -> 93 bytes | |||
-rw-r--r-- | advtrains/trainlogic.lua | 4 | ||||
-rw-r--r-- | advtrains/wagons.lua | 8 |
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 Binary files differnew file mode 100644 index 0000000..46c8bd9 --- /dev/null +++ b/advtrains/models/advtrains_platform_diag.b3d 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 Binary files differnew file mode 100644 index 0000000..a8ea557 --- /dev/null +++ b/advtrains/textures/advtrains_copytool.png diff --git a/advtrains/textures/advtrains_platform_diag.png b/advtrains/textures/advtrains_platform_diag.png Binary files differnew file mode 100644 index 0000000..6e262e2 --- /dev/null +++ b/advtrains/textures/advtrains_platform_diag.png 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:]"
|