develop #32
|
@ -436,80 +436,68 @@ end, true)
|
|||
|
||||
minetest.register_globalstep(function(dtime)
|
||||
timer = timer + dtime
|
||||
if timer > armor.config.init_delay then
|
||||
for player, count in pairs(pending_players) do
|
||||
local remove = init_player_armor(player) == true
|
||||
pending_players[player] = count + 1
|
||||
if remove == false and count > armor.config.init_times then
|
||||
minetest.log("warning", S("3d_armor: Failed to initialize player"))
|
||||
remove = true
|
||||
end
|
||||
if remove == true then
|
||||
pending_players[player] = nil
|
||||
if timer <= armor.config.init_delay then
|
||||
return
|
||||
end
|
||||
timer = 0
|
||||
|
||||
for player, count in pairs(pending_players) do
|
||||
local remove = init_player_armor(player) == true
|
||||
pending_players[player] = count + 1
|
||||
if remove == false and count > armor.config.init_times then
|
||||
minetest.log("warning", S("3d_armor: Failed to initialize player"))
|
||||
remove = true
|
||||
end
|
||||
if remove == true then
|
||||
pending_players[player] = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- water breathing protection, added by TenPlus1
|
||||
if armor.config.water_protect == true then
|
||||
for _,player in pairs(minetest.get_connected_players()) do
|
||||
local name = player:get_player_name()
|
||||
if armor.def[name].water > 0 and
|
||||
player:get_breath() < 10 then
|
||||
player:set_breath(10)
|
||||
end
|
||||
end
|
||||
timer = 0
|
||||
end
|
||||
end)
|
||||
|
||||
-- Fire Protection and water breathing, added by TenPlus1.
|
||||
|
||||
-- Fire Protection, added by TenPlus1.
|
||||
if armor.config.fire_protect == true then
|
||||
-- override hot nodes so they do not hurt player anywhere but mod
|
||||
-- override any hot nodes that do not already deal damage
|
||||
for _, row in pairs(armor.fire_nodes) do
|
||||
if minetest.registered_nodes[row[1]] then
|
||||
minetest.override_item(row[1], {damage_per_second = 0})
|
||||
local damage = minetest.registered_nodes[row[1]].damage_per_second
|
||||
if not damage or damage == 0 then
|
||||
minetest.override_item(row[1], {damage_per_second = row[3]})
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
print (S("[3d_armor] Fire Nodes disabled"))
|
||||
print ("[3d_armor] Fire Nodes disabled")
|
||||
end
|
||||
|
||||
if armor.config.water_protect == true or armor.config.fire_protect == true then
|
||||
minetest.register_globalstep(function(dtime)
|
||||
armor.timer = armor.timer + dtime
|
||||
if armor.timer < armor.config.update_time then
|
||||
return
|
||||
end
|
||||
for _,player in pairs(minetest.get_connected_players()) do
|
||||
local name = player:get_player_name()
|
||||
local pos = player:get_pos()
|
||||
local hp = player:get_hp()
|
||||
if not name or not pos or not hp then
|
||||
return
|
||||
end
|
||||
-- water breathing
|
||||
if armor.config.water_protect == true then
|
||||
if armor.def[name].water > 0 and
|
||||
player:get_breath() < 10 then
|
||||
player:set_breath(10)
|
||||
end
|
||||
end
|
||||
if armor.config.fire_protect == true then
|
||||
minetest.register_on_player_hpchange(function(player, hp_change, reason)
|
||||
|
||||
if reason.type == "node_damage" and reason.node then
|
||||
-- fire protection
|
||||
if armor.config.fire_protect == true then
|
||||
local fire_damage = true
|
||||
pos.y = pos.y + 1.4 -- head level
|
||||
local node_head = minetest.get_node(pos).name
|
||||
pos.y = pos.y - 1.2 -- feet level
|
||||
local node_feet = minetest.get_node(pos).name
|
||||
-- is player inside a hot node?
|
||||
for _, row in pairs(armor.fire_nodes) do
|
||||
-- check fire protection, if not enough then get hurt
|
||||
if row[1] == node_head or row[1] == node_feet then
|
||||
if fire_damage == true then
|
||||
if armor.config.fire_protect == true and hp_change < 0 then
|
||||
local name = player:get_player_name()
|
||||
for _,igniter in pairs(armor.fire_nodes) do
|
||||
if reason.node == igniter[1] then
|
||||
if armor.def[name].fire < igniter[2] then
|
||||
armor:punch(player, "fire")
|
||||
last_punch_time[name] = minetest.get_gametime()
|
||||
fire_damage = false
|
||||
end
|
||||
if hp > 0 and armor.def[name].fire < row[2] then
|
||||
hp = hp - row[3] * armor.config.update_time
|
||||
player:set_hp(hp)
|
||||
break
|
||||
else
|
||||
hp_change = 0
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
armor.timer = 0
|
||||
end)
|
||||
return hp_change
|
||||
end, true)
|
||||
end
|
||||
|
|
|
@ -182,9 +182,11 @@ minetest.register_node(nodename, {
|
|||
^- called when a train leaves the rail
|
||||
|
||||
-- The following function is only in effect when interlocking is enabled:
|
||||
on_train_approach = function(pos, train_id, train, index, lzbdata)
|
||||
on_train_approach = function(pos, train_id, train, index, has_entered, lzbdata)
|
||||
^- called when a train is approaching this position, called exactly once for every path recalculation (which can happen at any time)
|
||||
^- This is called so that if the train would start braking now, it would come to halt about(wide approx) 5 nodes before the rail.
|
||||
^- has_entered: when true, the train is already standing on this node with its front tip, and the enter callback has already been called.
|
||||
Possibly, some actions need not to be taken in this case. Only set if it's the very first node the train is standing on.
|
||||
^- lzbdata should be ignored and nothing should be assigned to it
|
||||
}
|
||||
})
|
||||
|
|
|
@ -72,6 +72,12 @@ function atc.send_command(pos, par_tid)
|
|||
end
|
||||
else
|
||||
atwarn("ATC rail at", pos, ": Sending command failed: There's no train at this position. This seems to be a bug.")
|
||||
-- huch
|
||||
--local train = advtrains.trains[train_id_temp_debug]
|
||||
--atlog("Train speed is",train.velocity,", have moved",train.dist_moved_this_step,", lever",train.lever)
|
||||
--advtrains.path_print(train, atlog)
|
||||
-- TODO track again when ATC bugs occur...
|
||||
|
||||
end
|
||||
else
|
||||
atwarn("ATC rail at", pos, ": Sending command failed: Entry for controller not found.")
|
||||
|
@ -108,57 +114,53 @@ advtrains.atc_function = function(def, preset, suffix, rotation)
|
|||
return {
|
||||
after_place_node=apn_func,
|
||||
after_dig_node=function(pos)
|
||||
return advtrains.pcall(function()
|
||||
advtrains.invalidate_all_paths(pos)
|
||||
advtrains.ndb.clear(pos)
|
||||
local pts=minetest.pos_to_string(pos)
|
||||
atc.controllers[pts]=nil
|
||||
end)
|
||||
advtrains.invalidate_all_paths(pos)
|
||||
advtrains.ndb.clear(pos)
|
||||
local pts=minetest.pos_to_string(pos)
|
||||
atc.controllers[pts]=nil
|
||||
end,
|
||||
on_receive_fields = function(pos, formname, fields, player)
|
||||
return advtrains.pcall(function()
|
||||
if advtrains.is_protected(pos, player:get_player_name()) then
|
||||
minetest.record_protection_violation(pos, player:get_player_name())
|
||||
if advtrains.is_protected(pos, player:get_player_name()) then
|
||||
minetest.record_protection_violation(pos, player:get_player_name())
|
||||
return
|
||||
end
|
||||
local meta=minetest.get_meta(pos)
|
||||
if meta then
|
||||
if not fields.save then
|
||||
--[[--maybe only the dropdown changed
|
||||
if fields.mode then
|
||||
meta:set_string("mode", idxtrans[fields.mode])
|
||||
if fields.mode=="digiline" then
|
||||
meta:set_string("infotext", attrans("ATC controller, mode @1\nChannel: @2", fields.mode, meta:get_string("command")) )
|
||||
else
|
||||
meta:set_string("infotext", attrans("ATC controller, mode @1\nCommand: @2", fields.mode, meta:get_string("command")) )
|
||||
end
|
||||
meta:set_string("formspec", atc.get_atc_controller_formspec(pos, meta))
|
||||
end]]--
|
||||
return
|
||||
end
|
||||
local meta=minetest.get_meta(pos)
|
||||
if meta then
|
||||
if not fields.save then
|
||||
--maybe only the dropdown changed
|
||||
if fields.mode then
|
||||
meta:set_string("mode", idxtrans[fields.mode])
|
||||
if fields.mode=="digiline" then
|
||||
meta:set_string("infotext", attrans("ATC controller, mode @1\nChannel: @2", (fields.mode or "?"), meta:get_string("command")) )
|
||||
else
|
||||
meta:set_string("infotext", attrans("ATC controller, mode @1\nCommand: @2", (fields.mode or "?"), meta:get_string("command")) )
|
||||
end
|
||||
meta:set_string("formspec", atc.get_atc_controller_formspec(pos, meta))
|
||||
end
|
||||
return
|
||||
end
|
||||
meta:set_string("mode", idxtrans[fields.mode])
|
||||
meta:set_string("command", fields.command)
|
||||
meta:set_string("command_on", fields.command_on)
|
||||
meta:set_string("channel", fields.channel)
|
||||
if fields.mode=="digiline" then
|
||||
meta:set_string("infotext", attrans("ATC controller, mode @1\nChannel: @2", (fields.mode or "?"), meta:get_string("command")) )
|
||||
else
|
||||
meta:set_string("infotext", attrans("ATC controller, mode @1\nCommand: @2", (fields.mode or "?"), meta:get_string("command")) )
|
||||
end
|
||||
meta:set_string("formspec", atc.get_atc_controller_formspec(pos, meta))
|
||||
--meta:set_string("mode", idxtrans[fields.mode])
|
||||
meta:set_string("command", fields.command)
|
||||
--meta:set_string("command_on", fields.command_on)
|
||||
meta:set_string("channel", fields.channel)
|
||||
--if fields.mode=="digiline" then
|
||||
-- meta:set_string("infotext", attrans("ATC controller, mode @1\nChannel: @2", fields.mode, meta:get_string("command")) )
|
||||
--else
|
||||
meta:set_string("infotext", attrans("ATC controller, mode @1\nCommand: @2", "-", meta:get_string("command")) )
|
||||
--end
|
||||
meta:set_string("formspec", atc.get_atc_controller_formspec(pos, meta))
|
||||
|
||||
local pts=minetest.pos_to_string(pos)
|
||||
local _, conns=advtrains.get_rail_info_at(pos, advtrains.all_tracktypes)
|
||||
atc.controllers[pts]={command=fields.command}
|
||||
if #advtrains.occ.get_trains_at(pos) > 0 then
|
||||
atc.send_command(pos)
|
||||
end
|
||||
local pts=minetest.pos_to_string(pos)
|
||||
local _, conns=advtrains.get_rail_info_at(pos, advtrains.all_tracktypes)
|
||||
atc.controllers[pts]={command=fields.command}
|
||||
if #advtrains.occ.get_trains_at(pos) > 0 then
|
||||
atc.send_command(pos)
|
||||
end
|
||||
end)
|
||||
end
|
||||
end,
|
||||
advtrains = {
|
||||
on_train_enter = function(pos, train_id)
|
||||
atc.send_command(pos)
|
||||
atc.send_command(pos, train_id)
|
||||
end,
|
||||
},
|
||||
}
|
||||
|
@ -256,6 +258,11 @@ local matchptn={
|
|||
end
|
||||
return 1
|
||||
end,
|
||||
["A([01])"]=function(id, train, match)
|
||||
if not advtrains.interlocking then return 2 end
|
||||
advtrains.interlocking.ars_set_disable(train, match=="0")
|
||||
return 2
|
||||
end,
|
||||
}
|
||||
|
||||
eval_conditional = function(command, arrow, speed)
|
||||
|
|
|
@ -13,76 +13,74 @@ minetest.register_tool("advtrains:copytool", {
|
|||
-- 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."))
|
||||
if ((not pointed_thing.type == "node") or (not placer.get_player_name)) then
|
||||
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
|
||||
end
|
||||
local pname = placer:get_player_name()
|
||||
|
||||
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
|
||||
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)
|
||||
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,
|
||||
-- Copy: Take the pointed-at train and put it on the clipboard
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
|
|
|
@ -32,7 +32,6 @@ minetest.register_entity("advtrains:discouple", {
|
|||
end,
|
||||
get_staticdata=function() return "DISCOUPLE" end,
|
||||
on_punch=function(self, player)
|
||||
return advtrains.pcall(function()
|
||||
local pname = player:get_player_name()
|
||||
if pname and pname~="" and self.wagon then
|
||||
if advtrains.safe_decouple_wagon(self.wagon.id, pname) then
|
||||
|
@ -41,16 +40,14 @@ minetest.register_entity("advtrains:discouple", {
|
|||
minetest.add_entity(self.object:getpos(), "advtrains:lockmarker")
|
||||
end
|
||||
end
|
||||
end)
|
||||
end,
|
||||
on_step=function(self, dtime)
|
||||
return advtrains.pcall(function()
|
||||
if not self.wagon then
|
||||
self.object:remove()
|
||||
return
|
||||
end
|
||||
--getyaw seems to be a reliable method to check if an object is loaded...if it returns nil, it is not.
|
||||
if not self.wagon.object:getyaw() then
|
||||
if not self.wagon.object:get_yaw() then
|
||||
self.object:remove()
|
||||
return
|
||||
end
|
||||
|
@ -58,7 +55,6 @@ minetest.register_entity("advtrains:discouple", {
|
|||
self.object:remove()
|
||||
return
|
||||
end
|
||||
end)
|
||||
end,
|
||||
})
|
||||
|
||||
|
@ -79,7 +75,6 @@ minetest.register_entity("advtrains:couple", {
|
|||
is_couple=true,
|
||||
static_save = false,
|
||||
on_activate=function(self, staticdata)
|
||||
return advtrains.pcall(function()
|
||||
if staticdata=="COUPLE" then
|
||||
--couple entities have no right to exist further...
|
||||
atprint("Couple loaded from staticdata, destroying")
|
||||
|
@ -87,11 +82,9 @@ minetest.register_entity("advtrains:couple", {
|
|||
return
|
||||
end
|
||||
self.object:set_armor_groups({immmortal=1})
|
||||
end)
|
||||
end,
|
||||
get_staticdata=function(self) return "COUPLE" end,
|
||||
on_rightclick=function(self, clicker)
|
||||
return advtrains.pcall(function()
|
||||
if not self.train_id_1 or not self.train_id_2 then return end
|
||||
|
||||
local pname=clicker
|
||||
|
@ -102,15 +95,12 @@ minetest.register_entity("advtrains:couple", {
|
|||
else
|
||||
lockmarker(self.object)
|
||||
end
|
||||
end)
|
||||
end,
|
||||
on_step=function(self, dtime)
|
||||
return advtrains.pcall(function()
|
||||
if advtrains.outside_range(self.object:getpos()) then
|
||||
if advtrains.wagon_outside_range(self.object:getpos()) then
|
||||
self.object:remove()
|
||||
return
|
||||
end
|
||||
advtrains.atprint_context_tid=self.train_id_1
|
||||
|
||||
if not self.train_id_1 or not self.train_id_2 then atprint("Couple: train ids not set!") self.object:remove() return end
|
||||
local train1=advtrains.trains[self.train_id_1]
|
||||
|
@ -155,14 +145,12 @@ minetest.register_entity("advtrains:couple", {
|
|||
end
|
||||
local pos_median=advtrains.pos_median(tp1, tp2)
|
||||
if not vector.equals(pos_median, self.object:getpos()) then
|
||||
self.object:setpos(pos_median)
|
||||
self.object:set_pos(pos_median)
|
||||
end
|
||||
self.position_set=true
|
||||
end
|
||||
atprintbm("couple step", t)
|
||||
advtrains.atprint_context_tid=nil
|
||||
|
||||
end)
|
||||
end,
|
||||
})
|
||||
minetest.register_entity("advtrains:lockmarker", {
|
||||
|
@ -175,7 +163,6 @@ minetest.register_entity("advtrains:lockmarker", {
|
|||
is_lockmarker=true,
|
||||
static_save = false,
|
||||
on_activate=function(self, staticdata)
|
||||
return advtrains.pcall(function()
|
||||
if staticdata=="COUPLE" then
|
||||
--couple entities have no right to exist further...
|
||||
atprint("Couple loaded from staticdata, destroying")
|
||||
|
@ -184,7 +171,6 @@ minetest.register_entity("advtrains:lockmarker", {
|
|||
end
|
||||
self.object:set_armor_groups({immmortal=1})
|
||||
self.life=5
|
||||
end)
|
||||
end,
|
||||
get_staticdata=function(self) return "COUPLE" end,
|
||||
on_step=function(self, dtime)
|
||||
|
|
|
@ -20,6 +20,32 @@ minetest.register_craft({
|
|||
{'', '', 'default:steel_ingot'},
|
||||
},
|
||||
})
|
||||
--Wallmounted Signal
|
||||
minetest.register_craft({
|
||||
output = 'advtrains:signal_wall_r_off 2',
|
||||
recipe = {
|
||||
{'dye:red', 'default:steel_ingot', 'default:steel_ingot'},
|
||||
{'', 'default:steel_ingot', ''},
|
||||
{'dye:dark_green', 'default:steel_ingot', 'default:steel_ingot'},
|
||||
},
|
||||
})
|
||||
|
||||
--Wallmounted Signals can be converted into every orientation by shapeless crafting
|
||||
minetest.register_craft({
|
||||
output = 'advtrains:signal_wall_l_off',
|
||||
type = "shapeless",
|
||||
recipe = {'advtrains:signal_wall_r_off'},
|
||||
})
|
||||
minetest.register_craft({
|
||||
output = 'advtrains:signal_wall_t_off',
|
||||
type = "shapeless",
|
||||
recipe = {'advtrains:signal_wall_l_off'},
|
||||
})
|
||||
minetest.register_craft({
|
||||
output = 'advtrains:signal_wall_r_off',
|
||||
type = "shapeless",
|
||||
recipe = {'advtrains:signal_wall_t_off'},
|
||||
})
|
||||
|
||||
--trackworker
|
||||
minetest.register_craft({
|
||||
|
|
|
@ -24,7 +24,10 @@ function advtrains.drb_dump(tid)
|
|||
return
|
||||
end
|
||||
repeat
|
||||
atdebug(ringbufs[tid][ringbufcnt[tid]])
|
||||
local t = ringbufs[tid][ringbufcnt[tid]]
|
||||
if t then
|
||||
atdebug(t)
|
||||
end
|
||||
ringbufcnt[tid]=ringbufcnt[tid]+1
|
||||
if ringbufcnt[tid] > ringbuflen then
|
||||
ringbufcnt[tid]=0
|
||||
|
|
|
@ -445,3 +445,28 @@ atdebug("pts",os.clock()-t1,"s")
|
|||
]]
|
||||
|
||||
|
||||
-- Function to check whether a position is near (within range of) any player
|
||||
function advtrains.position_in_range(pos, range)
|
||||
if not pos then
|
||||
return true
|
||||
end
|
||||
for _,p in pairs(minetest.get_connected_players()) do
|
||||
if vector.distance(p:get_pos(),pos)<=range then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local active_node_range = tonumber(minetest.settings:get("active_block_range"))*16 + 16
|
||||
-- Function to check whether node at position(pos) is "loaded"/"active"
|
||||
-- That is, whether it is within the active_block_range to a player
|
||||
if minetest.is_block_active then -- define function differently whether minetest.is_block_active is available or not
|
||||
advtrains.is_node_loaded = minetest.is_block_active
|
||||
else
|
||||
function advtrains.is_node_loaded(pos)
|
||||
if advtrains.position_in_range(pos, active_node_range) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -26,15 +26,37 @@ minetest.log("action", "[advtrains] Loading...")
|
|||
attrans = minetest.get_translator ("advtrains")
|
||||
|
||||
--advtrains
|
||||
advtrains = {trains={}, player_to_train_mapping={}}
|
||||
|
||||
DUMP_DEBUG_SAVE = false
|
||||
GENERATE_ATRICIFIAL_LAG = false
|
||||
-- =======================Development/debugging settings=====================
|
||||
-- DO NOT USE FOR NORMAL OPERATION
|
||||
local DUMP_DEBUG_SAVE = false
|
||||
-- dump the save files in human-readable format into advtrains_DUMP
|
||||
|
||||
local GENERATE_ATRICIFIAL_LAG = false
|
||||
local HOW_MANY_LAG = 1.0
|
||||
-- Simulate a higher server step interval, as it occurs when the server is on high load
|
||||
|
||||
advtrains.IGNORE_WORLD = false
|
||||
-- Run advtrains without respecting the world map
|
||||
-- - No world collision checks occur
|
||||
-- - The NDB forcibly places all nodes stored in it into the world regardless of the world's content.
|
||||
-- - Rails do not set the 'attached_node' group
|
||||
-- This mode can be useful for debugging/testing a world without the map data available
|
||||
-- In this case, choose 'singlenode' as mapgen
|
||||
|
||||
local NO_SAVE = false
|
||||
-- Do not save any data to advtrains save files
|
||||
|
||||
-- ==========================================================================
|
||||
|
||||
-- Use a global slowdown factor to slow down train movements. Now a setting
|
||||
advtrains.DTIME_LIMIT = tonumber(minetest.settings:get("advtrains_dtime_limit")) or 0.2
|
||||
advtrains.SAVE_INTERVAL = tonumber(minetest.settings:get("advtrains_save_interval")) or 60
|
||||
|
||||
--Constant for maximum connection value/division of the circle
|
||||
AT_CMAX = 16
|
||||
|
||||
advtrains = {trains={}, player_to_train_mapping={}}
|
||||
|
||||
-- get wagon loading range
|
||||
advtrains.wagon_load_range = tonumber(minetest.settings:get("advtrains_wagon_load_range"))
|
||||
if not advtrains.wagon_load_range then
|
||||
|
@ -61,25 +83,6 @@ local function reload_saves()
|
|||
end)
|
||||
end
|
||||
|
||||
function advtrains.pcall(fun)
|
||||
if no_action then return end
|
||||
|
||||
local succ, return1, return2, return3, return4=xpcall(fun, function(err)
|
||||
atwarn("Lua Error occured: ", err)
|
||||
atwarn(debug.traceback())
|
||||
if advtrains.atprint_context_tid then
|
||||
advtrains.path_print(advtrains.trains[advtrains.atprint_context_tid], atdebug)
|
||||
atwarn(advtrains.trains[advtrains.atprint_context_tid].debug)
|
||||
end
|
||||
end)
|
||||
if not succ then
|
||||
reload_saves()
|
||||
else
|
||||
return return1, return2, return3, return4
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
advtrains.modpath = minetest.get_modpath("advtrains")
|
||||
|
||||
--Advtrains dump (special treatment of pos and sigd)
|
||||
|
@ -464,7 +467,8 @@ advtrains.avt_save = function(remove_players_from_wagons)
|
|||
"trainparts", "recently_collided_with_env",
|
||||
"atc_brake_target", "atc_wait_finish", "atc_command", "atc_delay", "door_open",
|
||||
"text_outside", "text_inside", "line", "routingcode",
|
||||
"il_sections", "speed_restriction", "is_shunt", "points_split", "autocouple"
|
||||
"il_sections", "speed_restriction", "is_shunt",
|
||||
"points_split", "autocouple", "ars_disable",
|
||||
})
|
||||
--then save it
|
||||
tmp_trains[id]=v
|
||||
|
@ -560,14 +564,17 @@ end
|
|||
--## MAIN LOOP ##--
|
||||
--Calls all subsequent main tasks of both advtrains and atlatc
|
||||
local init_load=false
|
||||
local save_interval=60
|
||||
local save_timer=save_interval
|
||||
local save_timer = advtrains.SAVE_INTERVAL
|
||||
advtrains.mainloop_runcnt=0
|
||||
|
||||
advtrains.global_slowdown = 1
|
||||
|
||||
local t = 0
|
||||
minetest.register_globalstep(function(dtime_mt)
|
||||
return advtrains.pcall(function()
|
||||
if no_action then
|
||||
-- the advtrains globalstep is skipped by command. Return immediately
|
||||
return
|
||||
end
|
||||
|
||||
advtrains.mainloop_runcnt=advtrains.mainloop_runcnt+1
|
||||
--atprint("Running the main loop, runcnt",advtrains.mainloop_runcnt)
|
||||
--call load once. see advtrains.load() comment
|
||||
|
@ -575,24 +582,30 @@ minetest.register_globalstep(function(dtime_mt)
|
|||
advtrains.load()
|
||||
end
|
||||
|
||||
local dtime
|
||||
local dtime = dtime_mt * advtrains.global_slowdown
|
||||
if GENERATE_ATRICIFIAL_LAG then
|
||||
dtime = 0.2
|
||||
dtime = HOW_MANY_LAG
|
||||
if os.clock()<t then
|
||||
return
|
||||
end
|
||||
|
||||
t = os.clock()+0.2
|
||||
else
|
||||
--limit dtime: if trains move too far in one step, automation may cause stuck and wrongly braking trains
|
||||
dtime=dtime_mt
|
||||
if dtime>0.2 then
|
||||
atprint("Limiting dtime to 0.2!")
|
||||
dtime=0.2
|
||||
t = os.clock()+HOW_MANY_LAG
|
||||
end
|
||||
-- if dtime is too high, decrease global slowdown
|
||||
if advtrains.DTIME_LIMIT~=0 then
|
||||
if dtime > advtrains.DTIME_LIMIT then
|
||||
if advtrains.global_slowdown > 0.1 then
|
||||
advtrains.global_slowdown = advtrains.global_slowdown - 0.05
|
||||
else
|
||||
advtrains.global_slowdown = advtrains.global_slowdown / 2
|
||||
end
|
||||
dtime = advtrains.DTIME_LIMIT
|
||||
end
|
||||
-- recover global slowdown slowly over time
|
||||
advtrains.global_slowdown = math.min(advtrains.global_slowdown*1.02, 1)
|
||||
end
|
||||
|
||||
advtrains.mainloop_trainlogic(dtime)
|
||||
advtrains.mainloop_trainlogic(dtime,advtrains.mainloop_runcnt)
|
||||
if advtrains_itm_mainloop then
|
||||
advtrains_itm_mainloop(dtime)
|
||||
end
|
||||
|
@ -610,10 +623,9 @@ minetest.register_globalstep(function(dtime_mt)
|
|||
local t=os.clock()
|
||||
--save
|
||||
advtrains.save()
|
||||
save_timer=save_interval
|
||||
save_timer = advtrains.SAVE_INTERVAL
|
||||
atprintbm("saving", t)
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
--if something goes wrong in these functions, there is no help. no pcall here.
|
||||
|
@ -642,12 +654,27 @@ function advtrains.save(remove_players_from_wagons)
|
|||
atwarn("Instructed to save() but load() was never called!")
|
||||
return
|
||||
end
|
||||
|
||||
if advtrains.IGNORE_WORLD then
|
||||
advtrains.ndb.restore_all()
|
||||
end
|
||||
|
||||
if NO_SAVE then
|
||||
return
|
||||
end
|
||||
if no_action then
|
||||
atlog("[save] Saving requested externally, but Advtrains step is disabled. Not saving any data as state may be inconsistent.")
|
||||
return
|
||||
end
|
||||
|
||||
local t1 = os.clock()
|
||||
advtrains.avt_save(remove_players_from_wagons) --saving advtrains. includes ndb at advtrains.ndb.save_data()
|
||||
if atlatc then
|
||||
atlatc.save()
|
||||
end
|
||||
atprint("[save_all]Saved advtrains save files")
|
||||
atlog("Saved advtrains save files, took",math.floor((os.clock()-t1) * 1000),"ms")
|
||||
|
||||
-- Cleanup actions
|
||||
--TODO very simple yet hacky workaround for the "green signals" bug
|
||||
advtrains.invalidate_all_paths()
|
||||
end
|
||||
|
@ -662,11 +689,9 @@ minetest.register_chatcommand("at_empty_seats",
|
|||
description = "Detach all players, especially the offline ones, from all trains. Use only when no one serious is on a train.", -- Full description
|
||||
privs = {train_operator=true, server=true}, -- Require the "privs" privilege to run
|
||||
func = function(name, param)
|
||||
return advtrains.pcall(function()
|
||||
atwarn("Data is being saved. While saving, advtrains will remove the players from trains. Save files will be reloaded afterwards!")
|
||||
advtrains.save(true)
|
||||
reload_saves()
|
||||
end)
|
||||
end,
|
||||
})
|
||||
-- This chat command solves another problem: Trains getting randomly stuck.
|
||||
|
@ -676,13 +701,36 @@ minetest.register_chatcommand("at_reroute",
|
|||
description = "Delete all train routes, force them to recalculate",
|
||||
privs = {train_operator=true}, -- Only train operator is required, since this is relatively safe.
|
||||
func = function(name, param)
|
||||
return advtrains.pcall(function()
|
||||
advtrains.invalidate_all_paths()
|
||||
return true, "Successfully invalidated train routes"
|
||||
end)
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_chatcommand("at_disable_step",
|
||||
{
|
||||
params = "<yes/no>",
|
||||
description = "Disable the advtrains globalstep temporarily",
|
||||
privs = {server=true},
|
||||
func = function(name, param)
|
||||
if minetest.is_yes(param) then
|
||||
-- disable everything, and turn off saving
|
||||
no_action = true;
|
||||
atwarn("The advtrains globalstep has been disabled. Trains are not moving, and no data is saved! Run '/at_disable_step no' to enable again!")
|
||||
return true, "Disabled advtrains successfully"
|
||||
elseif no_action then
|
||||
atwarn("Re-enabling advtrains globalstep...")
|
||||
reload_saves()
|
||||
return true
|
||||
else
|
||||
return false, "Advtrains is already running normally!"
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
advtrains.is_no_action = function()
|
||||
return no_action
|
||||
end
|
||||
|
||||
|
||||
local tot=(os.clock()-lot)*1000
|
||||
minetest.log("action", "[advtrains] Loaded in "..tot.."ms")
|
||||
|
|
|
@ -4,20 +4,36 @@
|
|||
--[[
|
||||
Documentation of train.lzb table
|
||||
train.lzb = {
|
||||
trav = Current index that the traverser has advanced so far
|
||||
oncoming = table containing oncoming signals, in order of appearance on the path
|
||||
trav_index = Current index that the traverser has advanced so far
|
||||
checkpoints = table containing oncoming signals, in order of index
|
||||
{
|
||||
pos = position of the point
|
||||
idx = where this is on the path
|
||||
spd = speed allowed to pass
|
||||
fun = function(pos, id, train, index, speed, lzbdata)
|
||||
index = where this is on the path
|
||||
speed = speed allowed to pass. nil = no effect
|
||||
callback = function(pos, id, train, index, speed, lzbdata)
|
||||
-- Function that determines what to do on the train in the moment it drives over that point.
|
||||
-- When spd==0, called instead when train has stopped in front
|
||||
-- nil = no effect
|
||||
lzbdata = {}
|
||||
-- Table of custom data filled in by approach callbacks
|
||||
-- Whenever an approach callback inserts an LZB checkpoint with changed lzbdata,
|
||||
-- all consecutive approach callbacks will see these passed as lzbdata table.
|
||||
|
||||
udata = arbitrary user data, no official way to retrieve (do not use)
|
||||
}
|
||||
trav_lzbdata = currently active lzbdata table at traverser index
|
||||
}
|
||||
each step, for every item in "oncoming", we need to determine the location to start braking (+ some safety margin)
|
||||
and, if we passed this point for at least one of the items, initiate brake.
|
||||
When speed has dropped below, say 3, decrease the margin to zero, so that trains actually stop at the signal IP.
|
||||
The spd variable and travsht need to be updated on every aspect change. it's probably best to reset everything when any aspect changes
|
||||
The LZB subsystem keeps track of "checkpoints" the train will pass in the future, and has two main tasks:
|
||||
1. run approach callbacks, and run callbacks when passing LZB checkpoints
|
||||
2. keep track of the permitted speed at checkpoints, and make sure that the train brakes accordingly
|
||||
To perform 2, it populates the train.path_speed table which is handled along with the path subsystem.
|
||||
This table is used in trainlogic.lua/train_step_b() and applied to the velocity calculations.
|
||||
|
||||
Note: in contrast to node enter callbacks, which are called when the train passes the .5 index mark, LZB callbacks are executed on passing the .0 index mark!
|
||||
If an LZB checkpoint has speed 0, the train will still enter the node (the enter callback will be called), but will stop at the 0.9 index mark (for details, see SLOW_APPROACH in trainlogic.lua)
|
||||
|
||||
The start point for the LZB traverser (and thus the first node that will receive an approach callback) is floor(train.index) + 1. This means, once the LZB checkpoint callback has fired,
|
||||
this path node will not receive any further approach callbacks for the same approach situation
|
||||
]]
|
||||
|
||||
|
||||
|
@ -45,42 +61,130 @@ function advtrains.set_lzb_param(par, val)
|
|||
end
|
||||
end
|
||||
|
||||
local function resolve_latest_lzbdata(ckp, index)
|
||||
local i = #ckp
|
||||
local ckpi
|
||||
while i>0 do
|
||||
ckpi = ckp[i]
|
||||
if ckpi.index <= index and ckpi.lzbdata then
|
||||
return ckpi.lzbdata
|
||||
end
|
||||
i=i-1
|
||||
end
|
||||
return {}
|
||||
end
|
||||
|
||||
local function look_ahead(id, train)
|
||||
local lzb = train.lzb
|
||||
if lzb.zero_checkpoint then
|
||||
-- if the checkpoints list contains a zero checkpoint, don't look ahead
|
||||
-- in order to not trigger approach callbacks on the wrong path
|
||||
return
|
||||
end
|
||||
|
||||
local acc = advtrains.get_acceleration(train, 1)
|
||||
local vel = train.velocity
|
||||
-- worst-case: the starting point is maximum speed
|
||||
local vel = train.max_speed or train.velocity
|
||||
local brakedst = ( -(vel*vel) / (2*acc) ) * params.DST_FACTOR
|
||||
|
||||
local brake_i = advtrains.path_get_index_by_offset(train, train.index, brakedst + params.BRAKE_SPACE)
|
||||
--local brake_i = advtrains.path_get_index_by_offset(train, train.index, brakedst + params.BRAKE_SPACE)
|
||||
-- worst case (don't use index_by_offset)
|
||||
local brake_i = atfloor(train.index + brakedst + params.BRAKE_SPACE)
|
||||
atprint("LZB: looking ahead up to ", brake_i)
|
||||
|
||||
--local aware_i = advtrains.path_get_index_by_offset(train, brake_i, AWARE_ZONE)
|
||||
|
||||
local lzb = train.lzb
|
||||
local trav = lzb.trav
|
||||
local trav = lzb.trav_index
|
||||
-- retrieve latest lzbdata
|
||||
if not lzb.trav_lzbdata then
|
||||
lzb.trav_lzbdata = resolve_latest_lzbdata(lzb.checkpoints, trav)
|
||||
end
|
||||
|
||||
--train.debug = lspd
|
||||
if lzb.trav_lzbdata.off_track then
|
||||
--previous position was off track, do not scan any further
|
||||
end
|
||||
|
||||
while trav <= brake_i do
|
||||
trav = trav + 1
|
||||
while trav <= brake_i and not lzb.zero_checkpoint do
|
||||
local pos = advtrains.path_get(train, trav)
|
||||
-- check offtrack
|
||||
if trav > train.path_trk_f then
|
||||
table.insert(lzb.oncoming, {
|
||||
pos = pos,
|
||||
idx = trav-1,
|
||||
spd = 0,
|
||||
})
|
||||
if trav - 1 == train.path_trk_f then
|
||||
lzb.trav_lzbdata.off_track = true
|
||||
advtrains.lzb_add_checkpoint(train, trav - 1, 0, nil, lzb.trav_lzbdata)
|
||||
else
|
||||
-- run callbacks
|
||||
-- Note: those callbacks are defined in trainlogic.lua for consistency with the other node callbacks
|
||||
advtrains.tnc_call_approach_callback(pos, id, train, trav, lzb.data)
|
||||
advtrains.tnc_call_approach_callback(pos, id, train, trav, lzb.trav_lzbdata)
|
||||
|
||||
end
|
||||
trav = trav + 1
|
||||
|
||||
end
|
||||
|
||||
lzb.trav = trav
|
||||
lzb.trav_index = trav
|
||||
|
||||
end
|
||||
advtrains.lzb_look_ahead = look_ahead
|
||||
|
||||
|
||||
local function call_runover_callbacks(id, train)
|
||||
if not train.lzb then return end
|
||||
|
||||
local i = 1
|
||||
local idx = atfloor(train.index)
|
||||
local ckp = train.lzb.checkpoints
|
||||
while ckp[i] do
|
||||
if ckp[i].index <= idx then
|
||||
atprint("LZB: checkpoint run over: i=",ckp[i].index,"s=",ckp[i].speed)
|
||||
-- call callback
|
||||
local it = ckp[i]
|
||||
if it.callback then
|
||||
it.callback(it.pos, id, train, it.index, it.speed, train.lzb.lzbdata)
|
||||
end
|
||||
-- note: lzbdata is always defined as look_ahead was called before
|
||||
table.remove(ckp, i)
|
||||
else
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Flood-fills train.path_speed, based on this checkpoint
|
||||
local function apply_checkpoint_to_path(train, checkpoint)
|
||||
if not checkpoint.speed then
|
||||
return
|
||||
end
|
||||
atprint("LZB: applying checkpoint: i=",checkpoint.index,"s=",checkpoint.speed)
|
||||
|
||||
if checkpoint.speed == 0 then
|
||||
train.lzb.zero_checkpoint = true
|
||||
end
|
||||
|
||||
-- make sure path exists until checkpoint
|
||||
local pos = advtrains.path_get(train, checkpoint.index)
|
||||
|
||||
local brake_accel = advtrains.get_acceleration(train, 11)
|
||||
|
||||
-- start with the checkpoint index at specified speed
|
||||
local index = checkpoint.index
|
||||
local p_speed -- speed in path_speed
|
||||
local c_speed = checkpoint.speed -- calculated speed at current index
|
||||
while true do
|
||||
p_speed = train.path_speed[index]
|
||||
if (p_speed and p_speed <= c_speed) or index < train.index then
|
||||
--we're done. train already slower than wanted at this position
|
||||
return
|
||||
end
|
||||
-- insert calculated target speed
|
||||
train.path_speed[index] = c_speed
|
||||
-- calculate c_speed at previous index
|
||||
advtrains.path_get(train, index-1)
|
||||
local eldist = train.path_dist[index] - train.path_dist[index-1]
|
||||
-- Calculate the start velocity the train would have if it had a end velocity of c_speed and accelerating with brake_accel, after a distance of eldist:
|
||||
-- v0² = v1² - 2*a*s
|
||||
c_speed = math.sqrt( (c_speed * c_speed) - (2 * brake_accel * eldist) )
|
||||
index = index - 1
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
Distance needed to accelerate from v0 to v1 with constant acceleration a:
|
||||
|
@ -90,95 +194,74 @@ s = v0 * ------- + - * | ------- | = -----------
|
|||
a 2 \ a / 2*a
|
||||
]]
|
||||
|
||||
local function apply_control(id, train)
|
||||
local lzb = train.lzb
|
||||
|
||||
local i = 1
|
||||
while i<=#lzb.oncoming do
|
||||
if lzb.oncoming[i].idx < train.index then
|
||||
local ent = lzb.oncoming[i]
|
||||
if ent.fun then
|
||||
ent.fun(ent.pos, id, train, ent.idx, ent.spd, lzb.data)
|
||||
end
|
||||
|
||||
table.remove(lzb.oncoming, i)
|
||||
else
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
|
||||
for i, it in ipairs(lzb.oncoming) do
|
||||
local a = advtrains.get_acceleration(train, 1) --should be negative
|
||||
local v0 = train.velocity
|
||||
local v1 = it.spd
|
||||
if v1 and v1 <= v0 then
|
||||
local s = (v1*v1 - v0*v0) / (2*a)
|
||||
|
||||
local st = s + params.ADD_SLOW
|
||||
if v0 > 3 then
|
||||
st = s + params.ADD_FAST
|
||||
end
|
||||
if v0<=0 then
|
||||
st = s + params.ADD_STAND
|
||||
end
|
||||
|
||||
local i = advtrains.path_get_index_by_offset(train, it.idx, -st)
|
||||
|
||||
--train.debug = dump({v0f=v0*f, aff=a*f*f,v0=v0, v1=v1, f=f, a=a, s=s, st=st, i=i, idx=train.index})
|
||||
if i <= train.index then
|
||||
-- Gotcha! Braking...
|
||||
train.ctrl.lzb = 1
|
||||
--train.debug = train.debug .. "BRAKE!!!"
|
||||
return
|
||||
end
|
||||
|
||||
i = advtrains.path_get_index_by_offset(train, i, -params.ZONE_ROLL)
|
||||
if i <= train.index and v0>1 then
|
||||
-- roll control
|
||||
train.ctrl.lzb = 2
|
||||
return
|
||||
end
|
||||
i = advtrains.path_get_index_by_offset(train, i, -params.ZONE_HOLD)
|
||||
if i <= train.index and v0>1 then
|
||||
-- hold speed
|
||||
train.ctrl.lzb = 3
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
train.ctrl.lzb = nil
|
||||
end
|
||||
|
||||
local function invalidate(train)
|
||||
-- Removes all LZB checkpoints and restarts the traverser at the current train index
|
||||
function advtrains.lzb_invalidate(train)
|
||||
train.lzb = {
|
||||
trav = atfloor(train.index),
|
||||
data = {},
|
||||
oncoming = {},
|
||||
trav_index = atfloor(train.index) + 1,
|
||||
checkpoints = {},
|
||||
}
|
||||
end
|
||||
|
||||
function advtrains.lzb_invalidate(train)
|
||||
invalidate(train)
|
||||
-- LZB part of path_invalidate_ahead. Clears all checkpoints that are ahead of start_idx
|
||||
-- in contrast to path_inv_ahead, doesn't complain if start_idx is behind train.index, clears everything then
|
||||
function advtrains.lzb_invalidate_ahead(train, start_idx)
|
||||
if train.lzb then
|
||||
local idx = atfloor(start_idx)
|
||||
local i = 1
|
||||
while train.lzb.checkpoints[i] do
|
||||
if train.lzb.checkpoints[i].index >= idx then
|
||||
table.remove(train.lzb.checkpoints, i)
|
||||
else
|
||||
i=i+1
|
||||
end
|
||||
end
|
||||
train.lzb.trav_index = idx
|
||||
-- FIX reset trav_lzbdata (look_ahead fetches these when required)
|
||||
train.lzb.trav_lzbdata = nil
|
||||
-- re-apply all checkpoints to path_speed
|
||||
train.path_speed = {}
|
||||
train.lzb.zero_checkpoint = false
|
||||
for _,ckp in ipairs(train.lzb.checkpoints) do
|
||||
apply_checkpoint_to_path(train, ckp)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Add LZB control point
|
||||
-- udata: User-defined additional data
|
||||
function advtrains.lzb_add_checkpoint(train, index, speed, callback, udata)
|
||||
-- lzbdata: If you modify lzbdata in an approach callback, you MUST add a checkpoint AND pass the (modified) lzbdata into it.
|
||||
-- If you DON'T modify lzbdata, you MUST pass nil as lzbdata. Always modify the lzbdata table in place, never overwrite it!
|
||||
-- udata: user-defined data, do not use externally
|
||||
function advtrains.lzb_add_checkpoint(train, index, speed, callback, lzbdata, udata)
|
||||
local lzb = train.lzb
|
||||
local pos = advtrains.path_get(train, index)
|
||||
table.insert(lzb.oncoming, {
|
||||
local lzbdata_c = nil
|
||||
if lzbdata then
|
||||
-- make a shallow copy of lzbdata
|
||||
lzbdata_c = {}
|
||||
for k,v in pairs(lzbdata) do lzbdata_c[k] = v end
|
||||
end
|
||||
local ckp = {
|
||||
pos = pos,
|
||||
idx = index,
|
||||
spd = speed,
|
||||
fun = callback,
|
||||
index = index,
|
||||
speed = speed,
|
||||
callback = callback,
|
||||
lzbdata = lzbdata_c,
|
||||
udata = udata,
|
||||
})
|
||||
}
|
||||
table.insert(lzb.checkpoints, ckp)
|
||||
|
||||
apply_checkpoint_to_path(train, ckp)
|
||||
end
|
||||
|
||||
|
||||
advtrains.te_register_on_new_path(function(id, train)
|
||||
invalidate(train)
|
||||
look_ahead(id, train)
|
||||
advtrains.lzb_invalidate(train)
|
||||
-- Taken care of in pre-move hook (see train_step_b)
|
||||
--look_ahead(id, train)
|
||||
end)
|
||||
|
||||
advtrains.te_register_on_invalidate_ahead(function(id, train, start_idx)
|
||||
advtrains.lzb_invalidate_ahead(train, start_idx)
|
||||
end)
|
||||
|
||||
advtrains.te_register_on_update(function(id, train)
|
||||
|
@ -186,6 +269,8 @@ advtrains.te_register_on_update(function(id, train)
|
|||
atprint("LZB run: no path on train, skip step")
|
||||
return
|
||||
end
|
||||
look_ahead(id, train)
|
||||
apply_control(id, train)
|
||||
-- Note: look_ahead called from train_step_b before applying movement
|
||||
-- TODO: if more pre-move hooks are added, make a separate callback hook
|
||||
--look_ahead(id, train)
|
||||
call_runover_callbacks(id, train)
|
||||
end, true)
|
||||
|
|
|
@ -223,7 +223,7 @@ end
|
|||
|
||||
|
||||
function ndb.swap_node(pos, node, no_inval)
|
||||
if minetest.get_node_or_nil(pos) then
|
||||
if advtrains.is_node_loaded(pos) then
|
||||
minetest.swap_node(pos, node)
|
||||
end
|
||||
ndb.update(pos, node)
|
||||
|
@ -246,7 +246,7 @@ function ndb.update(pos, pnode)
|
|||
local resid = (nid * 4) + (l2b(node.param2 or 0))
|
||||
ndbset(pos.x, pos.y, pos.z, resid )
|
||||
--atdebug("nodedb: updating node", pos, "stored nid",nid,"assigned",ndb_nodeids[nid],"resulting cid",resid)
|
||||
minetest.after(0, advtrains.invalidate_all_paths, pos)
|
||||
advtrains.invalidate_all_paths_ahead(pos)
|
||||
else
|
||||
--at this position there is no longer a node that needs to be tracked.
|
||||
--atdebug("nodedb: updating node", pos, "cleared")
|
||||
|
@ -281,8 +281,9 @@ function advtrains.get_rail_info_at(pos, drives_on)
|
|||
return true, conns, railheight
|
||||
end
|
||||
|
||||
local IGNORE_WORLD = advtrains.IGNORE_WORLD
|
||||
|
||||
ndb.run_lbm = function(pos, node)
|
||||
return advtrains.pcall(function()
|
||||
local cid=ndbget(pos.x, pos.y, pos.z)
|
||||
if cid then
|
||||
--if in database, detect changes and apply.
|
||||
|
@ -294,11 +295,12 @@ ndb.run_lbm = function(pos, node)
|
|||
ndb.update(pos, node)
|
||||
else
|
||||
if (nodeid~=node.name or param2~=node.param2) then
|
||||
atprint("nodedb: lbm replaced", pos, "with nodeid", nodeid, "param2", param2, "cid is", cid)
|
||||
minetest.swap_node(pos, {name=nodeid, param2 = param2})
|
||||
--atprint("nodedb: lbm replaced", pos, "with nodeid", nodeid, "param2", param2, "cid is", cid)
|
||||
local newnode = {name=nodeid, param2 = param2}
|
||||
minetest.swap_node(pos, newnode)
|
||||
local ndef=minetest.registered_nodes[nodeid]
|
||||
if ndef and ndef.on_updated_from_nodedb then
|
||||
ndef.on_updated_from_nodedb(pos, node)
|
||||
if ndef and ndef.advtrains and ndef.advtrains.on_updated_from_nodedb then
|
||||
ndef.advtrains.on_updated_from_nodedb(pos, newnode)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
@ -309,7 +311,6 @@ ndb.run_lbm = function(pos, node)
|
|||
ndb.update(pos, node)
|
||||
end
|
||||
return false
|
||||
end)
|
||||
end
|
||||
|
||||
|
||||
|
@ -326,6 +327,7 @@ minetest.register_lbm({
|
|||
--used when restoring stuff after a crash
|
||||
ndb.restore_all = function()
|
||||
--atlog("Updating the map from the nodedb, this may take a while")
|
||||
local t1 = os.clock()
|
||||
local cnt=0
|
||||
local dcnt=0
|
||||
for y, ny in pairs(ndb_nodes) do
|
||||
|
@ -336,7 +338,7 @@ ndb.restore_all = function()
|
|||
if node then
|
||||
local ori_ndef=minetest.registered_nodes[node.name]
|
||||
local ndbnode=ndb.get_node_raw(pos)
|
||||
if ori_ndef and ori_ndef.groups.save_in_at_nodedb then --check if this node has been worldedited, and don't replace then
|
||||
if (ori_ndef and ori_ndef.groups.save_in_at_nodedb) or IGNORE_WORLD then --check if this node has been worldedited, and don't replace then
|
||||
if (ndbnode.name~=node.name or ndbnode.param2~=node.param2) then
|
||||
minetest.swap_node(pos, ndbnode)
|
||||
--atlog("Replaced",node.name,"@",pos,"with",ndbnode.name)
|
||||
|
@ -351,15 +353,13 @@ ndb.restore_all = function()
|
|||
end
|
||||
end
|
||||
end
|
||||
local text="Restore node database: Replaced "..cnt.." nodes, removed "..dcnt.." ghost nodes."
|
||||
local text="Restore node database: Replaced "..cnt.." nodes, removed "..dcnt.." ghost nodes. (took "..math.floor((os.clock()-t1) * 1000).."ms)"
|
||||
atlog(text)
|
||||
return text
|
||||
end
|
||||
|
||||
minetest.register_on_dignode(function(pos, oldnode, digger)
|
||||
return advtrains.pcall(function()
|
||||
ndb.clear(pos)
|
||||
end)
|
||||
end)
|
||||
|
||||
function ndb.get_nodes()
|
||||
|
@ -380,14 +380,12 @@ minetest.register_chatcommand("at_sync_ndb",
|
|||
description = "Write node db back to map and find ghost nodes", -- Full description
|
||||
privs = {train_operator=true},
|
||||
func = function(name, param)
|
||||
return advtrains.pcall(function()
|
||||
if os.time() < ptime+30 then
|
||||
if os.time() < ptime+30 and not minetest.get_player_privs(name, "server") then
|
||||
return false, "Please wait at least 30s from the previous execution of /at_restore_ndb!"
|
||||
end
|
||||
local text = ndb.restore_all()
|
||||
ptime=os.time()
|
||||
return true, text
|
||||
end)
|
||||
end,
|
||||
})
|
||||
|
||||
|
|
|
@ -196,7 +196,6 @@ function o.get_trains_over(ppos)
|
|||
local r = {}
|
||||
local i = 1
|
||||
while t[i] do
|
||||
local train = advtrains.trains[t[i]]
|
||||
local idx = t[i+1]
|
||||
r[t[i]] = idx
|
||||
i = i + 2
|
||||
|
|
|
@ -13,17 +13,19 @@ minetest.override_item("mesecons_switch:mesecon_switch_off", {
|
|||
mesecon.receptor_on(pos)
|
||||
minetest.sound_play("mesecons_switch", {pos=pos})
|
||||
end,
|
||||
on_updated_from_nodedb = function(pos, node)
|
||||
mesecon.receptor_off(pos)
|
||||
end,
|
||||
advtrains = {
|
||||
getstate = "off",
|
||||
setstate = function(pos, node, newstate)
|
||||
if newstate=="on" then
|
||||
advtrains.ndb.swap_node(pos, {name="mesecons_switch:mesecon_switch_on", param2=node.param2})
|
||||
mesecon.receptor_on(pos)
|
||||
if advtrains.is_node_loaded(pos) then
|
||||
mesecon.receptor_on(pos)
|
||||
end
|
||||
end
|
||||
end,
|
||||
on_updated_from_nodedb = function(pos, node)
|
||||
mesecon.receptor_off(pos)
|
||||
end,
|
||||
},
|
||||
})
|
||||
|
||||
|
@ -38,17 +40,19 @@ minetest.override_item("mesecons_switch:mesecon_switch_on", {
|
|||
mesecon.receptor_off(pos)
|
||||
minetest.sound_play("mesecons_switch", {pos=pos})
|
||||
end,
|
||||
on_updated_from_nodedb = function(pos, node)
|
||||
mesecon.receptor_on(pos)
|
||||
end,
|
||||
advtrains = {
|
||||
getstate = "on",
|
||||
setstate = function(pos, node, newstate)
|
||||
if newstate=="off" then
|
||||
advtrains.ndb.swap_node(pos, {name="mesecons_switch:mesecon_switch_off", param2=node.param2})
|
||||
mesecon.receptor_off(pos)
|
||||
if advtrains.is_node_loaded(pos) then
|
||||
mesecon.receptor_off(pos)
|
||||
end
|
||||
end
|
||||
end,
|
||||
fallback_state = "off",
|
||||
on_updated_from_nodedb = function(pos, node)
|
||||
mesecon.receptor_on(pos)
|
||||
end,
|
||||
},
|
||||
})
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
-- 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 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
|
||||
-- path_speed- Populated by the LZB system. The maximum speed (velocity) permitted in the moment this path item is passed.
|
||||
-- (this saves brake distance calculations every step to determine LZB control). nil means no limit.
|
||||
--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.
|
||||
|
@ -52,6 +54,8 @@ function advtrains.path_create(train, pos, connid, rel_index)
|
|||
[0] = advtrains.conn_angle_median(conns[mconnid].c, conns[connid].c)
|
||||
}
|
||||
|
||||
train.path_speed = { }
|
||||
|
||||
train.path_ext_f=0
|
||||
train.path_ext_b=0
|
||||
train.path_trk_f=0
|
||||
|
@ -123,6 +127,7 @@ function advtrains.path_invalidate(train, ignore_lock)
|
|||
train.path_cp = nil
|
||||
train.path_cn = nil
|
||||
train.path_dir = nil
|
||||
train.path_speed = nil
|
||||
train.path_ext_f=0
|
||||
train.path_ext_b=0
|
||||
train.path_trk_f=0
|
||||
|
@ -131,6 +136,40 @@ function advtrains.path_invalidate(train, ignore_lock)
|
|||
train.path_req_b=0
|
||||
|
||||
train.dirty = true
|
||||
--atdebug(train.id, "Path invalidated")
|
||||
end
|
||||
|
||||
-- Keeps the path intact, but invalidates all path nodes from the specified index (inclusive)
|
||||
-- onwards. This has the advantage that we don't need to recalculate the whole path, and we can do it synchronously.
|
||||
function advtrains.path_invalidate_ahead(train, start_idx, ignore_when_passed)
|
||||
if not train.path then
|
||||
-- the path wasn't even initialized. Nothing to do
|
||||
return
|
||||
end
|
||||
|
||||
local idx = atfloor(start_idx)
|
||||
--atdebug("Invalidate_ahead:",train.id,"start_index",start_idx,"cur_idx",train.index)
|
||||
|
||||
if(idx <= train.index - 0.5) then
|
||||
if ignore_when_passed then
|
||||
--atdebug("ignored passed")
|
||||
return
|
||||
end
|
||||
advtrains.path_print(train, atwarn)
|
||||
error("Train "+train.id+": Cannot path_invalidate_ahead start_idx="+idx+" as train has already passed!")
|
||||
end
|
||||
|
||||
-- leave current node in path, it won't change. What might change is the path onward from here (e.g. switch)
|
||||
local i = idx + 1
|
||||
while train.path[i] do
|
||||
advtrains.occ.clear_item(train.id, advtrains.round_vector_floor_y(train.path[i]))
|
||||
i = i+1
|
||||
end
|
||||
train.path_ext_f=idx
|
||||
train.path_trk_f=math.min(idx, train.path_trk_f)
|
||||
|
||||
-- callbacks called anyway for current node, because of LZB
|
||||
advtrains.run_callbacks_invahead(train.id, train, idx)
|
||||
end
|
||||
|
||||
-- Prints a path using the passed print function
|
||||
|
@ -141,12 +180,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 Speed")
|
||||
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]," ",train.path_speed[i])
|
||||
if i==train.path_trk_f then
|
||||
printf("--Front on-track border here--")
|
||||
end
|
||||
|
@ -350,6 +389,7 @@ function advtrains.path_clear_unused(train)
|
|||
train.path_ext_b = i + 1
|
||||
end
|
||||
|
||||
--[[ Why exactly are we clearing path from the front? This doesn't make sense!
|
||||
for i = train.path_ext_f,train.path_req_f + PATH_CLEAR_KEEP,-1 do
|
||||
advtrains.occ.clear_item(train.id, advtrains.round_vector_floor_y(train.path[i]))
|
||||
train.path[i] = nil
|
||||
|
@ -358,14 +398,16 @@ function advtrains.path_clear_unused(train)
|
|||
train.path_cn[i] = nil
|
||||
train.path_dir[i+1] = nil
|
||||
train.path_ext_f = i - 1
|
||||
end
|
||||
end ]]
|
||||
train.path_trk_b = math.max(train.path_trk_b, train.path_ext_b)
|
||||
train.path_trk_f = math.min(train.path_trk_f, train.path_ext_f)
|
||||
--train.path_trk_f = math.min(train.path_trk_f, train.path_ext_f)
|
||||
|
||||
train.path_req_f = math.ceil(train.index)
|
||||
train.path_req_b = math.floor(train.end_index or train.index)
|
||||
end
|
||||
|
||||
-- Scan the path of the train for position, without querying the occupation table
|
||||
-- returns index, or nil if pos is not on the path
|
||||
function advtrains.path_lookup(train, pos)
|
||||
local cp = advtrains.round_vector_floor_y(pos)
|
||||
for i = train.path_ext_b, train.path_ext_f do
|
||||
|
|
|
@ -45,3 +45,14 @@ advtrains_overrun_mode (Overrun mode) enum drop none,drop,normal
|
|||
# When a wagon leaves this range + 32 nodes, it is unloaded
|
||||
# If unset, defaults to active_block_range*16
|
||||
advtrains_wagon_load_range (Wagon Entity Load/Unload Range) int 96 32 512
|
||||
|
||||
# Simulation DTime Limit after which slow-down becomes effective
|
||||
# When the dtime value (time since last server step) is higher than this value,
|
||||
# advtrains applies a global slow-down factor to the dtime and to the velocity and
|
||||
# acceleration of wagons to decrease server load.
|
||||
# A value of 0 (default) disables this behavior.
|
||||
advtrains_dtime_limit (DTime Limit for slow-down) float 0.2 0 5
|
||||
|
||||
# Time interval in seconds in which advtrains stores its save data to disk
|
||||
# Nevertheless, advtrains saves all data when shutting down the server.
|
||||
advtrains_save_interval (Save Interval) int 60 20 3600
|
||||
|
|
|
@ -12,22 +12,26 @@ end
|
|||
|
||||
local function aspect(b)
|
||||
return {
|
||||
main = {
|
||||
free = b,
|
||||
speed = -1,
|
||||
},
|
||||
shunt = {
|
||||
free = false,
|
||||
proceed_as_main = true
|
||||
},
|
||||
dst = {
|
||||
free = true,
|
||||
speed = -1,
|
||||
},
|
||||
main = (not b) and 0, -- b ? false : 0
|
||||
shunt = false,
|
||||
proceed_as_main = true,
|
||||
dst = false,
|
||||
info = {}
|
||||
}
|
||||
end
|
||||
|
||||
local suppasp = {
|
||||
main = {0, false},
|
||||
dst = {false},
|
||||
shunt = nil,
|
||||
proceed_as_main = true,
|
||||
info = {
|
||||
call_on = false,
|
||||
dead_end = false,
|
||||
w_speed = nil,
|
||||
}
|
||||
}
|
||||
|
||||
for r,f in pairs({on={as="off", ls="green", als="red"}, off={as="on", ls="red", als="green"}}) do
|
||||
|
||||
advtrains.trackplacer.register_tracktype("advtrains:retrosignal", "")
|
||||
|
@ -63,6 +67,7 @@ for r,f in pairs({on={as="off", ls="green", als="red"}, off={as="on", ls="red",
|
|||
rules=advtrains.meseconrules,
|
||||
["action_"..f.as] = function (pos, node)
|
||||
advtrains.ndb.swap_node(pos, {name = "advtrains:retrosignal_"..f.as..rotation, param2 = node.param2}, true)
|
||||
advtrains.interlocking.signal_on_aspect_changed(pos)
|
||||
end
|
||||
}},
|
||||
on_rightclick=function(pos, node, player)
|
||||
|
@ -74,12 +79,13 @@ for r,f in pairs({on={as="off", ls="green", als="red"}, off={as="on", ls="red",
|
|||
advtrains.interlocking.show_ip_form(pos, pname)
|
||||
elseif advtrains.check_turnout_signal_protection(pos, player:get_player_name()) then
|
||||
advtrains.ndb.swap_node(pos, {name = "advtrains:retrosignal_"..f.as..rotation, param2 = node.param2}, true)
|
||||
advtrains.interlocking.signal_on_aspect_changed(pos)
|
||||
end
|
||||
end,
|
||||
-- new signal API
|
||||
advtrains = {
|
||||
set_aspect = function(pos, node, asp)
|
||||
if asp.main.free then
|
||||
if asp.main ~= 0 then
|
||||
advtrains.ndb.swap_node(pos, {name = "advtrains:retrosignal_on"..rotation, param2 = node.param2}, true)
|
||||
else
|
||||
advtrains.ndb.swap_node(pos, {name = "advtrains:retrosignal_off"..rotation, param2 = node.param2}, true)
|
||||
|
@ -87,7 +93,8 @@ for r,f in pairs({on={as="off", ls="green", als="red"}, off={as="on", ls="red",
|
|||
end,
|
||||
get_aspect = function(pos, node)
|
||||
return aspect(r=="on")
|
||||
end
|
||||
end,
|
||||
supported_aspects = suppasp,
|
||||
},
|
||||
can_dig = can_dig_func,
|
||||
})
|
||||
|
@ -120,6 +127,7 @@ for r,f in pairs({on={as="off", ls="green", als="red"}, off={as="on", ls="red",
|
|||
rules=advtrains.meseconrules,
|
||||
["action_"..f.as] = function (pos, node)
|
||||
advtrains.setstate(pos, f.als, node)
|
||||
advtrains.interlocking.signal_on_aspect_changed(pos)
|
||||
end
|
||||
}},
|
||||
on_rightclick=function(pos, node, player)
|
||||
|
@ -131,12 +139,13 @@ for r,f in pairs({on={as="off", ls="green", als="red"}, off={as="on", ls="red",
|
|||
advtrains.interlocking.show_ip_form(pos, pname)
|
||||
elseif advtrains.check_turnout_signal_protection(pos, player:get_player_name()) then
|
||||
advtrains.setstate(pos, f.als, node)
|
||||
advtrains.interlocking.signal_on_aspect_changed(pos)
|
||||
end
|
||||
end,
|
||||
-- new signal API
|
||||
advtrains = {
|
||||
set_aspect = function(pos, node, asp)
|
||||
if asp.main.free then
|
||||
if asp.main ~= 0 then
|
||||
advtrains.ndb.swap_node(pos, {name = "advtrains:signal_on"..rotation, param2 = node.param2}, true)
|
||||
else
|
||||
advtrains.ndb.swap_node(pos, {name = "advtrains:signal_off"..rotation, param2 = node.param2}, true)
|
||||
|
@ -145,6 +154,7 @@ for r,f in pairs({on={as="off", ls="green", als="red"}, off={as="on", ls="red",
|
|||
get_aspect = function(pos, node)
|
||||
return aspect(r=="on")
|
||||
end,
|
||||
supported_aspects = suppasp,
|
||||
getstate = f.ls,
|
||||
setstate = function(pos, node, newstate)
|
||||
if newstate == f.als then
|
||||
|
@ -204,7 +214,7 @@ for r,f in pairs({on={as="off", ls="green", als="red"}, off={as="on", ls="red",
|
|||
-- new signal API
|
||||
advtrains = {
|
||||
set_aspect = function(pos, node, asp)
|
||||
if asp.main.free then
|
||||
if asp.main ~= 0 then
|
||||
advtrains.ndb.swap_node(pos, {name = "advtrains:signal_wall_"..loc.."_on", param2 = node.param2}, true)
|
||||
else
|
||||
advtrains.ndb.swap_node(pos, {name = "advtrains:signal_wall_"..loc.."_off", param2 = node.param2}, true)
|
||||
|
@ -213,6 +223,7 @@ for r,f in pairs({on={as="off", ls="green", als="red"}, off={as="on", ls="red",
|
|||
get_aspect = function(pos, node)
|
||||
return aspect(r=="on")
|
||||
end,
|
||||
supported_aspects = suppasp,
|
||||
getstate = f.ls,
|
||||
setstate = function(pos, node, newstate)
|
||||
if newstate == f.als then
|
||||
|
|
|
@ -275,8 +275,7 @@ function tp.register_track_placer(nnprefix, imgprefix, dispname, def)
|
|||
groups={advtrains_trackplacer=1, digtron_on_place=1},
|
||||
liquids_pointable = def.liquids_pointable,
|
||||
on_place = function(itemstack, placer, pointed_thing)
|
||||
return advtrains.pcall(function()
|
||||
local name = placer:get_player_name()
|
||||
local name = placer:get_player_name()
|
||||
if not name then
|
||||
return itemstack, false
|
||||
end
|
||||
|
@ -303,7 +302,6 @@ function tp.register_track_placer(nnprefix, imgprefix, dispname, def)
|
|||
end
|
||||
end
|
||||
return itemstack, true
|
||||
end)
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
@ -317,7 +315,6 @@ minetest.register_craftitem("advtrains:trackworker",{
|
|||
wield_image = "advtrains_trackworker.png",
|
||||
stack_max = 1,
|
||||
on_place = function(itemstack, placer, pointed_thing)
|
||||
return advtrains.pcall(function()
|
||||
local name = placer:get_player_name()
|
||||
if not name then
|
||||
return
|
||||
|
@ -382,10 +379,8 @@ minetest.register_craftitem("advtrains:trackworker",{
|
|||
advtrains.ndb.swap_node(pos, {name=nnprefix.."_"..suffix..modext[modpos+1], param2=node.param2})
|
||||
end
|
||||
end
|
||||
end)
|
||||
end,
|
||||
on_use=function(itemstack, user, pointed_thing)
|
||||
return advtrains.pcall(function()
|
||||
local name = user:get_player_name()
|
||||
if not name then
|
||||
return
|
||||
|
@ -430,7 +425,6 @@ minetest.register_craftitem("advtrains:trackworker",{
|
|||
else
|
||||
atprint(name, dump(tp.tracks))
|
||||
end
|
||||
end)
|
||||
end,
|
||||
})
|
||||
|
||||
|
|
|
@ -469,7 +469,7 @@ function advtrains.register_tracks(tracktype, def, preset)
|
|||
tiles = {def.shared_texture or (def.texture_prefix.."_"..img_suffix..".png"), def.second_texture},
|
||||
|
||||
groups = {
|
||||
attached_node=1,
|
||||
attached_node = advtrains.IGNORE_WORLD and 0 or 1,
|
||||
advtrains_track=1,
|
||||
["advtrains_track_"..tracktype]=1,
|
||||
save_in_at_nodedb=1,
|
||||
|
|
|
@ -43,7 +43,7 @@ function advtrains.on_control_change(pc, train, flip)
|
|||
else
|
||||
local act=false
|
||||
if pc.jump then
|
||||
train.ctrl.user = 1
|
||||
train.ctrl_user = 1
|
||||
act=true
|
||||
end
|
||||
-- If atc command set, only "Jump" key can clear command. To prevent accidental control.
|
||||
|
@ -51,15 +51,15 @@ function advtrains.on_control_change(pc, train, flip)
|
|||
return
|
||||
end
|
||||
if pc.up then
|
||||
train.ctrl.user=4
|
||||
train.ctrl_user=4
|
||||
act=true
|
||||
end
|
||||
if pc.down then
|
||||
if train.velocity>0 then
|
||||
if pc.jump then
|
||||
train.ctrl.user = 0
|
||||
train.ctrl_user = 0
|
||||
else
|
||||
train.ctrl.user = 2
|
||||
train.ctrl_user = 2
|
||||
end
|
||||
act=true
|
||||
else
|
||||
|
@ -82,7 +82,7 @@ function advtrains.on_control_change(pc, train, flip)
|
|||
end
|
||||
end
|
||||
if not act then
|
||||
train.ctrl.user = nil
|
||||
train.ctrl_user = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -186,6 +186,9 @@ function advtrains.hud_train_format(train, flip)
|
|||
|
||||
local tlev=train.lever or 1
|
||||
if train.velocity==0 and not train.active_control then tlev=1 end
|
||||
if train.hud_lzb_effect_tmr then
|
||||
tlev=1
|
||||
end
|
||||
|
||||
local ht = {"[combine:440x110:0,0=(advtrains_hud_bg.png^[resize\\:440x110)"}
|
||||
local st = {}
|
||||
|
@ -245,7 +248,7 @@ function advtrains.hud_train_format(train, flip)
|
|||
if train.tarvelocity or train.atc_command then
|
||||
ht[#ht+1] = "10,10=(advtrains_hud_atc.png^[resize\\:30x30^[multiply\\:cyan)"
|
||||
end
|
||||
if train.ctrl.lzb then
|
||||
if train.hud_lzb_effect_tmr then
|
||||
ht[#ht+1] = "50,10=(advtrains_hud_lzb.png^[resize\\:30x30^[multiply\\:red)"
|
||||
end
|
||||
if train.is_shunt then
|
||||
|
@ -274,10 +277,10 @@ function advtrains.hud_train_format(train, flip)
|
|||
ht[#ht+1] = sformat("%d,85=(advtrains_hud_arrow.png^[multiply\\:cyan^[transformFY^[makealpha\\:#000000)", 1+train.tarvelocity*11)
|
||||
end
|
||||
local lzb = train.lzb
|
||||
if lzb and lzb.oncoming then
|
||||
local oc = lzb.oncoming
|
||||
if lzb and lzb.checkpoints then
|
||||
local oc = lzb.checkpoints
|
||||
for i = 1, #oc do
|
||||
local spd = oc[i].spd
|
||||
local spd = oc[i].speed
|
||||
local c = not spd and "lime" or (type(spd) == "number" and (spd == 0) and "red" or "orange") or nil
|
||||
if c then
|
||||
ht[#ht+1] = sformat("130,10=(advtrains_hud_bg.png^[resize\\:30x5^[colorize\\:%s)",c)
|
||||
|
@ -286,7 +289,7 @@ function advtrains.hud_train_format(train, flip)
|
|||
ht[#ht+1] = sformat("%d,50=(advtrains_hud_arrow.png^[multiply\\:red^[makealpha\\:#000000)", 1+spd*11)
|
||||
end
|
||||
local floor = math.floor
|
||||
local dist = floor(((oc[i].idx or train.index)-train.index))
|
||||
local dist = floor(((oc[i].index or train.index)-train.index))
|
||||
dist = math.max(0, math.min(999, dist))
|
||||
for j = 1, 3, 1 do
|
||||
sevenseg(floor((dist/10^(3-j))%10), 119+j*11, 18, 4, 2, "[colorize\\:"..c)
|
||||
|
|
|
@ -36,6 +36,7 @@ end
|
|||
local t_accel_all={
|
||||
[0] = -10,
|
||||
[1] = -3,
|
||||
[11] = -2, -- calculation base for LZB
|
||||
[2] = -0.5,
|
||||
[4] = 0.5,
|
||||
}
|
||||
|
@ -43,19 +44,35 @@ local t_accel_all={
|
|||
local t_accel_eng={
|
||||
[0] = 0,
|
||||
[1] = 0,
|
||||
[11] = 0,
|
||||
[2] = 0,
|
||||
[4] = 1.5,
|
||||
}
|
||||
|
||||
local VLEVER_EMERG = 0
|
||||
local VLEVER_BRAKE = 1
|
||||
local VLEVER_LZBCALC = 11
|
||||
local VLEVER_ROLL = 2
|
||||
local VLEVER_HOLD = 3
|
||||
local VLEVER_ACCEL = 4
|
||||
|
||||
-- How far in front of a whole index with LZB 0 restriction the train should come to a halt
|
||||
-- value must be between 0 and 0.5, exclusively
|
||||
local LZB_ZERO_APPROACH_DIST = 0.1
|
||||
-- Speed the train temporarily approaches the stop point with
|
||||
local LZB_ZERO_APPROACH_SPEED = 0.2
|
||||
|
||||
|
||||
|
||||
tp_player_tmr = 0
|
||||
|
||||
advtrains.mainloop_trainlogic=function(dtime)
|
||||
advtrains.mainloop_trainlogic=function(dtime, stepno)
|
||||
--build a table of all players indexed by pts. used by damage and door system.
|
||||
advtrains.playersbypts={}
|
||||
for _, player in pairs(minetest.get_connected_players()) do
|
||||
if not advtrains.player_to_train_mapping[player:get_player_name()] then
|
||||
--players in train are not subject to damage
|
||||
local ptspos=minetest.pos_to_string(vector.round(player:getpos()))
|
||||
local ptspos=minetest.pos_to_string(vector.round(player:get_pos()))
|
||||
advtrains.playersbypts[ptspos]=player
|
||||
end
|
||||
end
|
||||
|
@ -81,6 +98,7 @@ advtrains.mainloop_trainlogic=function(dtime)
|
|||
|
||||
for k,v in pairs(advtrains.trains) do
|
||||
advtrains.atprint_context_tid=k
|
||||
--atprint("=== Step",stepno,"===")
|
||||
advtrains.train_ensure_init(k, v)
|
||||
end
|
||||
|
||||
|
@ -113,11 +131,10 @@ function advtrains.tp_player_to_train(player)
|
|||
--set the player to the train position.
|
||||
--minetest will emerge the area and load the objects, which then will call reattach_all().
|
||||
--because player is in mapping, it will not be subject to dying.
|
||||
player:setpos(train.last_pos)
|
||||
player:set_pos(train.last_pos)
|
||||
end
|
||||
end
|
||||
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
|
||||
|
@ -127,12 +144,10 @@ minetest.register_on_joinplayer(function(player)
|
|||
wagon:reattach_all()
|
||||
end
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
|
||||
minetest.register_on_dieplayer(function(player)
|
||||
return advtrains.pcall(function()
|
||||
local pname=player:get_player_name()
|
||||
local id=advtrains.player_to_train_mapping[pname]
|
||||
if id then
|
||||
|
@ -146,7 +161,6 @@ minetest.register_on_dieplayer(function(player)
|
|||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
--[[
|
||||
|
@ -190,6 +204,8 @@ end
|
|||
|
||||
function advtrains.get_acceleration(train, lever)
|
||||
local acc_all = t_accel_all[lever]
|
||||
if not acc_all then return 0 end
|
||||
|
||||
local acc_eng = t_accel_eng[lever]
|
||||
local nwagons = #train.trainparts
|
||||
if nwagons == 0 then
|
||||
|
@ -215,14 +231,16 @@ local function mkcallback(name)
|
|||
assertt(func, "function")
|
||||
table.insert(callt, func)
|
||||
end
|
||||
return callt, function(id, train)
|
||||
return callt, function(id, train, param1, param2, param3)
|
||||
for _,f in ipairs(callt) do
|
||||
f(id, train)
|
||||
f(id, train, param1, param2, param3)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local callbacks_new_path, run_callbacks_new_path = mkcallback("new_path")
|
||||
local callbacks_invahead
|
||||
callbacks_invahead, advtrains.run_callbacks_invahead = mkcallback("invalidate_ahead") -- (id, train, start_idx)
|
||||
local callbacks_update, run_callbacks_update = mkcallback("update")
|
||||
local callbacks_create, run_callbacks_create = mkcallback("create")
|
||||
local callbacks_remove, run_callbacks_remove = mkcallback("remove")
|
||||
|
@ -240,21 +258,25 @@ function advtrains.train_ensure_init(id, train)
|
|||
end
|
||||
|
||||
train.dirty = true
|
||||
if train.no_step then return nil end
|
||||
if train.no_step then
|
||||
--atprint("in ensure_init: no_step set, train step ignored!")
|
||||
return nil
|
||||
end
|
||||
|
||||
assertdef(train, "velocity", 0)
|
||||
--assertdef(train, "tarvelocity", 0)
|
||||
assertdef(train, "acceleration", 0)
|
||||
assertdef(train, "id", id)
|
||||
assertdef(train, "ctrl", {})
|
||||
|
||||
|
||||
if not train.drives_on or not train.max_speed then
|
||||
--atprint("in ensure_init: missing properties, updating!")
|
||||
advtrains.update_trainpart_properties(id)
|
||||
end
|
||||
|
||||
--restore path
|
||||
if not train.path then
|
||||
--atprint("in ensure_init: Needs restoring path...")
|
||||
if not train.last_pos then
|
||||
atlog("Train",id,": Restoring path failed, no last_pos set! Train will be disabled. You can try to fix the issue in the save file.")
|
||||
train.no_step = true
|
||||
|
@ -277,6 +299,8 @@ function advtrains.train_ensure_init(id, train)
|
|||
|
||||
local result = advtrains.path_create(train, train.last_pos, train.last_connid or 1, train.last_frac or 0)
|
||||
|
||||
--atprint("in ensure_init: path_create result ",result)
|
||||
|
||||
if result==false then
|
||||
atlog("Train",id,": Restoring path failed, node at",train.last_pos,"is gone! Train will be disabled. You can try to place a rail at this position and restart the server.")
|
||||
train.no_step = true
|
||||
|
@ -304,6 +328,10 @@ function advtrains.train_ensure_init(id, train)
|
|||
return true
|
||||
end
|
||||
|
||||
local function v_target_apply(v_targets, lever, vel)
|
||||
v_targets[lever] = v_targets[lever] and math.min(v_targets[lever], vel) or vel
|
||||
end
|
||||
|
||||
function advtrains.train_step_b(id, train, dtime)
|
||||
if train.no_step or train.wait_for_path or not train.path then return end
|
||||
|
||||
|
@ -311,25 +339,49 @@ function advtrains.train_step_b(id, train, dtime)
|
|||
advtrains.path_get(train, atfloor(train.index + 2))
|
||||
advtrains.path_get(train, atfloor(train.end_index - 1))
|
||||
|
||||
-- run pre-move hooks
|
||||
-- TODO: if more pre-move hooks are added, make a separate callback hook
|
||||
advtrains.lzb_look_ahead(id, train)
|
||||
|
||||
--[[ again, new velocity control:
|
||||
There are two heterogenous means of control:
|
||||
-> set a fixed acceleration and ignore speed (user)
|
||||
-> steer towards a target speed, distance doesn't matter
|
||||
-> needs to specify the maximum acceleration/deceleration values they are willing to accelerate/brake with
|
||||
-> Reach a target speed after a certain distance (LZB, handled specially)
|
||||
|
||||
]]--
|
||||
|
||||
--- 3. handle velocity influences ---
|
||||
local train_moves=(train.velocity~=0)
|
||||
local tarvel_cap = train.speed_restriction
|
||||
|
||||
local v0 = train.velocity
|
||||
local sit_v_cap = train.max_speed -- Maximum speed in current situation (multiple limit factors)
|
||||
-- The desired speed change issued by the active control (user or atc)
|
||||
local ctrl_v_tar -- desired speed which should not be crossed by braking or accelerating
|
||||
local ctrl_accelerating = false -- whether the train should accelerate
|
||||
local ctrl_braking = false -- whether the train should brake
|
||||
local ctrl_lever -- the lever value to use to calculate the acceleration
|
||||
-- the final speed change after applying LZB
|
||||
local v_cap -- absolute maximum speed
|
||||
local v_tar -- desired speed which should not be crossed by braking or accelerating
|
||||
local accelerating = false-- whether the train should accelerate
|
||||
local braking = false -- whether the train should brake
|
||||
local lever -- the lever value to use to calculate the acceleration
|
||||
local train_moves = (v0 > 0)
|
||||
|
||||
if train.recently_collided_with_env then
|
||||
tarvel_cap=0
|
||||
if not train_moves then
|
||||
train.recently_collided_with_env=nil--reset status when stopped
|
||||
end
|
||||
end
|
||||
if train.locomotives_in_train==0 then
|
||||
tarvel_cap=0
|
||||
end
|
||||
|
||||
--- 3a. this can be useful for debugs/warnings and is used for check_trainpartload ---
|
||||
local t_info, train_pos=sid(id), advtrains.path_get(train, atfloor(train.index))
|
||||
if train_pos then
|
||||
t_info=t_info.." @"..minetest.pos_to_string(train_pos)
|
||||
--atprint("train_pos:",train_pos)
|
||||
--atprint("in train_step_b: applying collided_with_env")
|
||||
sit_v_cap = 0
|
||||
elseif train.locomotives_in_train==0 then
|
||||
--atprint("in train_step_b: applying no_locomotives")
|
||||
sit_v_cap = 0
|
||||
-- interlocking speed restriction
|
||||
elseif train.speed_restriction then
|
||||
--atprint("in train_step_b: applying interlocking speed restriction",train.speed_restriction)
|
||||
sit_v_cap = train.speed_restriction
|
||||
end
|
||||
|
||||
--apply off-track handling:
|
||||
|
@ -337,38 +389,47 @@ function advtrains.train_step_b(id, train, dtime)
|
|||
local back_off_track=train.end_index<train.path_trk_b
|
||||
train.off_track = front_off_track or back_off_track
|
||||
|
||||
if front_off_track then
|
||||
tarvel_cap=0
|
||||
end
|
||||
if back_off_track then -- eventually overrides front_off_track restriction
|
||||
tarvel_cap=1
|
||||
if back_off_track and (not v_cap or v_cap > 1) then
|
||||
--atprint("in train_step_b: applying back_off_track")
|
||||
sit_v_cap = 1
|
||||
elseif front_off_track then
|
||||
--atprint("in train_step_b: applying front_off_track")
|
||||
sit_v_cap = 0
|
||||
end
|
||||
|
||||
-- Driving control rework:
|
||||
--[[
|
||||
Items are only defined when something is controlling them.
|
||||
In order of precedence.
|
||||
train.ctrl = {
|
||||
lzb = restrictive override from LZB
|
||||
user = User input from driverstand
|
||||
atc = ATC command override (determined here)
|
||||
}
|
||||
The code here determines the precedence and writes the final control into train.lever
|
||||
]]
|
||||
|
||||
--interpret ATC command and apply auto-lever control when not actively controlled
|
||||
local trainvelocity = train.velocity
|
||||
|
||||
if train.ctrl.user then
|
||||
local userc = train.ctrl_user
|
||||
if userc then
|
||||
--atprint("in train_step_b: ctrl_user active",userc)
|
||||
advtrains.atc.train_reset_command(train)
|
||||
|
||||
if userc >= VLEVER_ACCEL then
|
||||
ctrl_accelerating = true
|
||||
else
|
||||
ctrl_braking = true
|
||||
end
|
||||
ctrl_lever = userc
|
||||
else
|
||||
if train.atc_command then
|
||||
if (not train.atc_delay or train.atc_delay<=0) and not train.atc_wait_finish then
|
||||
advtrains.atc.execute_atc_command(id, train)
|
||||
else
|
||||
train.atc_delay=train.atc_delay-dtime
|
||||
end
|
||||
elseif train.atc_delay then
|
||||
train.atc_delay = nil
|
||||
end
|
||||
|
||||
local braketar = train.atc_brake_target
|
||||
local emerg = false -- atc_brake_target==-1 means emergency brake (BB command)
|
||||
if braketar == -1 then
|
||||
braketar = 0
|
||||
emerg = true
|
||||
end
|
||||
if braketar and braketar>=trainvelocity then
|
||||
--atprint("in train_step_b: ATC: brake state braketar=",braketar,"emerg=",emerg)
|
||||
if braketar and braketar>=v0 then
|
||||
--atprint("in train_step_b: ATC: brake target cleared")
|
||||
train.atc_brake_target=nil
|
||||
braketar = nil
|
||||
end
|
||||
|
@ -380,111 +441,175 @@ function advtrains.train_step_b(id, train, dtime)
|
|||
train.atc_wait_finish=nil
|
||||
end
|
||||
end
|
||||
if train.atc_command then
|
||||
if (not train.atc_delay or train.atc_delay<=0) and not train.atc_wait_finish then
|
||||
advtrains.atc.execute_atc_command(id, train)
|
||||
else
|
||||
train.atc_delay=train.atc_delay-dtime
|
||||
end
|
||||
elseif train.atc_delay then
|
||||
train.atc_delay = nil
|
||||
end
|
||||
|
||||
train.ctrl.atc = nil
|
||||
if train.tarvelocity and train.tarvelocity>trainvelocity then
|
||||
train.ctrl.atc=4
|
||||
end
|
||||
if train.tarvelocity and train.tarvelocity<trainvelocity then
|
||||
if (braketar and braketar<trainvelocity) then
|
||||
if train.tarvelocity and train.tarvelocity>v0 then
|
||||
--atprint("in train_step_b: applying ATC ACCEL", train.tarvelocity)
|
||||
ctrl_accelerating = true
|
||||
ctrl_lever = VLEVER_ACCEL
|
||||
elseif train.tarvelocity and train.tarvelocity<v0 then
|
||||
ctrl_braking = true
|
||||
|
||||
if (braketar and braketar<v0) then
|
||||
if emerg then
|
||||
train.ctrl.atc = 0
|
||||
--atprint("in train_step_b: applying ATC EMERG", train.tarvelocity)
|
||||
ctrl_lever = VLEVER_EMERG
|
||||
else
|
||||
train.ctrl.atc=1
|
||||
--atprint("in train_step_b: applying ATC BRAKE", train.tarvelocity)
|
||||
ctrl_v_tar = braketar
|
||||
ctrl_lever = VLEVER_BRAKE
|
||||
end
|
||||
else
|
||||
train.ctrl.atc=2
|
||||
--atprint("in train_step_b: applying ATC ROLL", train.tarvelocity)
|
||||
ctrl_v_tar = train.tarvelocity
|
||||
ctrl_lever = VLEVER_ROLL
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--if tarvel_cap and train.tarvelocity and tarvel_cap<train.tarvelocity then
|
||||
-- train.tarvelocity=tarvel_cap
|
||||
--end
|
||||
--- 2b. look at v_target, determine the effective v_target and desired acceleration ---
|
||||
--atprint("in train_step_b: Resulting control before LZB: accelerating",ctrl_accelerating,"braking",ctrl_braking,"lever", ctrl_lever, "target", ctrl_v_tar)
|
||||
--train.debug = dump({tv_target,tv_lever})
|
||||
|
||||
local tmp_lever
|
||||
--atprint("in train_step_b: Current index",train.index,"end",train.end_index,"vel",v0)
|
||||
--- 3a. calculate the acceleration required to reach the speed restriction in path_speed (LZB) ---
|
||||
-- Iterates over the path nodes we WOULD pass if we were continuing with the current speed
|
||||
-- and determines the MINIMUM of path_speed in this range.
|
||||
-- Then, determines acceleration so that we can reach this 'overridden' target speed in this step (but short-circuited)
|
||||
local lzb_next_zero_barrier -- if defined, train should not pass this point as it's a 0-LZB
|
||||
local new_index_curr_tv -- pre-calculated new train index in lzb check
|
||||
local lzb_v_cap -- the maximum speed that LZB dictates
|
||||
|
||||
for _, lev in pairs(train.ctrl) do
|
||||
-- use the most restrictive of all control overrides
|
||||
tmp_lever = math.min(tmp_lever or 4, lev)
|
||||
end
|
||||
|
||||
if not tmp_lever then
|
||||
-- if there was no control at all, default to 3
|
||||
tmp_lever = 3
|
||||
end
|
||||
|
||||
if tarvel_cap and trainvelocity>tarvel_cap then
|
||||
tmp_lever = 0
|
||||
end
|
||||
|
||||
train.lever = tmp_lever
|
||||
|
||||
--- 3a. actually calculate new velocity ---
|
||||
if tmp_lever~=3 then
|
||||
local accel = advtrains.get_acceleration(train, tmp_lever)
|
||||
local vdiff = accel*dtime
|
||||
|
||||
-- This should only be executed when we are accelerating
|
||||
-- I suspect that this causes the braking bugs
|
||||
if tmp_lever == 4 then
|
||||
|
||||
-- ATC control exception: don't cross tarvelocity if
|
||||
-- atc provided a target_vel
|
||||
if train.tarvelocity then
|
||||
local tvdiff = train.tarvelocity - trainvelocity
|
||||
if tvdiff~=0 and math.abs(vdiff) > math.abs(tvdiff) then
|
||||
--applying this change would cross tarvelocity
|
||||
--atdebug("In Tvdiff condition, clipping",vdiff,"to",tvdiff)
|
||||
--atdebug("vel=",trainvelocity,"tvel=",train.tarvelocity)
|
||||
vdiff=tvdiff
|
||||
end
|
||||
end
|
||||
if tarvel_cap and trainvelocity<=tarvel_cap and trainvelocity+vdiff>tarvel_cap then
|
||||
vdiff = tarvel_cap - train.velocity
|
||||
end
|
||||
local mspeed = (train.max_speed or 10)
|
||||
if trainvelocity+vdiff > mspeed then
|
||||
vdiff = mspeed - trainvelocity
|
||||
local dst_curr_v = v0 * dtime
|
||||
new_index_curr_tv = advtrains.path_get_index_by_offset(train, train.index, dst_curr_v)
|
||||
local i = atfloor(train.index)
|
||||
local psp
|
||||
while true do
|
||||
psp = train.path_speed[i]
|
||||
if psp then
|
||||
lzb_v_cap = lzb_v_cap and math.min(lzb_v_cap, psp) or psp
|
||||
if psp == 0 and not lzb_next_zero_barrier then
|
||||
--atprint("in train_step_b: Found zero barrier: ",i)
|
||||
lzb_next_zero_barrier = i - LZB_ZERO_APPROACH_DIST
|
||||
end
|
||||
end
|
||||
|
||||
if trainvelocity+vdiff < 0 then
|
||||
vdiff = - trainvelocity
|
||||
if i > new_index_curr_tv then
|
||||
break
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
if lzb_next_zero_barrier and train.index < lzb_next_zero_barrier then
|
||||
lzb_v_cap = LZB_ZERO_APPROACH_SPEED
|
||||
end
|
||||
|
||||
train.acceleration=vdiff
|
||||
train.velocity=train.velocity+vdiff
|
||||
--if train.ctrl.user then
|
||||
-- train.tarvelocity = train.velocity
|
||||
--end
|
||||
else
|
||||
--atprint("in train_step_b: LZB calculation yields newindex=",new_index_curr_tv,"lzbtarget=",lzb_v_cap,"zero_barr=",lzb_next_zero_barrier,"")
|
||||
|
||||
-- LZB HUD: decrement timer and delete when 0
|
||||
if train.hud_lzb_effect_tmr then
|
||||
if train.hud_lzb_effect_tmr <=0 then
|
||||
train.hud_lzb_effect_tmr = nil
|
||||
else
|
||||
train.hud_lzb_effect_tmr = train.hud_lzb_effect_tmr - 1
|
||||
end
|
||||
end
|
||||
|
||||
-- We now need to bring ctrl_*, sit_v_cap and lzb_v_cap together to determine the final controls.
|
||||
local v_cap = sit_v_cap -- always defined, by default train.max_speed
|
||||
if lzb_v_cap and lzb_v_cap < v_cap then
|
||||
v_cap = lzb_v_cap
|
||||
lever = VLEVER_BRAKE -- actually irrelevant, acceleration is not considered anyway unless v_tar is also set.
|
||||
-- display LZB control override in the HUD
|
||||
if lzb_v_cap <= v0 then
|
||||
train.hud_lzb_effect_tmr = 1
|
||||
-- This is to signal the HUD that LZB is active. This works as a timer to avoid HUD blinking
|
||||
end
|
||||
end
|
||||
|
||||
v_tar = ctrl_v_tar
|
||||
-- if v_cap is smaller than the current speed, we need to brake in all cases.
|
||||
if v_cap < v0 then
|
||||
braking = true
|
||||
lever = VLEVER_BRAKE
|
||||
-- set v_tar to v_cap to not slow down any further than required.
|
||||
-- unless control wants us to brake too, then we use control's v_tar.
|
||||
if not ctrl_v_tar or ctrl_v_tar > v_cap then
|
||||
v_tar = v_cap
|
||||
end
|
||||
else -- else, use what the ctrl says
|
||||
braking = ctrl_braking
|
||||
accelerating = ctrl_accelerating and not braking
|
||||
lever = ctrl_lever
|
||||
end
|
||||
train.lever = lever
|
||||
|
||||
--atprint("in train_step_b: final control: accelerating",accelerating,"braking",braking,"lever", lever, "target", v_tar)
|
||||
|
||||
-- reset train acceleration when holding speed
|
||||
if not braking and not accelerating then
|
||||
train.acceleration = 0
|
||||
end
|
||||
|
||||
--- 3b. if braking, modify the velocity BEFORE the movement
|
||||
if braking then
|
||||
local dv = advtrains.get_acceleration(train, lever) * dtime
|
||||
local v1 = v0 + dv
|
||||
if v_tar and v1 < v_tar then
|
||||
--atprint("in train_step_b: Braking: Hit v_tar!")
|
||||
v1 = v_tar
|
||||
end
|
||||
if v1 > v_cap then
|
||||
--atprint("in train_step_b: Braking: Hit v_cap!")
|
||||
v1 = v_cap
|
||||
end
|
||||
if v1 < 0 then
|
||||
--atprint("in train_step_b: Braking: Hit 0!")
|
||||
v1 = 0
|
||||
end
|
||||
|
||||
train.acceleration = (v1 - v0) / dtime
|
||||
train.velocity = v1
|
||||
--atprint("in train_step_b: Braking: New velocity",v1," (yields acceleration",train.acceleration,")")
|
||||
-- make saved new_index_curr_tv invalid because speed has changed
|
||||
new_index_curr_tv = nil
|
||||
end
|
||||
|
||||
--- 4. move train ---
|
||||
-- if we have calculated the new end index before, don't do that again
|
||||
if not new_index_curr_tv then
|
||||
local dst_curr_v = train.velocity * dtime
|
||||
new_index_curr_tv = advtrains.path_get_index_by_offset(train, train.index, dst_curr_v)
|
||||
--atprint("in train_step_b: movement calculation (re)done, yields newindex=",new_index_curr_tv)
|
||||
else
|
||||
--atprint("in train_step_b: movement calculation reusing from LZB newindex=",new_index_curr_tv)
|
||||
end
|
||||
|
||||
local idx_floor = math.floor(train.index)
|
||||
local pdist = (train.path_dist[idx_floor+1] - train.path_dist[idx_floor])
|
||||
local distance = (train.velocity*dtime) / pdist
|
||||
|
||||
--debugging code
|
||||
--train.debug = atdump(train.ctrl).."step_dist: "..math.floor(distance*1000)
|
||||
|
||||
train.index=train.index+distance
|
||||
-- if the zeroappr mechanism has hit, go no further than zeroappr index
|
||||
if lzb_next_zero_barrier and new_index_curr_tv > lzb_next_zero_barrier then
|
||||
--atprint("in train_step_b: Zero barrier hit, clipping to newidx_tv=",new_index_curr_tv, "zb_idx=",lzb_next_zero_barrier)
|
||||
new_index_curr_tv = lzb_next_zero_barrier
|
||||
end
|
||||
train.index = new_index_curr_tv
|
||||
|
||||
recalc_end_index(train)
|
||||
--atprint("in train_step_b: New index",train.index,"end",train.end_index,"vel",train.velocity)
|
||||
|
||||
--- 4a. if accelerating, modify the velocity AFTER the movement
|
||||
if accelerating then
|
||||
local dv = advtrains.get_acceleration(train, lever) * dtime
|
||||
local v1 = v0 + dv
|
||||
if v_tar and v1 > v_tar then
|
||||
--atprint("in train_step_b: Accelerating: Hit v_tar!")
|
||||
v1 = v_tar
|
||||
end
|
||||
if v1 > v_cap then
|
||||
--atprint("in train_step_b: Accelerating: Hit v_cap!")
|
||||
v1 = v_cap
|
||||
end
|
||||
|
||||
train.acceleration = (v1 - v0) / dtime
|
||||
train.velocity = v1
|
||||
--atprint("in train_step_b: Accelerating: New velocity",v1," (yields acceleration",train.acceleration,")")
|
||||
end
|
||||
end
|
||||
|
||||
function advtrains.train_step_c(id, train, dtime)
|
||||
|
@ -527,7 +652,7 @@ function advtrains.train_step_c(id, train, dtime)
|
|||
local collpos = advtrains.path_get(train, atround(collindex))
|
||||
if collpos then
|
||||
local rcollpos=advtrains.round_vector_floor_y(collpos)
|
||||
local is_loaded_area = minetest.get_node_or_nil(rcollpos) ~= nil
|
||||
local is_loaded_area = advtrains.is_node_loaded(rcollpos)
|
||||
for x=-train.extent_h,train.extent_h do
|
||||
for z=-train.extent_h,train.extent_h do
|
||||
local testpos=vector.add(rcollpos, {x=x, y=0, z=z})
|
||||
|
@ -609,8 +734,9 @@ local callbacks_enter_node, run_callbacks_enter_node = mknodecallback("enter")
|
|||
local callbacks_leave_node, run_callbacks_leave_node = mknodecallback("leave")
|
||||
|
||||
-- Node callback for approaching
|
||||
-- Might be called multiple times, whenever path is recalculated
|
||||
-- signature is function(pos, id, train, index, lzbdata)
|
||||
-- Might be called multiple times, whenever path is recalculated. Also called for the first node the train is standing on, then has_entered is true.
|
||||
-- signature is function(pos, id, train, index, has_entered, lzbdata)
|
||||
-- has_entered: true if the "enter" callback has already been executed for this train in this location
|
||||
-- lzbdata: arbitrary data (shared between all callbacks), deleted when LZB is restarted.
|
||||
-- These callbacks are called in order of distance as train progresses along tracks, so lzbdata can be used to
|
||||
-- keep track of a train's state once it passes this point
|
||||
|
@ -666,16 +792,19 @@ end
|
|||
|
||||
function advtrains.tnc_call_approach_callback(pos, train_id, train, index, lzbdata)
|
||||
--atdebug("tnc approach",pos,train_id, lzbdata)
|
||||
local has_entered = atround(train.index) == index
|
||||
|
||||
local node = advtrains.ndb.get_node(pos) --this spares the check if node is nil, it has a name in any case
|
||||
local mregnode=minetest.registered_nodes[node.name]
|
||||
if mregnode and mregnode.advtrains and mregnode.advtrains.on_train_approach then
|
||||
mregnode.advtrains.on_train_approach(pos, train_id, train, index, lzbdata)
|
||||
mregnode.advtrains.on_train_approach(pos, train_id, train, index, has_entered, lzbdata)
|
||||
end
|
||||
|
||||
-- call other registered callbacks
|
||||
run_callbacks_approach_node(pos, train_id, train, index, lzbdata)
|
||||
run_callbacks_approach_node(pos, train_id, train, index, has_entered, lzbdata)
|
||||
end
|
||||
|
||||
-- === te callback definition for tnc node callbacks ===
|
||||
|
||||
advtrains.te_register_on_new_path(function(id, train)
|
||||
train.tnc = {
|
||||
|
@ -865,19 +994,12 @@ function advtrains.spawn_wagons(train_id)
|
|||
atwarn("Train",train_id,"Wagon #",i,": Saved train ID",data.train_id,"did not match!")
|
||||
data.train_id = train_id
|
||||
end
|
||||
if not advtrains.wagon_objects[w_id] or not advtrains.wagon_objects[w_id]:getyaw() then
|
||||
if not advtrains.wagon_objects[w_id] or not advtrains.wagon_objects[w_id]:get_yaw() then
|
||||
-- eventually need to spawn new object. check if position is loaded.
|
||||
local index = advtrains.path_get_index_by_offset(train, train.index, -data.pos_in_train)
|
||||
local pos = advtrains.path_get(train, atfloor(index))
|
||||
|
||||
local spawn = false
|
||||
for _,p in pairs(minetest.get_connected_players()) do
|
||||
if vector.distance(p:get_pos(),pos)<=ablkrng then
|
||||
spawn = true
|
||||
end
|
||||
end
|
||||
|
||||
if spawn then
|
||||
if advtrains.position_in_range(pos, ablkrng) then
|
||||
--atdebug("wagon",w_id,"spawning")
|
||||
local wt = advtrains.get_wagon_prototype(data)
|
||||
local wagon = minetest.add_entity(pos, wt):get_luaentity()
|
||||
|
@ -1024,7 +1146,7 @@ end
|
|||
function advtrains.train_check_couples(train)
|
||||
--atdebug("rechecking couples")
|
||||
if train.cpl_front then
|
||||
if not train.cpl_front:getyaw() then
|
||||
if not train.cpl_front:get_yaw() then
|
||||
-- objectref is no longer valid. reset.
|
||||
train.cpl_front = nil
|
||||
end
|
||||
|
@ -1032,7 +1154,7 @@ function advtrains.train_check_couples(train)
|
|||
if not train.cpl_front then
|
||||
-- recheck front couple
|
||||
local front_trains, pos = advtrains.occ.get_occupations(train, atround(train.index) + CPL_CHK_DST)
|
||||
if minetest.get_node_or_nil(pos) then -- if the position is loaded...
|
||||
if advtrains.is_node_loaded(pos) then -- if the position is loaded...
|
||||
for tid, idx in pairs(front_trains) do
|
||||
local other_train = advtrains.trains[tid]
|
||||
if not advtrains.train_ensure_init(tid, other_train) then
|
||||
|
@ -1054,7 +1176,7 @@ function advtrains.train_check_couples(train)
|
|||
end
|
||||
end
|
||||
if train.cpl_back then
|
||||
if not train.cpl_back:getyaw() then
|
||||
if not train.cpl_back:get_yaw() then
|
||||
-- objectref is no longer valid. reset.
|
||||
train.cpl_back = nil
|
||||
end
|
||||
|
@ -1062,7 +1184,7 @@ function advtrains.train_check_couples(train)
|
|||
if not train.cpl_back then
|
||||
-- recheck back couple
|
||||
local back_trains, pos = advtrains.occ.get_occupations(train, atround(train.end_index) - CPL_CHK_DST)
|
||||
if minetest.get_node_or_nil(pos) then -- if the position is loaded...
|
||||
if advtrains.is_node_loaded(pos) then -- if the position is loaded...
|
||||
for tid, idx in pairs(back_trains) do
|
||||
local other_train = advtrains.trains[tid]
|
||||
if not advtrains.train_ensure_init(tid, other_train) then
|
||||
|
@ -1202,6 +1324,18 @@ function advtrains.invalidate_all_paths(pos)
|
|||
advtrains.invalidate_path(id)
|
||||
end
|
||||
end
|
||||
|
||||
-- Calls invalidate_path_ahead on all trains occupying (having paths over) this node
|
||||
-- Can be called during train step.
|
||||
function advtrains.invalidate_all_paths_ahead(pos)
|
||||
local tab = advtrains.occ.get_trains_over(pos)
|
||||
|
||||
for id,index in pairs(tab) do
|
||||
local train = advtrains.trains[id]
|
||||
advtrains.path_invalidate_ahead(train, index, true)
|
||||
end
|
||||
end
|
||||
|
||||
function advtrains.invalidate_path(id)
|
||||
--atdebug("Path invalidate:",id)
|
||||
local v=advtrains.trains[id]
|
||||
|
|
|
@ -10,24 +10,15 @@
|
|||
-- TP delay when getting off wagon
|
||||
local GETOFF_TP_DELAY = 0.5
|
||||
|
||||
local IGNORE_WORLD = advtrains.IGNORE_WORLD
|
||||
|
||||
advtrains.wagons = {}
|
||||
advtrains.wagon_prototypes = {}
|
||||
advtrains.wagon_objects = {}
|
||||
|
||||
local unload_wgn_range = advtrains.wagon_load_range + 32
|
||||
function advtrains.outside_range(pos) -- returns true if the object is outside of unload_wgn_range of any player
|
||||
-- this is part of a workaround until mintest core devs decide to fix a bug with static_save=false.
|
||||
local outofrange = true
|
||||
if not pos then
|
||||
return true
|
||||
end
|
||||
for _,p in pairs(minetest.get_connected_players()) do
|
||||
if vector.distance(p:get_pos(),pos)<=unload_wgn_range then
|
||||
outofrange = false
|
||||
break
|
||||
end
|
||||
end
|
||||
return outofrange
|
||||
function advtrains.wagon_outside_range(pos) -- returns true if the object is outside of unload_wgn_range of any player
|
||||
return not advtrains.position_in_range(pos, unload_wgn_range)
|
||||
end
|
||||
|
||||
local setting_show_ids = minetest.settings:get_bool("advtrains_show_ids")
|
||||
|
@ -165,7 +156,7 @@ function wagon:ensure_init()
|
|||
atwarn("wagon",self.id,"uninitialized, removing")
|
||||
self:destroy()
|
||||
else
|
||||
self.object:setvelocity({x=0,y=0,z=0})
|
||||
self.object:set_velocity({x=0,y=0,z=0})
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
@ -177,7 +168,6 @@ end
|
|||
|
||||
-- Remove the wagon
|
||||
function wagon:on_punch(puncher, time_from_last_punch, tool_capabilities, direction)
|
||||
return advtrains.pcall(function()
|
||||
if not self:ensure_init() then return end
|
||||
|
||||
local data = advtrains.wagons[self.id]
|
||||
|
@ -234,7 +224,6 @@ function wagon:on_punch(puncher, time_from_last_punch, tool_capabilities, direct
|
|||
for _,item in ipairs(self.drops or {self.name}) do
|
||||
inv:add_item("main", item)
|
||||
end
|
||||
end)
|
||||
end
|
||||
function wagon:destroy()
|
||||
--some rules:
|
||||
|
@ -279,11 +268,15 @@ function wagon:is_driver_stand(seat)
|
|||
end
|
||||
|
||||
function wagon:on_step(dtime)
|
||||
return advtrains.pcall(function()
|
||||
if not self:ensure_init() then return end
|
||||
|
||||
if advtrains.is_no_action() then
|
||||
self.object:remove()
|
||||
return
|
||||
end
|
||||
|
||||
local t=os.clock()
|
||||
local pos = self.object:getpos()
|
||||
local pos = self.object:get_pos()
|
||||
local data = advtrains.wagons[self.id]
|
||||
|
||||
if not pos then
|
||||
|
@ -300,6 +293,8 @@ function wagon:on_step(dtime)
|
|||
|
||||
local train=self:train()
|
||||
|
||||
local is_in_loaded_area = advtrains.is_node_loaded(pos)
|
||||
|
||||
--custom on_step function
|
||||
if self.custom_on_step then
|
||||
self:custom_on_step(dtime, data, train)
|
||||
|
@ -409,8 +404,8 @@ function wagon:on_step(dtime)
|
|||
|
||||
--for path to be available. if not, skip step
|
||||
if not train.path or train.no_step then
|
||||
self.object:setvelocity({x=0, y=0, z=0})
|
||||
self.object:setacceleration({x=0, y=0, z=0})
|
||||
self.object:set_velocity({x=0, y=0, z=0})
|
||||
self.object:set_acceleration({x=0, y=0, z=0})
|
||||
return
|
||||
end
|
||||
if not data.pos_in_train then
|
||||
|
@ -453,7 +448,7 @@ function wagon:on_step(dtime)
|
|||
end
|
||||
|
||||
--checking for environment collisions(a 3x3 cube around the center)
|
||||
if not train.recently_collided_with_env then
|
||||
if not IGNORE_WORLD and is_in_loaded_area and not train.recently_collided_with_env then
|
||||
local collides=false
|
||||
local exh = self.extent_h or 1
|
||||
local exv = self.extent_v or 2
|
||||
|
@ -477,9 +472,9 @@ function wagon:on_step(dtime)
|
|||
|
||||
--DisCouple
|
||||
-- FIX: Need to do this after the yaw calculation
|
||||
if data.pos_in_trainparts and data.pos_in_trainparts>1 then
|
||||
if is_in_loaded_area and data.pos_in_trainparts and data.pos_in_trainparts>1 then
|
||||
if train.velocity==0 then
|
||||
if not self.discouple or not self.discouple.object:getyaw() then
|
||||
if not self.discouple or not self.discouple.object:get_yaw() then
|
||||
atprint(self.id,"trying to spawn discouple")
|
||||
local dcpl_pos = vector.add(pos, {y=0, x=-math.sin(yaw)*self.wagon_span, z=math.cos(yaw)*self.wagon_span})
|
||||
local object=minetest.add_entity(dcpl_pos, "advtrains:discouple")
|
||||
|
@ -492,7 +487,7 @@ function wagon:on_step(dtime)
|
|||
end
|
||||
end
|
||||
else
|
||||
if self.discouple and self.discouple.object:getyaw() then
|
||||
if self.discouple and self.discouple.object:get_yaw() then
|
||||
self.discouple.object:remove()
|
||||
atprint(self.id," removing discouple")
|
||||
end
|
||||
|
@ -500,8 +495,8 @@ function wagon:on_step(dtime)
|
|||
end
|
||||
|
||||
--FIX: use index of the wagon, not of the train.
|
||||
local velocity = train.velocity
|
||||
local acceleration = (train.acceleration or 0)
|
||||
local velocity = train.velocity * advtrains.global_slowdown
|
||||
local acceleration = (train.acceleration or 0) * (advtrains.global_slowdown*advtrains.global_slowdown)
|
||||
local velocityvec = vector.multiply(vdir, velocity)
|
||||
local accelerationvec = vector.multiply(vdir, acceleration)
|
||||
|
||||
|
@ -530,7 +525,7 @@ function wagon:on_step(dtime)
|
|||
end
|
||||
end
|
||||
if not players_in then
|
||||
if advtrains.outside_range(pos) then
|
||||
if advtrains.wagon_outside_range(pos) then
|
||||
--atdebug("wagon",self.id,"unloading (too far away)")
|
||||
-- Workaround until minetest engine deletes attached sounds
|
||||
if self.sound_loop_handle then
|
||||
|
@ -548,9 +543,9 @@ function wagon:on_step(dtime)
|
|||
or self.old_yaw~=yaw
|
||||
or updatepct_timer_elapsed then--only send update packet if something changed
|
||||
|
||||
self.object:setpos(pos)
|
||||
self.object:setvelocity(velocityvec)
|
||||
self.object:setacceleration(accelerationvec)
|
||||
self.object:set_pos(pos)
|
||||
self.object:set_velocity(velocityvec)
|
||||
self.object:set_acceleration(accelerationvec)
|
||||
|
||||
if #self.seats > 0 and self.old_yaw ~= yaw then
|
||||
if not self.player_yaw then
|
||||
|
@ -583,7 +578,7 @@ function wagon:on_step(dtime)
|
|||
end
|
||||
self.object:set_rotation({x=pitch, y=yaw, z=0})
|
||||
else
|
||||
self.object:setyaw(yaw)
|
||||
self.object:set_yaw(yaw)
|
||||
end
|
||||
|
||||
if self.update_animation then
|
||||
|
@ -604,11 +599,9 @@ function wagon:on_step(dtime)
|
|||
self.old_acceleration_vector=accelerationvec
|
||||
self.old_yaw=yaw
|
||||
atprintbm("wagon step", t)
|
||||
end)
|
||||
end
|
||||
|
||||
function wagon:on_rightclick(clicker)
|
||||
return advtrains.pcall(function()
|
||||
if not self:ensure_init() then return end
|
||||
if not clicker or not clicker:is_player() then
|
||||
return
|
||||
|
@ -696,7 +689,6 @@ function wagon:on_rightclick(clicker)
|
|||
self:show_get_on_form(pname)
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function wagon:get_on(clicker, seatno)
|
||||
|
@ -779,7 +771,7 @@ function wagon:get_off(seatno)
|
|||
|
||||
--atdebug("platpos:", platpos, "offpos:", offpos)
|
||||
if minetest.get_item_group(minetest.get_node(platpos).name, "platform")>0 then
|
||||
minetest.after(GETOFF_TP_DELAY, function() clicker:setpos(offpos) end)
|
||||
minetest.after(GETOFF_TP_DELAY, function() clicker:set_pos(offpos) end)
|
||||
--atdebug("tp",offpos)
|
||||
return
|
||||
end
|
||||
|
@ -799,7 +791,7 @@ function wagon:get_off(seatno)
|
|||
offp=vector.add({x=isx and r*2 or 0, y=1, z=not isx and r*2 or 0}, objpos)
|
||||
--atdebug("platpos:", p, "offpos:", offp)
|
||||
if minetest.get_item_group(minetest.get_node(p).name, "platform")>0 then
|
||||
minetest.after(GETOFF_TP_DELAY, function() clicker:setpos(offp) end)
|
||||
minetest.after(GETOFF_TP_DELAY, function() clicker:set_pos(offp) end)
|
||||
--atdebug("tp",offp)
|
||||
return
|
||||
end
|
||||
|
@ -996,10 +988,10 @@ function wagon:show_bordcom(pname)
|
|||
|
||||
-- Interlocking functionality: If the interlocking module is loaded, you can set the signal aspect
|
||||
-- from inside the train
|
||||
if advtrains.interlocking and train.lzb and #train.lzb.oncoming > 0 then
|
||||
if advtrains.interlocking and train.lzb and #train.lzb.checkpoints > 0 then
|
||||
local i=1
|
||||
while train.lzb.oncoming[i] do
|
||||
local oci = train.lzb.oncoming[i]
|
||||
while train.lzb.checkpoints[i] do
|
||||
local oci = train.lzb.checkpoints[i]
|
||||
if oci.udata and oci.udata.signal_pos then
|
||||
if advtrains.interlocking.db.get_sigd_for_signal(oci.udata.signal_pos) then
|
||||
form = form .. "button[4.5,8;5,1;ilrs;Remote Routesetting]"
|
||||
|
@ -1008,6 +1000,9 @@ function wagon:show_bordcom(pname)
|
|||
end
|
||||
i=i+1
|
||||
end
|
||||
if train.ars_disable then
|
||||
form = form .. "button[4.5,7;5,1;ilarsenable;Clear 'Disable ARS' flag]"
|
||||
end
|
||||
end
|
||||
|
||||
minetest.show_formspec(pname, "advtrains_bordcom_"..self.id, form)
|
||||
|
@ -1080,18 +1075,23 @@ function wagon:handle_bordcom_fields(pname, formname, fields)
|
|||
|
||||
-- Interlocking functionality: If the interlocking module is loaded, you can set the signal aspect
|
||||
-- from inside the train
|
||||
if fields.ilrs and advtrains.interlocking and train.lzb and #train.lzb.oncoming > 0 then
|
||||
local i=1
|
||||
while train.lzb.oncoming[i] do
|
||||
local oci = train.lzb.oncoming[i]
|
||||
if oci.udata and oci.udata.signal_pos then
|
||||
local sigd = advtrains.interlocking.db.get_sigd_for_signal(oci.udata.signal_pos)
|
||||
if sigd then
|
||||
advtrains.interlocking.show_signalling_form(sigd, pname)
|
||||
return
|
||||
if advtrains.interlocking then
|
||||
if fields.ilrs and train.lzb and #train.lzb.checkpoints > 0 then
|
||||
local i=1
|
||||
while train.lzb.checkpoints[i] do
|
||||
local oci = train.lzb.checkpoints[i]
|
||||
if oci.udata and oci.udata.signal_pos then
|
||||
local sigd = advtrains.interlocking.db.get_sigd_for_signal(oci.udata.signal_pos)
|
||||
if sigd then
|
||||
advtrains.interlocking.show_signalling_form(sigd, pname)
|
||||
return
|
||||
end
|
||||
end
|
||||
i=i+1
|
||||
end
|
||||
i=i+1
|
||||
end
|
||||
if fields.ilarsenable then
|
||||
advtrains.interlocking.ars_set_disable(train, false)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -1102,7 +1102,6 @@ function wagon:handle_bordcom_fields(pname, formname, fields)
|
|||
end
|
||||
|
||||
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
return advtrains.pcall(function()
|
||||
local uid=string.match(formname, "^advtrains_geton_(.+)$")
|
||||
if uid then
|
||||
for _,wagon in pairs(minetest.luaentities) do
|
||||
|
@ -1186,7 +1185,6 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
|
|||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
end)
|
||||
function wagon:seating_from_key_helper(pname, fields, no)
|
||||
local data = advtrains.wagons[self.id]
|
||||
|
@ -1390,7 +1388,6 @@ function advtrains.register_wagon(sysname_p, prototype, desc, inv_img, nincreati
|
|||
groups = { not_in_creative_inventory = nincreative and 1 or 0},
|
||||
|
||||
on_place = function(itemstack, placer, pointed_thing)
|
||||
return advtrains.pcall(function()
|
||||
if not pointed_thing.type == "node" then
|
||||
return
|
||||
end
|
||||
|
@ -1428,8 +1425,6 @@ function advtrains.register_wagon(sysname_p, prototype, desc, inv_img, nincreati
|
|||
itemstack:take_item()
|
||||
end
|
||||
return itemstack
|
||||
|
||||
end)
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
|
|
@ -19,38 +19,50 @@ local function get_over_function(speed, shunt)
|
|||
if speed == 0 and minetest.settings:get_bool("at_il_force_lzb_halt") then
|
||||
atwarn(id,"overrun LZB 0 restriction (red signal) ",pos)
|
||||
-- Set train 1 index backward. Hope this does not lead to bugs...
|
||||
train.index = index - 0.5
|
||||
train.velocity = 0
|
||||
train.ctrl.lzb = 0
|
||||
minetest.after(0, advtrains.invalidate_path, id)
|
||||
--train.index = index - 0.5
|
||||
train.speed_restriction = 0
|
||||
|
||||
--TODO temporary
|
||||
--advtrains.drb_dump(id)
|
||||
--error("Debug: "..id.." triggered LZB-0")
|
||||
else
|
||||
train.speed_restriction = speed
|
||||
train.is_shunt = shunt
|
||||
end
|
||||
--atdebug("train drove over IP: speed=",speed,"shunt=",shunt)
|
||||
end
|
||||
end
|
||||
|
||||
advtrains.tnc_register_on_approach(function(pos, id, train, index, lzbdata)
|
||||
advtrains.tnc_register_on_approach(function(pos, id, train, index, has_entered, lzbdata)
|
||||
|
||||
--atdebug(id,"IL ApprC",pos,index,lzbdata)
|
||||
--train.debug = advtrains.print_concat_table({train.is_shunt,"|",index,"|",lzbdata})
|
||||
|
||||
local pts = advtrains.roundfloorpts(pos)
|
||||
local cn = train.path_cn[index]
|
||||
local travsht = lzbdata.travsht
|
||||
local travsht = lzbdata.il_shunt
|
||||
|
||||
local travspd = lzbdata.il_speed
|
||||
|
||||
if travsht==nil then
|
||||
travsht = train.is_shunt
|
||||
-- lzbdata has reset
|
||||
travspd = train.speed_restriction
|
||||
travsht = train.is_shunt or false
|
||||
end
|
||||
|
||||
local travspd = lzbdata.travspd
|
||||
local travwspd = lzbdata.travwspd
|
||||
|
||||
|
||||
-- check for signal
|
||||
local asp, spos = il.db.get_ip_signal_asp(pts, cn)
|
||||
|
||||
-- do ARS if needed
|
||||
if spos then
|
||||
local ars_enabled = not train.ars_disable
|
||||
-- Note on ars_disable:
|
||||
-- Theoretically, the ars_disable flag would need to behave like the speed restriction field: it should be
|
||||
-- stored in lzbdata and updated once the train drives over. However, for the sake of simplicity, it is simply
|
||||
-- a value in the train. In this case, this is sufficient because once a train triggers ARS for the first time,
|
||||
-- resetting the path does not matter to the set route and ARS doesn't need to be called again.
|
||||
if spos and ars_enabled then
|
||||
--atdebug(id,"IL Spos (ARS)",spos,asp)
|
||||
local sigd = il.db.get_sigd_for_signal(spos)
|
||||
if sigd then
|
||||
|
@ -60,22 +72,22 @@ advtrains.tnc_register_on_approach(function(pos, id, train, index, lzbdata)
|
|||
--atdebug("trav: ",pos, cn, asp, spos, "travsht=", lzb.travsht)
|
||||
local lspd
|
||||
if asp then
|
||||
--atdebug(id,"IL Signal",spos,asp)
|
||||
--atdebug(id,"IL Signal",spos, asp, lzbdata, "trainstate", train.speed_restriction, train.is_shunt)
|
||||
local nspd = 0
|
||||
--interpreting aspect and determining speed to proceed
|
||||
if travsht then
|
||||
--shunt move
|
||||
if asp.shunt.free then
|
||||
if asp.shunt then
|
||||
nspd = SHUNT_SPEED_MAX
|
||||
elseif asp.shunt.proceed_as_main and asp.main.free then
|
||||
nspd = asp.main.speed
|
||||
elseif asp.proceed_as_main and asp.main ~= 0 then
|
||||
nspd = asp.main
|
||||
travsht = false
|
||||
end
|
||||
else
|
||||
--train move
|
||||
if asp.main.free then
|
||||
nspd = asp.main.speed
|
||||
elseif asp.shunt.free then
|
||||
if asp.main ~= 0 then
|
||||
nspd = asp.main
|
||||
elseif asp.shunt then
|
||||
nspd = SHUNT_SPEED_MAX
|
||||
travsht = true
|
||||
end
|
||||
|
@ -89,25 +101,26 @@ advtrains.tnc_register_on_approach(function(pos, id, train, index, lzbdata)
|
|||
end
|
||||
end
|
||||
|
||||
local nwspd = asp.info.w_speed
|
||||
if nwspd then
|
||||
if nwspd == -1 then
|
||||
travwspd = nil
|
||||
else
|
||||
travwspd = nwspd
|
||||
end
|
||||
end
|
||||
--atdebug("ns,wns,ts,wts", nspd, nwspd, travspd, travwspd)
|
||||
--atdebug("ns,ts", nspd, travspd)
|
||||
|
||||
lspd = travspd
|
||||
if travwspd and (not lspd or lspd>travwspd) then
|
||||
lspd = travwspd
|
||||
end
|
||||
|
||||
local udata = {signal_pos = spos}
|
||||
local callback = get_over_function(lspd, travsht)
|
||||
advtrains.lzb_add_checkpoint(train, index, lspd, callback, udata)
|
||||
lzbdata.il_shunt = travsht
|
||||
lzbdata.il_speed = travspd
|
||||
--atdebug("new lzbdata",lzbdata)
|
||||
advtrains.lzb_add_checkpoint(train, index, lspd, callback, lzbdata, udata)
|
||||
end
|
||||
lzbdata.travsht = travsht
|
||||
lzbdata.travspd = travspd
|
||||
lzbdata.travwspd = travwspd
|
||||
end)
|
||||
|
||||
-- Set the ars_disable flag to the value passed
|
||||
-- Triggers a path invalidation if set to false
|
||||
function advtrains.interlocking.ars_set_disable(train, value)
|
||||
if value then
|
||||
train.ars_disable = true
|
||||
else
|
||||
train.ars_disable = nil
|
||||
minetest.after(0, advtrains.path_invalidate, train)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -131,6 +131,37 @@ function ildb.load(data)
|
|||
if data.npr_rails then
|
||||
advtrains.interlocking.npr_rails = data.npr_rails
|
||||
end
|
||||
|
||||
--COMPATIBILITY to Signal aspect format
|
||||
-- TODO remove in time...
|
||||
for pts,tcb in pairs(track_circuit_breaks) do
|
||||
for connid, tcbs in ipairs(tcb) do
|
||||
if tcbs.routes then
|
||||
for _,route in ipairs(tcbs.routes) do
|
||||
if route.aspect then
|
||||
-- transform the signal aspect format
|
||||
local asp = route.aspect
|
||||
if type(asp.main) == "table" then
|
||||
atwarn("Transforming route aspect of signal",pts,"/",connid,"")
|
||||
if asp.main.free then
|
||||
asp.main = asp.main.speed
|
||||
else
|
||||
asp.main = 0
|
||||
end
|
||||
if asp.dst.free then
|
||||
asp.dst = asp.dst.speed
|
||||
else
|
||||
asp.dst = 0
|
||||
end
|
||||
asp.proceed_as_main = asp.shunt.proceed_as_main
|
||||
asp.shunt = asp.shunt.free
|
||||
-- Note: info table not transferred, it's not used right now
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function ildb.save()
|
||||
|
@ -149,6 +180,7 @@ end
|
|||
--[[
|
||||
TCB data structure
|
||||
{
|
||||
-- This is the "A" side of the TCB
|
||||
[1] = { -- Variant: with adjacent TCs.
|
||||
ts_id = <id> -- ID of the assigned track section
|
||||
signal = <pos> -- optional: when set, routes can be set from this tcb/direction and signal
|
||||
|
@ -164,6 +196,7 @@ TCB data structure
|
|||
routes = { <route definition> } -- a collection of routes from this signal
|
||||
route_auto = <boolean> -- When set, we will automatically re-set the route (designated by routeset)
|
||||
},
|
||||
-- This is the "B" side of the TCB
|
||||
[2] = { -- Variant: end of track-circuited area (initial state of TC)
|
||||
ts_id = nil, -- this is the indication for end_of_interlocking
|
||||
section_free = <boolean>, --this can be set by an exit node via mesecons or atlatc,
|
||||
|
|
|
@ -6,10 +6,10 @@
|
|||
|
||||
|
||||
local setaspect = function(pos, node, asp)
|
||||
if not asp.main.free then
|
||||
if asp.main == 0 then
|
||||
advtrains.ndb.swap_node(pos, {name="advtrains_interlocking:ds_danger"})
|
||||
else
|
||||
if asp.dst.free and asp.main.speed == -1 then
|
||||
if asp.dst ~= 0 and asp.main == -1 then
|
||||
advtrains.ndb.swap_node(pos, {name="advtrains_interlocking:ds_free"})
|
||||
else
|
||||
advtrains.ndb.swap_node(pos, {name="advtrains_interlocking:ds_slow"})
|
||||
|
@ -22,18 +22,10 @@ local setaspect = function(pos, node, asp)
|
|||
end
|
||||
|
||||
local suppasp = {
|
||||
main = {
|
||||
free = nil,
|
||||
speed = {6, -1},
|
||||
},
|
||||
dst = {
|
||||
free = nil,
|
||||
speed = nil,
|
||||
},
|
||||
shunt = {
|
||||
free = false,
|
||||
proceed_as_main = true,
|
||||
},
|
||||
main = {0, 6, -1},
|
||||
dst = {0, false},
|
||||
shunt = false,
|
||||
proceed_as_main = true,
|
||||
info = {
|
||||
call_on = false,
|
||||
dead_end = false,
|
||||
|
@ -74,10 +66,7 @@ minetest.register_node("advtrains_interlocking:ds_free", {
|
|||
supported_aspects = suppasp,
|
||||
get_aspect = function(pos, node)
|
||||
return {
|
||||
main = {
|
||||
free = true,
|
||||
speed = -1,
|
||||
}
|
||||
main = -1,
|
||||
}
|
||||
end,
|
||||
},
|
||||
|
@ -98,10 +87,7 @@ minetest.register_node("advtrains_interlocking:ds_slow", {
|
|||
supported_aspects = suppasp,
|
||||
get_aspect = function(pos, node)
|
||||
return {
|
||||
main = {
|
||||
free = true,
|
||||
speed = 6,
|
||||
}
|
||||
main = 6,
|
||||
}
|
||||
end,
|
||||
},
|
||||
|
|
|
@ -112,7 +112,8 @@ route = {
|
|||
next = <sigd>, -- of the next (note: next) TCB on the route
|
||||
locks = {<pts> = "state"} -- route locks of this route segment
|
||||
}
|
||||
terminal =
|
||||
terminal = <sigd>,
|
||||
aspect = <signal aspect>,--note, might change in future
|
||||
}
|
||||
The first item in the TCB path (namely i=0) is always the start signal of this route,
|
||||
so this is left out.
|
||||
|
|
|
@ -129,7 +129,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
|
|||
advtrains.interlocking.show_route_edit_form(pname, sigd, routeid)
|
||||
end
|
||||
|
||||
advtrains.interlocking.show_signal_aspect_selector(pname, suppasp, route.name, callback, route.aspect)
|
||||
advtrains.interlocking.show_signal_aspect_selector(pname, suppasp, route.name, callback, route.aspect or advtrains.interlocking.GENERIC_FREE)
|
||||
return
|
||||
end
|
||||
if fields.delete then
|
||||
|
|
|
@ -6,21 +6,6 @@ local function sigd_to_string(sigd)
|
|||
return minetest.pos_to_string(sigd.p).." / "..lntrans[sigd.s]
|
||||
end
|
||||
|
||||
local asp_generic_free = {
|
||||
main = {
|
||||
free = true,
|
||||
speed = -1,
|
||||
},
|
||||
shunt = {
|
||||
free = false,
|
||||
},
|
||||
dst = {
|
||||
free = true,
|
||||
speed = -1,
|
||||
},
|
||||
info = {}
|
||||
}
|
||||
|
||||
local ildb = advtrains.interlocking.db
|
||||
local ilrs = {}
|
||||
|
||||
|
@ -127,7 +112,7 @@ function ilrs.set_route(signal, route, try)
|
|||
}
|
||||
if c_tcbs.signal then
|
||||
c_tcbs.route_committed = true
|
||||
c_tcbs.aspect = route.aspect or asp_generic_free
|
||||
c_tcbs.aspect = route.aspect or advtrains.interlocking.GENERIC_FREE
|
||||
c_tcbs.route_origin = signal
|
||||
advtrains.interlocking.update_signal_aspect(c_tcbs)
|
||||
end
|
||||
|
|
|
@ -3,42 +3,46 @@
|
|||
|
||||
--[[
|
||||
Signal aspect table:
|
||||
Note: All speeds are measured in m/s, aka the number of + signs in the HUD.
|
||||
asp = {
|
||||
main = {
|
||||
free = <boolean>,
|
||||
speed = <int km/h>,
|
||||
},
|
||||
shunt = {
|
||||
free = <boolean>,
|
||||
main = <int speed>,
|
||||
-- Main signal aspect, tells state and permitted speed of next section
|
||||
-- 0 = section is blocked
|
||||
-- >0 = section is free, speed limit is this value
|
||||
-- -1 = section is free, maximum speed permitted
|
||||
-- false/nil = Signal doesn't provide main signal information, retain current speed limit.
|
||||
shunt = <boolean>,
|
||||
-- Whether train may proceed as shunt move, on sight
|
||||
-- main aspect takes precedence over this
|
||||
proceed_as_main = <boolean>,
|
||||
-- If an approaching train is a shunt move and "main.free" is set,
|
||||
-- When main==0, train switches to shunt move and is restricted to speed 6
|
||||
proceed_as_main = <boolean>,
|
||||
-- If an approaching train is a shunt move and 'shunt' is false,
|
||||
-- the train may proceed as a train move under the "main" aspect
|
||||
-- if the main aspect permits it (i.e. main!=0)
|
||||
-- If this is not set, shunt moves are NOT allowed to switch to
|
||||
-- a train move, and must stop even if "main.free" is set.
|
||||
-- a train move, and must stop even if "main" would permit passing.
|
||||
-- This is intended to be used for "Halt for shunt moves" signs.
|
||||
}
|
||||
dst = {
|
||||
free = <boolean>,
|
||||
speed = <int km/h>,
|
||||
}
|
||||
info = {
|
||||
call_on = <boolean>, -- Call-on route, expect train in track ahead (not implemented yet)
|
||||
dead_end = <boolean>, -- Route ends on a dead end (e.g. bumper) (not implemented yet)
|
||||
w_speed = <integer>,
|
||||
-- "Warning speed restriction". Supposed for short-term speed
|
||||
-- restrictions which always override any other restrictions
|
||||
-- imposed by "speed" fields, until lifted by a value of -1
|
||||
-- (Example: german Langsamfahrstellen-Signale)
|
||||
|
||||
dst = <int speed>,
|
||||
-- Distant signal aspect, tells state and permitted speed of the section after next section
|
||||
-- The character of these information is purely informational
|
||||
-- At this time, this field is not actively used
|
||||
-- 0 = section is blocked
|
||||
-- >0 = section is free, speed limit is this value
|
||||
-- -1 = section is free, maximum speed permitted
|
||||
-- false/nil = Signal doesn't provide distant signal information.
|
||||
|
||||
-- the character of call_on and dead_end is purely informative
|
||||
call_on = <boolean>, -- Call-on route, expect train in track ahead (not implemented yet)
|
||||
dead_end = <boolean>, -- Route ends on a dead end (e.g. bumper) (not implemented yet)
|
||||
|
||||
w_speed = <integer>,
|
||||
-- "Warning speed restriction". Supposed for short-term speed
|
||||
-- restrictions which always override any other restrictions
|
||||
-- imposed by "speed" fields, until lifted by a value of -1
|
||||
-- (Example: german Langsamfahrstellen-Signale)
|
||||
}
|
||||
}
|
||||
-- For "speed" and "w_speed" fields, a value of -1 means that the
|
||||
-- restriction is lifted. If they are omitted, the value imposed at
|
||||
-- the last aspect received remains valid.
|
||||
-- The "dst" subtable can be completely omitted when no explicit dst
|
||||
-- aspect should be signalled to the train. In this case, the last
|
||||
-- signalled dst aspect remains valid.
|
||||
|
||||
== How signals actually work in here ==
|
||||
Each signal (in the advtrains universe) is some node that has at least the
|
||||
|
@ -60,10 +64,16 @@ advtrains = {
|
|||
-- This function gets called whenever the signal should display
|
||||
-- a new or changed signal aspect. It is not required that
|
||||
-- the signal actually displays the exact same aspect, since
|
||||
-- some signals can not do this by design.
|
||||
-- Example: pure shunt signals can not display a "main" aspect
|
||||
-- and have no effect on train moves, so they will only ever
|
||||
-- honor the shunt.free field for their aspect.
|
||||
-- some signals can not do this by design. However, it must
|
||||
-- display an aspect that is at least as restrictive as the passed
|
||||
-- aspect as far as it is capable of doing so.
|
||||
-- Examples:
|
||||
-- - pure shunt signals can not display a "main" aspect
|
||||
-- and have no effect on train moves, so they will only ever
|
||||
-- honor the shunt.free field for their aspect.
|
||||
-- - the german Hl system can only signal speeds of 40, 60
|
||||
-- and 100 km/h, a speed of 80km/h should then be signalled
|
||||
-- as 60 km/h instead.
|
||||
-- In turn, it is not guaranteed that the aspect will fulfill the
|
||||
-- criteria put down in supported_aspects.
|
||||
-- If set_aspect is present, supported_aspects should also be declared.
|
||||
|
@ -87,51 +97,52 @@ advtrains = {
|
|||
false: always shows "blocked", unchangable
|
||||
true: always shows "free", unchangable
|
||||
-- Any of the "speed" fields should contain a list of possible values
|
||||
-- to be set as restriction. If omitted, this signal should never
|
||||
-- set the corresponding "speed" field in the aspect, which means
|
||||
-- that the previous speed limit stays valid
|
||||
-- to be set as restriction. If omitted, the value of the described
|
||||
-- field is always assumed to be false (no information)
|
||||
-- A speed of 0 means that the signal can show a "blocked" aspect
|
||||
-- (which is probably the case for most signals)
|
||||
-- If the signal can signal "no information" on one of the fields
|
||||
-- (thus false is an acceptable value), include false in the list
|
||||
-- If your signal can only display a single speed (may it be -1),
|
||||
-- always enclose that single value into a list. (such as {-1})
|
||||
main = {
|
||||
free = <boolean/nil>,
|
||||
speed = {<speed1>, ..., <speedn>} or nil,
|
||||
},
|
||||
dst = {
|
||||
free = <boolean/nil>,
|
||||
speed = {<speed1>, ..., <speedn>} or nil,
|
||||
},
|
||||
shunt = {
|
||||
free = <boolean/nil>,
|
||||
},
|
||||
info = {
|
||||
call_on = <boolean/nil>,
|
||||
dead_end = <boolean/nil>,
|
||||
w_speed = {<speed1>, ..., <speedn>} or nil,
|
||||
}
|
||||
main = {<speed1>, ..., <speedn>} or nil,
|
||||
dst = {<speed1>, ..., <speedn>} or nil,
|
||||
shunt = <boolean/nil>,
|
||||
|
||||
call_on = <boolean/nil>,
|
||||
dead_end = <boolean/nil>,
|
||||
w_speed = {<speed1>, ..., <speedn>} or nil,
|
||||
|
||||
},
|
||||
Example for supported_aspects:
|
||||
supported_aspects = {
|
||||
main = {0, 6, -1}, -- can show either "Section blocked", "Proceed at speed 6" or "Proceed at maximum speed"
|
||||
dst = {0, false}, -- can show only if next signal shows "blocked", no other information.
|
||||
shunt = false, -- shunting by this signal is never allowed.
|
||||
|
||||
call_on = false,
|
||||
dead_end = false,
|
||||
w_speed = nil,
|
||||
-- none of the information can be shown by the signal
|
||||
|
||||
},
|
||||
|
||||
get_aspect = function(pos, node)
|
||||
-- This function gets called by the train safety system. It
|
||||
should return the aspect that this signal actually displays,
|
||||
not preferably the input of set_aspect.
|
||||
-- For regular, full-featured light signals, they will probably
|
||||
honor all entries in the original aspect, however, e.g.
|
||||
simple shunt signals always return main.free=true regardless of
|
||||
simple shunt signals always return main=false regardless of
|
||||
the set_aspect input because they can not signal "Halt" to
|
||||
train moves.
|
||||
-- advtrains.interlocking.DANGER contains a default "all-danger" aspect.
|
||||
-- If your signal does not cover certain sub-tables of the aspect,
|
||||
the following reasonable defaults are automatically assumed:
|
||||
main = {
|
||||
free = true,
|
||||
}
|
||||
dst = {
|
||||
free = true,
|
||||
}
|
||||
shunt = {
|
||||
free = false,
|
||||
proceed_as_main = false,
|
||||
}
|
||||
main = false (unchanged)
|
||||
dst = false (unchanged)
|
||||
shunt = false (shunting not allowed)
|
||||
info = {} (no further information)
|
||||
end,
|
||||
}
|
||||
on_rightclick = advtrains.interlocking.signal_rc_handler
|
||||
|
@ -155,51 +166,37 @@ This function will query get_aspect to retrieve the new aspect.
|
|||
]]--
|
||||
|
||||
local DANGER = {
|
||||
main = {
|
||||
free = false,
|
||||
speed = 0,
|
||||
},
|
||||
shunt = {
|
||||
free = false,
|
||||
},
|
||||
dst = {
|
||||
free = false,
|
||||
speed = 0,
|
||||
},
|
||||
info = {}
|
||||
main = 0,
|
||||
dst = false,
|
||||
shunt = false,
|
||||
}
|
||||
advtrains.interlocking.DANGER = DANGER
|
||||
|
||||
local function fillout_aspect(asp)
|
||||
if not asp.main then
|
||||
asp.main = {
|
||||
free = true,
|
||||
}
|
||||
elseif type(asp.main) ~= "table" then
|
||||
asp.main = {
|
||||
free = asp.main~=0,
|
||||
speed = asp.main,
|
||||
}
|
||||
end
|
||||
if not asp.dst then
|
||||
asp.dst = {
|
||||
free = true,
|
||||
}
|
||||
end
|
||||
if not asp.shunt then
|
||||
asp.shunt = {
|
||||
free = false,
|
||||
proceed_as_main = false,
|
||||
}
|
||||
elseif type(asp.shunt) ~= "table" then
|
||||
asp.shunt = {
|
||||
free = asp.shunt,
|
||||
proceed_as_main = asp.proceed_as_main,
|
||||
}
|
||||
end
|
||||
if not asp.info then
|
||||
asp.info = {}
|
||||
advtrains.interlocking.GENERIC_FREE = {
|
||||
main = -1,
|
||||
shunt = false,
|
||||
dst = false,
|
||||
}
|
||||
|
||||
local function convert_aspect_if_necessary(asp)
|
||||
if type(asp.main) == "table" then
|
||||
local newasp = {}
|
||||
if asp.main.free then
|
||||
newasp.main = asp.main.speed
|
||||
else
|
||||
newasp.main = 0
|
||||
end
|
||||
if asp.dst and asp.dst.free then
|
||||
newasp.dst = asp.dst.speed
|
||||
else
|
||||
newasp.dst = 0
|
||||
end
|
||||
newasp.proceed_as_main = asp.shunt.proceed_as_main
|
||||
newasp.shunt = asp.shunt.free
|
||||
-- Note: info table not transferred, it's not used right now
|
||||
return newasp
|
||||
end
|
||||
return asp
|
||||
end
|
||||
|
||||
function advtrains.interlocking.update_signal_aspect(tcbs)
|
||||
|
@ -219,7 +216,7 @@ function advtrains.interlocking.signal_after_dig(pos)
|
|||
end
|
||||
|
||||
function advtrains.interlocking.signal_set_aspect(pos, asp)
|
||||
fillout_aspect(asp)
|
||||
asp = convert_aspect_if_necessary(asp)
|
||||
local node=advtrains.ndb.get_node(pos)
|
||||
local ndef=minetest.registered_nodes[node.name]
|
||||
if ndef and ndef.advtrains and ndef.advtrains.set_aspect then
|
||||
|
@ -234,17 +231,17 @@ function advtrains.interlocking.signal_on_aspect_changed(pos)
|
|||
if not ipts then return end
|
||||
local ipos = minetest.string_to_pos(ipts)
|
||||
|
||||
local tns = advtrains.occ.get_trains_over(ipos)
|
||||
for id, sidx in pairs(tns) do
|
||||
-- local train = advtrains.trains[id]
|
||||
--if train.index <= sidx then
|
||||
minetest.after(0, advtrains.invalidate_path, id)
|
||||
--end
|
||||
end
|
||||
advtrains.invalidate_all_paths_ahead(ipos)
|
||||
end
|
||||
|
||||
function advtrains.interlocking.signal_rc_handler(pos, node, player, itemstack, pointed_thing)
|
||||
local pname = player:get_player_name()
|
||||
local control = player:get_player_control()
|
||||
if control.aux1 then
|
||||
advtrains.interlocking.show_ip_form(pos, pname)
|
||||
return
|
||||
end
|
||||
|
||||
local sigd = advtrains.interlocking.db.get_sigd_for_signal(pos)
|
||||
if sigd then
|
||||
advtrains.interlocking.show_signalling_form(sigd, pname)
|
||||
|
@ -252,7 +249,16 @@ function advtrains.interlocking.signal_rc_handler(pos, node, player, itemstack,
|
|||
local ndef = minetest.registered_nodes[node.name]
|
||||
if ndef.advtrains and ndef.advtrains.set_aspect then
|
||||
-- permit to set aspect manually
|
||||
minetest.show_formspec(pname, "at_il_sigasp_"..minetest.pos_to_string(pos), "field[aspect;Set Aspect ('A' to assign IP);D0D0D]")
|
||||
local function callback(pname, aspect)
|
||||
advtrains.interlocking.signal_set_aspect(pos, aspect)
|
||||
end
|
||||
local isasp = ndef.advtrains.get_aspect(pos, node)
|
||||
|
||||
advtrains.interlocking.show_signal_aspect_selector(
|
||||
pname,
|
||||
ndef.advtrains.supported_aspects,
|
||||
"Set aspect manually", callback,
|
||||
isasp)
|
||||
else
|
||||
--static signal - only IP
|
||||
advtrains.interlocking.show_ip_form(pos, pname)
|
||||
|
@ -260,45 +266,13 @@ function advtrains.interlocking.signal_rc_handler(pos, node, player, itemstack,
|
|||
end
|
||||
end
|
||||
|
||||
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
local pname = player:get_player_name()
|
||||
local pts = string.match(formname, "^at_il_sigasp_(.+)$")
|
||||
local pos
|
||||
if pts then pos = minetest.string_to_pos(pts) end
|
||||
if pos and fields.aspect then
|
||||
if fields.aspect == "A" then
|
||||
advtrains.interlocking.show_ip_form(pos, pname)
|
||||
return
|
||||
end
|
||||
local mfs, msps, dfs, dsps, shs = string.match(fields.aspect, "^([FD])([-0-9]+)([FD])([-0-9]+)([FD])$")
|
||||
local asp = {
|
||||
main = {
|
||||
free = mfs=="F",
|
||||
speed = tonumber(msps),
|
||||
},
|
||||
shunt = {
|
||||
free = shs=="F",
|
||||
},
|
||||
dst = {
|
||||
free = dfs=="F",
|
||||
speed = tonumber(dsps),
|
||||
},
|
||||
info = {
|
||||
call_on = false, -- Call-on route, expect train in track ahead
|
||||
dead_end = false, -- Route ends on a dead end (e.g. bumper)
|
||||
}
|
||||
}
|
||||
advtrains.interlocking.signal_set_aspect(pos, asp)
|
||||
end
|
||||
end)
|
||||
|
||||
-- Returns the aspect the signal at pos is supposed to show
|
||||
function advtrains.interlocking.signal_get_supposed_aspect(pos)
|
||||
local sigd = advtrains.interlocking.db.get_sigd_for_signal(pos)
|
||||
if sigd then
|
||||
local tcbs = advtrains.interlocking.db.get_tcbs(sigd)
|
||||
if tcbs.aspect then
|
||||
return tcbs.aspect
|
||||
return convert_aspect_if_necessary(tcbs.aspect)
|
||||
end
|
||||
end
|
||||
return DANGER;
|
||||
|
@ -312,8 +286,7 @@ function advtrains.interlocking.signal_get_aspect(pos)
|
|||
if ndef and ndef.advtrains and ndef.advtrains.get_aspect then
|
||||
local asp = ndef.advtrains.get_aspect(pos, node)
|
||||
if not asp then asp = DANGER end
|
||||
fillout_aspect(asp)
|
||||
return asp
|
||||
return convert_aspect_if_necessary(asp)
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
@ -447,42 +420,45 @@ local players_aspsel = {}
|
|||
suppasp: "supported_aspects" table
|
||||
purpose: form title string
|
||||
callback: func(pname, aspect) called on form submit
|
||||
isasp: aspect currently set
|
||||
]]
|
||||
function advtrains.interlocking.show_signal_aspect_selector(pname, p_suppasp, p_purpose, callback, p_isasp)
|
||||
function advtrains.interlocking.show_signal_aspect_selector(pname, p_suppasp, p_purpose, callback, isasp)
|
||||
local suppasp = p_suppasp or {
|
||||
main = {}, dst = {}, shunt = {}, info = {},
|
||||
main = {0, -1}, dst = {false}, shunt = false, info = {},
|
||||
}
|
||||
local purpose = p_purpose or ""
|
||||
local isasp = p_isasp and fillout_aspect(p_isasp)
|
||||
|
||||
local form = "size[7,5]label[0.5,0.5;Select Signal Aspect:]"
|
||||
form = form.."label[0.5,1;"..purpose.."]"
|
||||
|
||||
form = form.."label[0.5,1.5;== Main Signal ==]"
|
||||
if suppasp.main.free == nil then
|
||||
local st = 2
|
||||
if isasp and not isasp.main.free then st=1 end
|
||||
form = form.."dropdown[0.5,2;2;main_free;danger,free;"..st.."]"
|
||||
end
|
||||
if suppasp.main.speed then
|
||||
local selid = 1
|
||||
if isasp and isasp.main.speed then
|
||||
for idx, spv in ipairs(suppasp.main.speed) do
|
||||
if spv == isasp.main.speed then
|
||||
selid = idx
|
||||
break
|
||||
end
|
||||
end
|
||||
local selid = 1
|
||||
local entries = {}
|
||||
for idx, spv in ipairs(suppasp.main) do
|
||||
local entry
|
||||
if spv == 0 then
|
||||
entry = "Halt"
|
||||
elseif spv == -1 then
|
||||
entry = "Continue at maximum speed"
|
||||
elseif not spv then
|
||||
entry = "Continue\\, speed limit unchanged (no info)"
|
||||
else
|
||||
entry = "Continue at speed of "..spv
|
||||
end
|
||||
-- hack: the crappy formspec system returns the label, not the index. save the index in it.
|
||||
entries[idx] = idx.."| "..entry
|
||||
if isasp and spv == (isasp.main or false) then
|
||||
selid = idx
|
||||
end
|
||||
form = form.."label[2.3,1;Speed:]"
|
||||
form = form.."dropdown[3,2;2;main_speed;"..table.concat(suppasp.main.speed, ",")..";"..selid.."]"
|
||||
end
|
||||
form = form.."dropdown[0.5,2;6;main;"..table.concat(entries, ",")..";"..selid.."]"
|
||||
|
||||
|
||||
form = form.."label[0.5,3;== Shunting ==]"
|
||||
if suppasp.shunt.free == nil then
|
||||
if suppasp.shunt == nil then
|
||||
local st = 1
|
||||
if isasp and isasp.shunt.free then st=2 end
|
||||
form = form.."dropdown[0.5,3.5;2;shunt_free;---,allowed;"..st.."]"
|
||||
if isasp and isasp.shunt then st=2 end
|
||||
form = form.."dropdown[0.5,3.5;6;shunt_free;---,allowed;"..st.."]"
|
||||
end
|
||||
|
||||
form = form.."button_exit[0.5,4.5; 5,1;save;OK]"
|
||||
|
@ -507,12 +483,10 @@ local function usebool(sup, val, free)
|
|||
return sup
|
||||
end
|
||||
end
|
||||
local function usespeed(sup, val)
|
||||
if sup then
|
||||
return tonumber(val)
|
||||
else
|
||||
return nil
|
||||
end
|
||||
|
||||
-- other side of hack: extract the index
|
||||
local function ddindex(val)
|
||||
return tonumber(string.match(val, "^(%d+)|"))
|
||||
end
|
||||
|
||||
-- TODO use non-hacky way to parse outputs
|
||||
|
@ -523,17 +497,12 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
|
|||
if psl then
|
||||
if formname == "at_il_sigaspdia_"..psl.token then
|
||||
if fields.save then
|
||||
local maini = ddindex(fields.main)
|
||||
if not maini then return end
|
||||
local asp = {
|
||||
main = {
|
||||
free = usebool(psl.suppasp.main.free, fields.main_free, "free"),
|
||||
speed = usespeed(psl.suppasp.main.speed, fields.main_speed),
|
||||
},
|
||||
dst = {
|
||||
free = true, speed = -1,
|
||||
},
|
||||
shunt = {
|
||||
free = usebool(psl.suppasp.shunt.free, fields.shunt_free, "allowed"),
|
||||
},
|
||||
main = psl.suppasp.main[maini],
|
||||
dst = false,
|
||||
shunt = usebool(psl.suppasp.shunt, fields.shunt_free, "allowed"),
|
||||
info = {}
|
||||
}
|
||||
psl.callback(pname, asp)
|
||||
|
|
|
@ -118,6 +118,16 @@ minetest.register_node("advtrains_interlocking:tcb_node", {
|
|||
end,
|
||||
})
|
||||
|
||||
|
||||
-- Crafting
|
||||
minetest.register_craft({
|
||||
output = 'advtrains_interlocking:tcb_node 4',
|
||||
recipe = {
|
||||
{'mesecons:wire_00000000_off', 'basic_materials:ic', 'mesecons:wire_00000000_off'},
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
minetest.register_on_punchnode(function(pos, node, player, pointed_thing)
|
||||
local pname = player:get_player_name()
|
||||
if not minetest.check_player_privs(pname, "interlocking") then
|
||||
|
@ -558,7 +568,7 @@ local sig_pselidx = {}
|
|||
-- Players having a signalling form open
|
||||
local p_open_sig_form = {}
|
||||
|
||||
function advtrains.interlocking.show_signalling_form(sigd, pname, sel_rte)
|
||||
function advtrains.interlocking.show_signalling_form(sigd, pname, sel_rte, called_from_form_update)
|
||||
if not minetest.check_player_privs(pname, "train_operator") then
|
||||
minetest.chat_send_player(pname, "Insufficient privileges to use this!")
|
||||
return
|
||||
|
@ -651,7 +661,10 @@ function advtrains.interlocking.show_signalling_form(sigd, pname, sel_rte)
|
|||
p_open_sig_form[pname] = sigd
|
||||
|
||||
-- always a good idea to update the signal aspect
|
||||
advtrains.interlocking.update_signal_aspect(tcbs)
|
||||
if not called_from_form_update then
|
||||
-- FIX prevent a callback loop
|
||||
advtrains.interlocking.update_signal_aspect(tcbs)
|
||||
end
|
||||
end
|
||||
|
||||
function advtrains.interlocking.update_player_forms(sigd)
|
||||
|
@ -763,7 +776,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
|
|||
tcbs.route_auto = false
|
||||
end
|
||||
|
||||
advtrains.interlocking.show_signalling_form(sigd, pname, sel_rte)
|
||||
advtrains.interlocking.show_signalling_form(sigd, pname, sel_rte, true)
|
||||
return
|
||||
end
|
||||
|
||||
|
|
|
@ -23,6 +23,16 @@ local adefunc = function(def, preset, suffix, rotation)
|
|||
advtrains.interlocking.npr_rails[pe] = nil
|
||||
end,
|
||||
on_receive_fields = function(pos, formname, fields, player)
|
||||
local pname = player:get_player_name()
|
||||
if not minetest.check_player_privs(pname, {interlocking=true}) then
|
||||
minetest.chat_send_player(pname, "Interlocking privilege required!")
|
||||
return
|
||||
end
|
||||
if minetest.is_protected(pos, pname) then
|
||||
minetest.chat_send_player(pname, "This rail is protected!")
|
||||
minetest.record_protection_violation(pos, pname)
|
||||
return
|
||||
end
|
||||
if fields.npr then
|
||||
local pe = advtrains.encode_pos(pos)
|
||||
advtrains.interlocking.npr_rails[pe] = tonumber(fields.npr)
|
||||
|
|
|
@ -169,7 +169,8 @@ local adefunc = function(def, preset, suffix, rotation)
|
|||
show_stoprailform(pos, player)
|
||||
end,
|
||||
advtrains = {
|
||||
on_train_approach = function(pos,train_id, train, index)
|
||||
on_train_approach = function(pos,train_id, train, index, has_entered)
|
||||
if has_entered then return end -- do not stop again!
|
||||
if train.path_cn[index] == 1 then
|
||||
local pe = advtrains.encode_pos(pos)
|
||||
local stdata = advtrains.lines.stops[pe]
|
||||
|
@ -184,6 +185,7 @@ local adefunc = function(def, preset, suffix, rotation)
|
|||
local stn = advtrains.lines.stations[stdata.stn]
|
||||
local stnname = stn and stn.name or "Unknown Station"
|
||||
train.text_inside = "Next Stop:\n"..stnname
|
||||
advtrains.interlocking.ars_set_disable(train, true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -201,7 +203,7 @@ local adefunc = function(def, preset, suffix, rotation)
|
|||
local stnname = stn and stn.name or "Unknown Station"
|
||||
|
||||
-- Send ATC command and set text
|
||||
advtrains.atc.train_set_command(train, "B0 W O"..stdata.doors..(stdata.kick and "K" or "").." D"..stdata.wait.." OC "..(stdata.reverse and "R" or "").."D"..(stdata.ddelay or 1) .. "S" ..(stdata.speed or "M"), true)
|
||||
advtrains.atc.train_set_command(train, "B0 W O"..stdata.doors..(stdata.kick and "K" or "").." D"..stdata.wait.." OC "..(stdata.reverse and "R" or "").."D"..(stdata.ddelay or 1) .. " A1 S" ..(stdata.speed or "M"), true)
|
||||
train.text_inside = stnname
|
||||
if tonumber(stdata.wait) then
|
||||
minetest.after(tonumber(stdata.wait), function() train.text_inside = "" end)
|
||||
|
|
440
mods/advtrains/advtrains_luaautomation/README.md
Normal file
|
@ -0,0 +1,440 @@
|
|||
|
||||
# Advtrains - Lua Automation features
|
||||
|
||||
This mod offers components that run LUA code and interface with each other through a global environment. It makes complex automated railway systems possible. The mod is sometimes abbreviated as 'LuaATC' or 'atlatc'. This stands for AdvTrainsLuaATC. This short name has been chosen for user convenience, since the name of this mod ('advtrains_luaautomation') is very long.
|
||||
|
||||
A probably more complete documentation of LuaATC is found on the [Advtrains Wiki](http://advtrains.de/wiki/doku.php?id=usage:atlatc:start)
|
||||
|
||||
## Privileges
|
||||
To perform any operations using this mod (except executing operation panels), players need the "atlatc" privilege.
|
||||
This privilege should never be granted to anyone except trusted administrators. Even though the LUA environment is sandboxed, it is still possible to DoS the server by coding infinite loops or requesting expotentially growing interrupts.
|
||||
|
||||
## Environments
|
||||
|
||||
Each active component is assigned to an environment where all atlac data is held. Components in different environments can't inferface with each other.
|
||||
This system allows multiple independent automation systems to run simultaneously without polluting each other's environment.
|
||||
|
||||
- `/env_create <env_name>`:
|
||||
Create environment with the given name. To be able to do anything, you first need to create an environment. Choose the name wisely, you can't change it afterwards without deleting the environment and starting again.
|
||||
|
||||
- `/env_setup <env_name>`:
|
||||
Invoke the form to edit the environment's initialization code. For more information, see the section on active components. You can also delete an environment from here.
|
||||
|
||||
- `/env_subscribe <env_name>`, `/env_unsubscribe <env_name>`:
|
||||
Subscribe or unsubscribe from log/error messages originating from this environment
|
||||
|
||||
- `/env_subscriptions [env_name]`:
|
||||
List your subscriptions or players subscribed to an environment.
|
||||
|
||||
|
||||
## Functions and variables
|
||||
### General Functions and Variables
|
||||
The following standard Lua libraries are available:
|
||||
- `string`
|
||||
- `math`
|
||||
- `table`
|
||||
- `os`
|
||||
|
||||
The following standard Lua functions are available:
|
||||
- `assert`
|
||||
- `error`
|
||||
- `ipairs`
|
||||
- `pairs`
|
||||
- `next`
|
||||
- `select`
|
||||
- `tonumber`
|
||||
- `tostring`
|
||||
- `type`
|
||||
- `unpack`
|
||||
|
||||
Any attempt to overwrite the predefined values results in an error.
|
||||
|
||||
### LuaAutomation Global Variables
|
||||
- `S`
|
||||
The variable 'S' contains a table which is shared between all components of the environment. Its contents are persistent over server restarts. May not contain functions, every other value is allowed.
|
||||
|
||||
- `F`
|
||||
The variable 'F' also contains a table which is shared between all components of the environment. Its contents are discarded on server shutdown or when the init code gets re-run. Every data type is allowed, even functions.
|
||||
The purpose of this table is not to save data, but to provide static value and function definitions. The table should be populated by the init code.
|
||||
|
||||
### LuaAutomation Global Functions
|
||||
> Note: in the following functions, all parameters named `pos` designate a position. You can use the following:
|
||||
> - a default Minetest position vector (eg. {x=34, y=2, z=-18})
|
||||
> - the POS(34,2,-18) shorthand below.
|
||||
> - A string, the passive component name. See 'passive component naming'.
|
||||
|
||||
|
||||
|
||||
- `POS(x,y,z)`
|
||||
Shorthand function to create a position vector {x=?, y=?, z=?} with less characters.
|
||||
|
||||
- `getstate(pos)`
|
||||
Get the state of the passive component at position `pos`.
|
||||
|
||||
- `setstate(pos, newstate)`
|
||||
Set the state of the passive component at position `pos`.
|
||||
|
||||
- `is_passive(pos)`
|
||||
Checks whether there is a passive component at the position pos (and/or whether a passive component with this name exists)
|
||||
|
||||
- `interrupt(time, message)`
|
||||
Cause LuaAutomation to trigger an `int` event on this component after the given time in seconds with the specified `message` field. `message` can be of any Lua data type. Returns true. *Not available in init code.*
|
||||
|
||||
- `interrupt_safe(time, message)`
|
||||
Like `interrupt()`, but does not add an interrupt and returns false when an interrupt (of any type) is already present for this component. Returns true when interrupt was successfully added.
|
||||
|
||||
- `interrupt_pos(pos, message)`
|
||||
Immediately trigger an `ext_int` event on the active component at position pos. `message` is like in interrupt(). Use with care, or better **_don't use_**! Incorrect use can result in **_expotential growth of interrupts_**.
|
||||
|
||||
- `clear_interrupts()`
|
||||
Removes any pending interrupts of this node.
|
||||
|
||||
- `digiline_send(channel, message)`
|
||||
Make this active component send a digiline message on the specified channel.
|
||||
Not available in init code.
|
||||
|
||||
#### Interlocking Route Management Functions
|
||||
If `advtrains_interlocking` is enabled, the following aditional functions can be used:
|
||||
|
||||
- `can_set_route(pos, route_name)`
|
||||
Returns whether it is possible to set the route designated by route_name from the signal at pos.
|
||||
|
||||
- `set_route(pos, route_name)`
|
||||
Requests the given route from the signal at pos. Has the same effect as clicking "Set Route" in the signalling dialog.
|
||||
|
||||
- `cancel_route(pos)`
|
||||
Cancels the route that is set from the signal at pos. Has the same effect as clicking "Cancel Route" in the signalling dialog.
|
||||
|
||||
- `get_aspect(pos)`
|
||||
Returns the signal aspect of the signal at pos. A signal aspect has the following format:
|
||||
```lua
|
||||
asp = {
|
||||
main = <int speed>,
|
||||
-- Main signal aspect, tells state and permitted speed of next section
|
||||
-- 0 = section is blocked
|
||||
-- >0 = section is free, speed limit is this value
|
||||
-- -1 = section is free, maximum speed permitted
|
||||
-- false = Signal doesn't provide main signal information, retain current speed limit.
|
||||
shunt = <boolean>,
|
||||
-- Whether train may proceed as shunt move, on sight
|
||||
-- main aspect takes precedence over this
|
||||
-- When main==0, train switches to shunt move and is restricted to speed 8
|
||||
proceed_as_main = <boolean>,
|
||||
-- If an approaching train is a shunt move and 'shunt' is false,
|
||||
-- the train may proceed as a train move under the "main" aspect
|
||||
-- if the main aspect permits it (i.e. main!=0)
|
||||
-- If this is not set, shunt moves are NOT allowed to switch to
|
||||
-- a train move, and must stop even if "main" would permit passing.
|
||||
-- This is intended to be used for "Halt for shunt moves" signs.
|
||||
|
||||
dst = <int speed>,
|
||||
-- Distant signal aspect, tells state and permitted speed of the section after next section
|
||||
-- The character of these information is purely informational
|
||||
-- At this time, this field is not actively used
|
||||
-- 0 = section is blocked
|
||||
-- >0 = section is free, speed limit is this value
|
||||
-- -1 = section is free, maximum speed permitted
|
||||
-- false = Signal doesn't provide distant signal information.
|
||||
|
||||
-- the character of call_on and dead_end is purely informative
|
||||
call_on = <boolean>, -- Call-on route, expect train in track ahead (not implemented yet)
|
||||
dead_end = <boolean>, -- Route ends on a dead end (e.g. bumper) (not implemented yet)
|
||||
|
||||
w_speed = <integer>,
|
||||
-- "Warning speed restriction". Supposed for short-term speed
|
||||
-- restrictions which always override any other restrictions
|
||||
-- imposed by "speed" fields, until lifted by a value of -1
|
||||
-- (Example: german Langsamfahrstellen-Signale)
|
||||
}
|
||||
```
|
||||
As of January 2020, the 'dst', 'call_on' and 'dead_end' fields are not used.
|
||||
|
||||
#### Lines
|
||||
|
||||
The advtrains_line_automation component adds a few contraptions that should make creating timeable systems easier.
|
||||
Part of its functionality is also available in LuaATC:
|
||||
|
||||
- `rwt.*` - all Railway Time functions are included as documented in [the wiki](https://advtrains.de/wiki/doku.php?id=dev:lines:rwt)
|
||||
|
||||
- `schedule(rw_time, msg)`, `schedule_in(rw_dtime, msg)`
|
||||
Schedules an event of type {type="schedule", schedule=true, msg=msg} at (resp. after) the specified railway time (which can be in any format). You can only schedule one event this way. (uses the new lines-internal scheduler)
|
||||
|
||||
Note: Using the lines scheduler is preferred over using `interrupt()`, as it's more performant and safer to use.
|
||||
|
||||
## Events
|
||||
The event table is a variable created locally by the component being triggered. It is a table with the following format:
|
||||
```lua
|
||||
event = {
|
||||
type = "<event type>",
|
||||
<event type> = true,
|
||||
--additional event-specific content
|
||||
}
|
||||
```
|
||||
You can check the event type by using the following:
|
||||
```lua
|
||||
if event.type == "wanted" then
|
||||
--do stuff
|
||||
end
|
||||
```
|
||||
or
|
||||
```lua
|
||||
if event.wanted then
|
||||
--do stuff
|
||||
end
|
||||
```
|
||||
where `wanted` is the event type to check for.
|
||||
See the "Active Components" section below for details on the various event types as not all of them are applicable to all components.
|
||||
|
||||
## Components
|
||||
Atlac components introduce automation-capable components that fall within two categories:
|
||||
- Active Components are components that are able to run Lua code, triggered by specific events.
|
||||
- Passive Components can't perform actions themselves. Their state can be read and set by active components or manually by the player.
|
||||
|
||||
### Lua ATC Rails
|
||||
Lua ATC rails are the only components that can actually interface with trains. The following event types are available to the Lua ATC rails:
|
||||
- `{type="train", train=true, id="<train_id>"}`
|
||||
* This event is fired when a train enters the rail. The field `id` is the unique train ID, which is 6-digit random numerical string.
|
||||
* If the world contains trains from an older advtrains version, this string may be longer and contain a dot `.`
|
||||
|
||||
- `{type="int", int=true, msg=<message>}`
|
||||
* Fired when an interrupt set by the `interrupt` function runs out. `<message>` is the message passed to the interrupt function.
|
||||
* For backwards compatiblity reasons, `<message>` is also contained in an `event.message` variable.
|
||||
|
||||
- `{type="ext_int", ext_int=true, message=<message>}`
|
||||
* Fired when another node called `interrupt_pos` on this position. `message` is the message passed to the interrupt_pos function.
|
||||
|
||||
- `{type="digiline", digiline=true, channel=<channel>, msg=<message>}`
|
||||
* Fired when the controller receives a digiline message.
|
||||
|
||||
#### Basic Lua Rail Functions and Variables
|
||||
In addition to the above environment functions, the following functions are available to whilst the train is in contact with the LuaATC rail:
|
||||
|
||||
- `atc_send(<atc_command>)`
|
||||
Sends the specified ATC command to the train (a string) and returns true. If there is no train, returns false and does nothing. See [atc_command.txt](../atc_command.txt) for the ATC command syntax.
|
||||
|
||||
- `atc_reset()`
|
||||
Resets the train's current ATC command. If there is no train, returns false and does nothing.
|
||||
|
||||
- `atc_arrow`
|
||||
Boolean, true when the train is driving in the direction of the arrows of the ATC rail. Nil if there is no train.
|
||||
|
||||
- `atc_id`
|
||||
Train ID of the train currently passing the controller. Nil if there's no train.
|
||||
|
||||
- `atc_speed`
|
||||
Speed of the train, or nil if there is no train.
|
||||
|
||||
- `atc_set_text_outside(text)`
|
||||
Set text shown on the outside of the train. Pass nil to show no text. `text` must be a string.
|
||||
|
||||
- `atc_set_text_inside(text)`
|
||||
Set text shown to train passengers. Pass nil to show no text. `text` must be a string.
|
||||
|
||||
- `get_line()`
|
||||
Returns the "Line" property of the train (a string).
|
||||
This can be used to distinguish between trains of different lines and route them appropriately.
|
||||
The interlocking system also uses this property for Automatic Routesetting.
|
||||
|
||||
- `set_line(line)`
|
||||
Sets the "Line" property of the train (a string).
|
||||
If the first digit of this string is a number (0-9), any subway wagons on the train (from advtrains_train_subway) will have this one displayed as line number
|
||||
(where "0" is actually shown as Line 10 on the train)
|
||||
|
||||
- `get_rc()`
|
||||
Returns the "Routingcode" property of the train (a string).
|
||||
The interlocking system uses this property for Automatic Routesetting.
|
||||
|
||||
- `set_rc(routingcode)`
|
||||
Sets the "Routingcode" property of the train (a string).
|
||||
The interlocking system uses this property for Automatic Routesetting.
|
||||
|
||||
#### Shunting Functions and Variables
|
||||
There are several functions available especially for shunting operations. Some of these functions make use of Freight Codes (FC) set in the Wagon Properties of each wagon and/or locomotive:
|
||||
|
||||
- `split_at_index(index, atc_command)`
|
||||
Splits the train at the specified index, into a train with index-1 wagons and a second train starting with the index-th wagon. The `atc_command` specified is sent to the second train after decoupling. `"S0"` or `"B0"` is common to ensure any locomotives in the remaining train don't continue to move.
|
||||
|
||||
Example: train has wagons `"foo","foo","foo","bar","bar","bar"`
|
||||
Command: `split_at_index(4,"S0")`
|
||||
Result: first train (continues at previous speed): `"foo","foo","foo"`, second train (slows at S0): `"bar","bar","bar"`
|
||||
|
||||
- `split_at_fc(atc_command, len)`
|
||||
Splits the train in such a way that all cars with non-empty current FC of the first part of the train have the same FC. The
|
||||
`atc_command` specified is sent to the rear part, as with split_at_index. It returns the fc of the cars of the first part.
|
||||
|
||||
Example : Train has current FCs `"" "" "bar" "foo" "bar"`
|
||||
Command: `split_at_fc(<atc_command>)`
|
||||
Result: `train "" "" "bar"` and `train "foo" "bar"`
|
||||
The function returns `"bar"` in this case.
|
||||
|
||||
The optional argument `len` specifies the maximum length for the
|
||||
first part of the train.
|
||||
Example: Train has current FCs `"foo" "foo" "foo" "foo" "bar" "bar"`
|
||||
Command: `split_at_fc(<atc_command>,3)`
|
||||
Result: `"foo" "foo" "foo"` and `"foo" "bar" "bar"`
|
||||
The function returns `"foo"` in this case.
|
||||
|
||||
- `split_off_locomotive(command, len)`
|
||||
Splits off the locomotives at the front of the train, which are
|
||||
identified by an empty FC. `command` specifies the ATC command to be
|
||||
executed by the rear half of the train. The optional argument `len` specifies the maximum length for the
|
||||
first part of the train as above.
|
||||
|
||||
- `step_fc()`
|
||||
Steps the FCs of all train cars forward. FCs are composed of codes
|
||||
separated by exclamation marks (`!`), for instance
|
||||
`"foo!bar!baz"`. Each wagon has a current FC, indicating its next
|
||||
destination. Stepping the freight code forward, selects the next
|
||||
code after the !. If the end of the string is reached, then the
|
||||
first code is selected, except if the string ends with a question
|
||||
mark (`?`), then the order is reversed.
|
||||
|
||||
|
||||
- `train_length()`
|
||||
returns the number of cars the train is composed of.
|
||||
|
||||
- `set_autocouple()`
|
||||
Sets the train into autocouple mode. The train will couple to the next train it collides with.
|
||||
|
||||
- `unset_autocouple()`
|
||||
Unsets autocouple mode
|
||||
|
||||
Deprecated:
|
||||
|
||||
- `set_shunt()`, `unset_shunt()`
|
||||
deprecated aliases for set_autocouple() and unset_autocouple(), will be removed from a later release.
|
||||
|
||||
|
||||
#### Interlocking
|
||||
This additional function is available when advtrains_interlocking is enabled:
|
||||
|
||||
- `atc_set_disable_ars(boolean)`
|
||||
Disables (true) or enables (false) the use of ARS for this train. The train will not trigger ARS (automatic route setting) on signals then.
|
||||
|
||||
Note: If you want to disable ARS from an approach callback, the call to `atc_set_disable_ars(true)` *must* happen during the approach callback, and may not be deferred to an interrupt(). Else the train might trigger an ARS before the interrupt fires.
|
||||
|
||||
#### Approach callbacks
|
||||
The LuaATC interface provides a way to hook into the approach callback system, which is for example used in the TSR rails (provided by advtrains_interlocking) or the station tracks (provided by advtrains_lines). However, for compatibility reasons, this behavior needs to be explicitly enabled.
|
||||
|
||||
Enabling the receiving of approach events works by setting a variable in the local environment of the ATC rail, by inserting the following code:
|
||||
|
||||
```lua
|
||||
__approach_callback_mode = 1
|
||||
-- to receive approach callbacks only in arrow direction
|
||||
-- or alternatively
|
||||
__approach_callback_mode = 2
|
||||
-- to receive approach callbacks in both directions
|
||||
```
|
||||
|
||||
The following event will be emitted when a train approaches:
|
||||
```lua
|
||||
{type="approach", approach=true, id="<train_id>"}
|
||||
```
|
||||
|
||||
Please note these important considerations when using approach callbacks:
|
||||
|
||||
- Approach events might be generated multiple times for the same approaching train. If you are using atc_set_lzb_tsr(), you need to call this function on every run of the approach callback, even if you issued it before for the same train.
|
||||
- A reference to the train is available while executing this event, so that functions such as atc_send() or atc_set_text_outside() can be called. On any consecutive interrupts, that reference will no longer be available until the train enters the track ("train" event)
|
||||
- Unlike all other callbacks, approach callbacks are executed synchronous during the train step. This may cause unexpected side effects when performing certain actions (such as switching turnouts, setting signals/routes) from inside such a callback. I strongly encourage you to only run things that are absolutely necessary at this point in time, and defer anything else to an interrupt(). Be aware that certain things might trigger unexpected behavior.
|
||||
|
||||
Operations that are safe to execute in approach callbacks:
|
||||
|
||||
- anything related only to the global environment (setting things in S)
|
||||
- digiline_send()
|
||||
- atc_set_text_*()
|
||||
- atc_set_lzb_tsr() (see below)
|
||||
|
||||
In the context of approach callbacks, one more function is available:
|
||||
|
||||
- `atc_set_lzb_tsr(speed)`
|
||||
Impose a Temporary Speed Restriction at the location of this rail, making the train pass this rail at the specified speed. (Causes the same behavior as the TSR rail)
|
||||
|
||||
#### Timetable Automation
|
||||
|
||||
The advtrains_line_automation component adds a few contraptions that should make creating timeable systems easier.
|
||||
Part of its functionality is also available in LuaATC:
|
||||
|
||||
- `rwt.*`
|
||||
All Railway Time functions are included as documented in https://advtrains.de/wiki/doku.php?id=dev:lines:rwt
|
||||
|
||||
- `schedule(rw_time, msg)`
|
||||
- `schedule_in(rw_dtime, msg)`
|
||||
Schedules the following event `{type="schedule", schedule=true, msg=msg}` at (resp. after) the specified railway time (which can be in any format). You can only schedule one event this way. Uses the new lines-internal scheduler.
|
||||
|
||||
### Operator panel
|
||||
This simple node executes its actions when punched. It can be used to change a switch and update the corresponding signals or similar applications. It can also be connected to by the`digilines` mod.
|
||||
|
||||
The event fired is `{type="punch", punch=true}` by default. In case of an interrupt or a digiline message, the events are similar to the ones of the ATC rail.
|
||||
|
||||
### Init code
|
||||
The initialization code is not a component as such, but rather a part of the whole environment. It can (and should) be used to make definitions that other components can refer to.
|
||||
A basic example function to define behavior for trains in stations:
|
||||
```lua
|
||||
function F.station(station_name)
|
||||
if event.train then
|
||||
atc_send("B0WOL")
|
||||
atc_set_text_inside(station_name)
|
||||
interrupt(10,"depart")
|
||||
end
|
||||
if event.int and event.message="depart" then
|
||||
atc_set_text_inside("") --an empty string clears the displayed text
|
||||
atc_send("OCD1SM")
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
The corresponding Lua ATC Rail(s) would then contain the following or similar:
|
||||
```lua
|
||||
F.station("Main Station")
|
||||
```
|
||||
|
||||
The init code is run whenever the F table needs to be refilled with data. This is the case on server startup and whenever the init code is changed and you choose to run it.
|
||||
The event table of the init code is always `{type="init", init=true}` and can not be anything else.
|
||||
Functions are run in the environment of the currently active node, regardless of where they were defined.
|
||||
|
||||
### Passive components
|
||||
|
||||
All passive components can be interfaced with the `setstate()` and `getstate()` functions (see above).
|
||||
Each node below has been mapped to specific "states":
|
||||
|
||||
#### Signals
|
||||
The red/green light signals `advtrains:signal_on/off` are interfaceable. Others such as `advtrains:retrosignal_on/off` are not. If advtrains_interlocking is enabled, trains will obey the signal if the influence point is set.
|
||||
|
||||
- "green" - Signal shows green light
|
||||
- "red" - Signal shows red light
|
||||
|
||||
#### Switches/Turnouts
|
||||
All default rail switches are interfaceable, independent of orientation.
|
||||
|
||||
- "cr" The switch is set in the direction that is not straight.
|
||||
- "st" The switch is set in the direction that is straight.
|
||||
|
||||
The "Y" and "3-Way" switches have custom states. Looking from the convergence point:
|
||||
|
||||
- "l" The switch is set towards the left.
|
||||
- "c" The switch is set towards the center (3-way only).
|
||||
- "r" The switch is set towards the right.
|
||||
|
||||
|
||||
#### Mesecon Switch
|
||||
The Mesecon switch can be switched using LuaAutomation. Note that this is not possible on levers or protected mesecon switches, only the unprotected full-node 'Switch' block `mesecons_switch:mesecon_switch_on/off`.
|
||||
|
||||
- "on" - the switch is switched on.
|
||||
- "off" - the switch is switched off.
|
||||
|
||||
#### Andrew's Cross
|
||||
|
||||
- "on" - it blinks.
|
||||
- "off" - it does not blink.
|
||||
|
||||
#### Passive Component Naming
|
||||
You can assign names to passive components using the Passive Component Naming tool.
|
||||
Once you set a name for any component, you can reference it by that name in the `getstate()` and `setstate()` functions.
|
||||
This way, you don't need to memorize positions.
|
||||
|
||||
Example: signal named `"Stn_P1_out"` at `(1,2,3)`
|
||||
Use `setstate("Stn_P1_out", "green")` instead of `setstate(POS(1,2,3), "green")`
|
||||
|
||||
If `advtrains_interlocking` is enabled, PC-Naming can also be used to name interlocking signals for route setting via the `set_route()` functions.
|
||||
**Important**: The "Signal Name" field in the signalling formspec is completely independent from PC-Naming and can't be used to look up the position. You need to explicitly use the PC-Naming tool.
|
||||
|
|
@ -1,288 +0,0 @@
|
|||
|
||||
#### Advtrains - Lua Automation features
|
||||
|
||||
This mod offers components that run LUA code and interface with each other through a global environment. It makes complex automated railway systems possible.
|
||||
|
||||
### atlatc
|
||||
The mod is sometimes abbreviated as 'atlatc'. This stands for AdvTrainsLuaATC. This short name has been chosen for user convenience, since the name of this mod ('advtrains_luaautomation') is very long.
|
||||
|
||||
### Privilege
|
||||
To perform any operations using this mod (except executing operation panels), players need the "atlatc" privilege.
|
||||
This privilege should never be granted to anyone except trusted administrators. Even though the LUA environment is sandboxed, it is still possible to DoS the server by coding infinite loops or requesting expotentially growing interrupts.
|
||||
|
||||
### Active and passive
|
||||
Active components are these who have LUA code running in them. They are triggered on specific events. Passive components are dumb, they only have a state and can be set to another state, they can't perform actions themselves.
|
||||
|
||||
### Environments
|
||||
|
||||
Each active component is assigned to an environment. This is where all data are held. Components in different environments can't inferface with each other.
|
||||
This system allows multiple independent automation systems to run simultaneously without polluting each other's environment.
|
||||
|
||||
/env_create <env_name>
|
||||
Create environment with the given name. To be able to do anything, you first need to create an environment. Choose the name wisely, you can't change it afterwards.
|
||||
|
||||
/env_setup <env_name>
|
||||
Invoke the form to edit the environment's initialization code. For more information, see the section on active components. You can also delete an environment from here.
|
||||
|
||||
### Active components
|
||||
|
||||
The code of every active component is run on specific events which are explained soon. When run, every variable written that is not local and is no function or userdata is saved over code re-runs and over server restarts. Additionally, the following global variables are defined:
|
||||
|
||||
# event
|
||||
The variable 'event' contains a table with information on the current event. How this table can look is explained below.
|
||||
|
||||
# S
|
||||
The variable 'S' contains a table which is shared between all components of the environment. Its contents are persistent over server restarts. May not contain functions, every other value is allowed.
|
||||
Example:
|
||||
Component 1: S.stuff="foo"
|
||||
Component 2: print(S.stuff)
|
||||
-> foo
|
||||
|
||||
# F
|
||||
The variable 'F' also contains a table which is shared between all components of the environment. Its contents are discarded on server shutdown or when the init code gets re-run. Every data type is allowed, even functions.
|
||||
The purpose of this table is not to save data, but to provide static value and function definitions. The table should be populated by the init code.
|
||||
|
||||
# Standard Lua functions
|
||||
The following standard Lua libraries are available:
|
||||
string, math, table, os
|
||||
The following standard Lua functions are available:
|
||||
assert, error, ipairs, pairs, next, select, tonumber, tostring, type, unpack
|
||||
|
||||
Every attempt to overwrite any of the predefined values results in an error.
|
||||
|
||||
# LuaAutomation-specific global functions
|
||||
|
||||
POS(x,y,z)
|
||||
Shorthand function to create a position vector {x=?, y=?, z=?} with less characters
|
||||
|
||||
In the following functions, all parameters named 'pos' designate a position. You can use either:
|
||||
- a default Minetest position vector (like {x=34, y=2, z=-18})
|
||||
- the POS(34,2,-18) shorthand
|
||||
- A string, the passive component name. See 'passive component naming'.
|
||||
|
||||
getstate(pos)
|
||||
Get the state of the passive component at position 'pos'. See section on passive components for more info.
|
||||
pos can be either a position vector (created by POS()) or a string, the name of this passive component.
|
||||
|
||||
setstate(pos, newstate)
|
||||
Set the state of the passive component at position 'pos'.
|
||||
|
||||
is_passive(pos)
|
||||
Checks whether there is a passive component at the position pos (and/or whether a passive component with this name exists)
|
||||
|
||||
interrupt(time, message)
|
||||
Cause LuaAutomation to trigger an 'int' event on this component after the given time in seconds with the specified 'message' field. 'message' can be of any Lua data type.
|
||||
Not available in init code!
|
||||
|
||||
interrupt_pos(pos, message)
|
||||
Immediately trigger an 'ext_int' event on the active component at position pos. 'message' is like in interrupt().
|
||||
USE WITH CARE, or better don't use! Incorrect use can result in expotential growth of interrupts.
|
||||
|
||||
digiline_send(channel, message)
|
||||
Make this active component send a digiline message on the specified channel.
|
||||
Not available in init code!
|
||||
|
||||
-- The next 4 functions are available when advtrains_interlocking is enabled: --
|
||||
|
||||
can_set_route(pos, route_name)
|
||||
Returns whether it is possible to set the route designated by route_name from the signal at pos.
|
||||
|
||||
set_route(pos, route_name)
|
||||
Requests the given route from the signal at pos. Has the same effect as clicking "Set Route" in the signalling dialog.
|
||||
|
||||
cancel_route(pos)
|
||||
Cancels the route that is set from the signal at pos. Has the same effect as clicking "Cancel Route" in the signalling dialog.
|
||||
|
||||
get_aspect(pos)
|
||||
Returns the signal aspect of the signal at pos. A signal aspect has the following format:
|
||||
aspect = {
|
||||
main = { -- the next track section in line. Shows blocked for shunt routes
|
||||
free = <boolean>,
|
||||
speed = <int km/h>,
|
||||
},
|
||||
shunt = { -- whether a "shunting allowed" aspect should be shown
|
||||
free = <boolean>,
|
||||
}
|
||||
dst = { -- the aspect of the next main signal on (at end of) route
|
||||
free = <boolean>,
|
||||
speed = <int km/h>,
|
||||
}
|
||||
info = {
|
||||
call_on = <boolean>, -- Call-on route, expect train in track ahead
|
||||
dead_end = <boolean>, -- Route ends on a dead end (e.g. bumper)
|
||||
}
|
||||
}
|
||||
As of August 2018, only the aspect.main.free field is ever used by the interlocking system.
|
||||
|
||||
# Lines
|
||||
|
||||
The advtrains_line_automation component adds a few contraptions that should make creating timeable systems easier.
|
||||
Part of its functionality is also available in LuaATC:
|
||||
|
||||
- rwt.* - all Railway Time functions are included as documented in https://advtrains.de/wiki/doku.php?id=dev:lines:rwt
|
||||
|
||||
- schedule(rw_time, msg)
|
||||
- schedule_in(rw_dtime, msg)
|
||||
Schedules an event of type {type="schedule", schedule=true, msg=msg} at (resp. after) the specified railway time.
|
||||
(which can be in any format). You can only schedule one event this way. (uses the new lines-internal scheduler)
|
||||
|
||||
## Components and events
|
||||
|
||||
The event table is a table of the following format:
|
||||
{
|
||||
type = "<type>",
|
||||
<type> = true,
|
||||
... additional content ...
|
||||
}
|
||||
You can check for the event type by either using
|
||||
if event.type == "wanted" then ...do stuff... end
|
||||
or
|
||||
if event.wanted then ...do stuff... end
|
||||
(if 'wanted' is the event type to check for)
|
||||
|
||||
# Init code
|
||||
The initialization code is not a component as such, but rather a part of the whole environment. It can (and should) be used to make definitions that other components can refer to.
|
||||
Examples:
|
||||
A function to define behavior for trains in subway stations:
|
||||
function F.station()
|
||||
if event.train then atc_send("B0WOL") end
|
||||
if event.int and event.message="depart" then atc_send("OCD1SM") end
|
||||
end
|
||||
|
||||
The init code is run whenever the F table needs to be refilled with data. This is the case on server startup and whenever the init code is changed and you choose to run it.
|
||||
Functions are run in the environment of the currently active node, regardless of where they were defined. So, the 'event' table always reflects the state of the calling node.
|
||||
|
||||
The 'event' table of the init code is always {type="init", init=true}.
|
||||
|
||||
# ATC rails
|
||||
The Lua-controlled ATC rails are the only components that can actually interface with trains. The following event types are generated:
|
||||
|
||||
{type="train", train=true, id="<train_id>"}
|
||||
This event is fired when a train enters the rail. The field 'id' is the unique train ID, which is 6-digit random numerical string.
|
||||
If the world contains trains from an older advtrains version, this string may be longer and contain a dot (.)
|
||||
|
||||
{type="int", int=true, msg=<message>}
|
||||
Fired when an interrupt set by the 'interrupt' function runs out. 'message' is the message passed to the interrupt function.
|
||||
For backwards compatiblity reasons, <message> is also contained in an event.message field.
|
||||
{type="ext_int", ext_int=true, message=<message>}
|
||||
Fired when another node called 'interrupt_pos' on this position. 'message' is the message passed to the interrupt_pos function.
|
||||
|
||||
{type="digiline", digiline=true, channel=<channel>, msg=<message>}
|
||||
Fired when the controller receives a digiline message.
|
||||
|
||||
In addition to the default environment functions, the following functions are available:
|
||||
|
||||
atc_send(<atc_command>)
|
||||
Sends the specified ATC command to the train and returns true. If there is no train, returns false and does nothing.
|
||||
atc_reset()
|
||||
Resets the train's current ATC command. If there is no train, returns false and does nothing.
|
||||
atc_arrow
|
||||
Boolean, true when the train is driving in the direction of the arrows of the ATC rail. Nil if there is no train.
|
||||
atc_id
|
||||
Train ID of the train currently passing the controller. Nil if there's no train.
|
||||
atc_speed
|
||||
Speed of the train, or nil if there is no train.
|
||||
atc_set_text_outside(text)
|
||||
Set text shown on the outside of the train. Pass nil to show no text.
|
||||
atc_set_text_inside(text)
|
||||
Set text shown to train passengers. Pass nil to show no text.
|
||||
get_line()
|
||||
Returns the "Line" property of the train (a string).
|
||||
This can be used to distinguish between trains of different lines and route them appropriately.
|
||||
The interlocking system also uses this property for Automatic Routesetting.
|
||||
set_line(line)
|
||||
Sets the "Line" property of the train (a string).
|
||||
If the first digit of this string is a number (0-9), any subway wagons on the train will have this one displayed as line number
|
||||
(where "0" is actually shown as Line 10 on the train)
|
||||
get_rc()
|
||||
Returns the "Routingcode" property of the train (a string).
|
||||
The interlocking system uses this property for Automatic Routesetting.
|
||||
set_rc(routingcode)
|
||||
Sets the "Routingcode" property of the train (a string).
|
||||
The interlocking system uses this property for Automatic Routesetting.
|
||||
split_at_index(index, command)
|
||||
Splits the train at the specified index, into a train with index-1 wagons and a second train starting with the index-th wagon.
|
||||
command specifies an atc command to be sent to the second train after decoupling.
|
||||
split_at_fc(command, len)
|
||||
Splits the train in such a way that all cars with non-empty
|
||||
current FC of the first part of the train have the same FC. The
|
||||
command specified is sent to the rear part, as with
|
||||
split_at_index. It returns the fc of the cars of the first part.
|
||||
|
||||
The optional argument len specifies the maximum length for the
|
||||
first part of the train. Say, we have len=3, and the train has ""
|
||||
"" "foo" "foo" "foo" "bar", then the first train part will be ""
|
||||
"" "foo".
|
||||
|
||||
Example : Train has current FCs "" "" "foo" "bar" "foo"
|
||||
Result: first train: "" "" "foo"; second train: "bar" "foo"
|
||||
The command returns "foo" in this case
|
||||
split_off_locomotive(command, len)
|
||||
Splits off the locomotives at the front of the train, which are
|
||||
identified by an empty FC. command specifies the command to be
|
||||
executed by the rear half of the train.
|
||||
|
||||
The optional argument len specifies the maximum length for the
|
||||
first part of the train. Say, we have len=3, and the train has ""
|
||||
"" "foo" "foo" "foo" "bar", then the first train part will be ""
|
||||
"" "foo".
|
||||
step_fc()
|
||||
Steps the FCs of all train cars forward. FCs are composed of codes
|
||||
separated by exclamation marks (!), for instance
|
||||
"foo!bar!baz". Each wagon has a current FC, indicating its next
|
||||
destination. Stepping the freight code forward, selects the next
|
||||
code after the !. If the end of the string is reached, then the
|
||||
first code is selected, except if the string ends with a question
|
||||
mark, then the order is reversed.
|
||||
|
||||
train_length()
|
||||
returns the number of cars the train is composed of
|
||||
set_autocouple()
|
||||
Sets the train into autocouple mode
|
||||
unset_autocouple()
|
||||
Unsets autocouple mode
|
||||
|
||||
set_shunt(), unset_shunt()
|
||||
deprecated aliases for set_autocouple() and unset_autocouple(), will be removed from a later release.
|
||||
|
||||
|
||||
# Operator panel
|
||||
This simple node executes its actions when punched. It can be used to change a switch and update the corresponding signals or similar applications.
|
||||
|
||||
The event fired is {type="punch", punch=true} by default. In case of an interrupt or a digiline message, the events are similar to the ones of the ATC rail.
|
||||
|
||||
### Passive components
|
||||
|
||||
All passive components can be interfaced with the setstate and getstate functions(see above).
|
||||
Below, each apperance is mapped to the "state" of that node.
|
||||
|
||||
## Signals
|
||||
The light signals are interfaceable, the analog signals are not.
|
||||
"green" - Signal shows green light
|
||||
"red" - Signal shows red light
|
||||
|
||||
## Switches
|
||||
All default rail switches are interfaceable, independent of orientation.
|
||||
"cr" - The switch is set in the direction that is not straight.
|
||||
"st" - The switch is set in the direction that is straight.
|
||||
|
||||
## Mesecon Switch
|
||||
The Mesecon switch can be switched using LuaAutomation. Note that this is not possible on levers, only the full-node 'Switch' block.
|
||||
"on" - the switch is switched on
|
||||
"off" - the switch is switched off
|
||||
|
||||
##Andrew's Cross
|
||||
"on" - it blinks
|
||||
"off" - it does not blink
|
||||
|
||||
### Passive component naming
|
||||
You can assign names to passive components using the Passive Component Naming tool.
|
||||
Once you set a name for any component, you can reference it by that name in the getstate() and setstate() functions, like this:
|
||||
(Imagine a signal that you have named "Stn_P1_out" at position (1,2,3) )
|
||||
setstate("Stn_P1_out", "green") instead of setstate(POS(1,2,3), "green")
|
||||
This way, you don't need to memorize positions.
|
||||
|
||||
PC-Naming can also be used to name interlocking signals for route setting via the set_route() functions. IMPORTANT: The "Signal Name" set in the
|
||||
signalling formspec is completely independent and can NOT be used to look up the position, you need to explicitly use the PCNaming tool.
|
||||
|
||||
--TODO: Ein paar mehr Codebeispiele wären schön, insbesondere mit os.date und so...
|
|
@ -101,7 +101,7 @@ function ac.run_in_env(pos, evtdata, customfct_p)
|
|||
end
|
||||
|
||||
local meta
|
||||
if minetest.get_node_or_nil(pos) then
|
||||
if advtrains.is_node_loaded(pos) then
|
||||
meta=minetest.get_meta(pos)
|
||||
end
|
||||
|
||||
|
@ -109,8 +109,9 @@ function ac.run_in_env(pos, evtdata, customfct_p)
|
|||
atwarn("LuaAutomation component at",ph,": Not an existing environment: "..(nodetbl.env or "<nil>"))
|
||||
return false
|
||||
end
|
||||
local env = atlatc.envs[nodetbl.env]
|
||||
if not nodetbl.code or nodetbl.code=="" then
|
||||
atwarn("LuaAutomation component at",ph,": No code to run! (insert -- to suppress warning)")
|
||||
env:log("warning", "LuaAutomation component at",ph,": No code to run! (insert -- to suppress warning)")
|
||||
return false
|
||||
end
|
||||
|
||||
|
@ -121,11 +122,25 @@ function ac.run_in_env(pos, evtdata, customfct_p)
|
|||
assert(t >= 0)
|
||||
atlatc.interrupt.add(t, pos, {type="int", int=true, message=imesg, msg=imesg}) --Compatiblity "message" field.
|
||||
end
|
||||
customfct.interrupt_safe=function(t, imesg)
|
||||
assertt(t, "number")
|
||||
assert(t >= 0)
|
||||
if atlatc.interrupt.has_at_pos(pos) then
|
||||
return false
|
||||
end
|
||||
atlatc.interrupt.add(t, pos, {type="int", int=true, message=imesg, msg=imesg}) --Compatiblity "message" field.
|
||||
return true
|
||||
end
|
||||
customfct.clear_interrupts=function()
|
||||
atlatc.interrupt.clear_ints_at_pos(pos)
|
||||
end
|
||||
-- add digiline_send function, if digiline is loaded
|
||||
if minetest.global_exists("digiline") then
|
||||
customfct.digiline_send=function(channel, msg)
|
||||
assertt(channel, "string")
|
||||
digiline:receptor_send(pos, digiline.rules.default, channel, msg)
|
||||
if advtrains.is_node_loaded(pos) then
|
||||
digiline:receptor_send(pos, digiline.rules.default, channel, msg)
|
||||
end
|
||||
end
|
||||
end
|
||||
-- add lines scheduler if enabled
|
||||
|
@ -139,15 +154,20 @@ function ac.run_in_env(pos, evtdata, customfct_p)
|
|||
end
|
||||
|
||||
local datain=nodetbl.data or {}
|
||||
local succ, dataout = atlatc.envs[nodetbl.env]:execute_code(datain, nodetbl.code, evtdata, customfct)
|
||||
local succ, dataout = env:execute_code(datain, nodetbl.code, evtdata, customfct)
|
||||
if succ then
|
||||
atlatc.active.nodes[ph].data=atlatc.remove_invalid_data(dataout)
|
||||
else
|
||||
atlatc.active.nodes[ph].err=dataout
|
||||
atwarn("LuaAutomation ATC interface rail at",ph,": LUA Error:",dataout)
|
||||
env:log("error", "LuaATC component at",ph,": LUA Error:",dataout)
|
||||
if meta then
|
||||
meta:set_string("infotext", "LuaAutomation ATC interface rail, ERROR:"..dataout)
|
||||
meta:set_string("infotext", "LuaATC component, ERROR:"..dataout)
|
||||
end
|
||||
--TODO temporary
|
||||
--if customfct.atc_id then
|
||||
-- advtrains.drb_dump(customfct.atc_id)
|
||||
-- error("Debug: LuaATC error hit!")
|
||||
--end
|
||||
end
|
||||
if meta then
|
||||
meta:set_string("formspec", ac.getform(pos, meta))
|
||||
|
|
|
@ -5,7 +5,10 @@
|
|||
--Using subtable
|
||||
local r={}
|
||||
|
||||
function r.fire_event(pos, evtdata)
|
||||
-- Note on appr_internal:
|
||||
-- The Approach callback is a special corner case: the train is not on the node, and it is executed synchronized
|
||||
-- (in the train step right during LZB traversal). We therefore need access to the train id and the lzbdata table
|
||||
function r.fire_event(pos, evtdata, appr_internal)
|
||||
|
||||
local ph=minetest.pos_to_string(pos)
|
||||
local railtbl = atlatc.active.nodes[ph]
|
||||
|
@ -15,37 +18,34 @@ function r.fire_event(pos, evtdata)
|
|||
return
|
||||
end
|
||||
|
||||
|
||||
local arrowconn = railtbl.arrowconn
|
||||
if not arrowconn then
|
||||
atwarn("LuaAutomation ATC interface rail at",ph,": Incomplete Data! Please visit position and click 'Save'!")
|
||||
return
|
||||
end
|
||||
|
||||
--prepare ingame API for ATC. Regenerate each time since pos needs to be known
|
||||
--If no train, then return false.
|
||||
local train_id=advtrains.get_train_at_pos(pos)
|
||||
local train, atc_arrow, tvel
|
||||
if train_id then train=advtrains.trains[train_id] end
|
||||
if train then
|
||||
if not train.path then
|
||||
--we happened to get in between an invalidation step
|
||||
--delay
|
||||
atlatc.interrupt.add(0,pos,evtdata)
|
||||
return
|
||||
end
|
||||
local index = advtrains.path_lookup(train, pos)
|
||||
|
||||
local iconnid = 1
|
||||
if index then
|
||||
iconnid = train.path_cn[index]
|
||||
else
|
||||
atwarn("ATC rail at", pos, ": Rail not on train's path! Can't determine arrow direction. Assuming +!")
|
||||
end
|
||||
atc_arrow = iconnid == 1
|
||||
-- try to get the train from the event data
|
||||
-- This workaround is required because the callback is one step delayed, and a fast train may have already left the node.
|
||||
-- Also used for approach callback
|
||||
local train_id = evtdata._train_id
|
||||
local atc_arrow = evtdata._train_arrow
|
||||
local train, tvel
|
||||
|
||||
if train_id then
|
||||
train=advtrains.trains[train_id]
|
||||
-- speed
|
||||
tvel=train.velocity
|
||||
-- if still no train_id available, try to get the train at my position
|
||||
else
|
||||
train_id=advtrains.get_train_at_pos(pos)
|
||||
if train_id then
|
||||
train=advtrains.trains[train_id]
|
||||
advtrains.train_ensure_init(train_id, train)
|
||||
-- look up atc_arrow
|
||||
local index = advtrains.path_lookup(train, pos)
|
||||
atc_arrow = (train.path_cn[index] == 1)
|
||||
-- speed
|
||||
tvel=train.velocity
|
||||
end
|
||||
end
|
||||
|
||||
local customfct={
|
||||
atc_send = function(cmd)
|
||||
if not train_id then return false end
|
||||
|
@ -92,12 +92,13 @@ function r.fire_event(pos, evtdata)
|
|||
advtrains.train_step_fc(train)
|
||||
end,
|
||||
set_shunt = function()
|
||||
-- enable shunting mode
|
||||
if not train_id then return false end
|
||||
train.autocouple = true
|
||||
train.is_shunt = true
|
||||
end,
|
||||
unset_shunt = function()
|
||||
if not train_id then return false end
|
||||
train.autocouple = nil
|
||||
train.is_shunt = nil
|
||||
end,
|
||||
set_autocouple = function ()
|
||||
if not train_id then return false end
|
||||
|
@ -106,7 +107,7 @@ function r.fire_event(pos, evtdata)
|
|||
unset_autocouple = function ()
|
||||
if not train_id then return false end
|
||||
train.autocouple = nil
|
||||
end,
|
||||
end,
|
||||
set_line = function(line)
|
||||
if type(line)~="string" and type(line)~="number" then
|
||||
return false
|
||||
|
@ -150,45 +151,89 @@ function r.fire_event(pos, evtdata)
|
|||
advtrains.trains[train_id].text_inside=text
|
||||
return true
|
||||
end,
|
||||
atc_set_lzb_tsr = function(speed)
|
||||
if not appr_internal then
|
||||
error("atc_set_lzb_tsr() can only be used during 'approach' events!")
|
||||
end
|
||||
assert(tonumber(speed), "Number expected!")
|
||||
|
||||
local index = appr_internal.index
|
||||
advtrains.lzb_add_checkpoint(train, index, speed, nil)
|
||||
|
||||
return true
|
||||
end,
|
||||
}
|
||||
-- interlocking specific
|
||||
if advtrains.interlocking then
|
||||
customfct.atc_set_ars_disable = function(value)
|
||||
advtrains.interlocking.ars_set_disable(train, value)
|
||||
end
|
||||
end
|
||||
|
||||
atlatc.active.run_in_env(pos, evtdata, customfct)
|
||||
|
||||
end
|
||||
|
||||
if minetest.get_modpath("advtrains_train_track") ~= nil then
|
||||
advtrains.register_tracks("default", {
|
||||
nodename_prefix="advtrains_luaautomation:dtrack",
|
||||
texture_prefix="advtrains_dtrack_atc",
|
||||
models_prefix="advtrains_dtrack",
|
||||
models_suffix=".b3d",
|
||||
shared_texture="advtrains_dtrack_shared_atc.png",
|
||||
description=atltrans("LuaAutomation ATC Rail"),
|
||||
formats={},
|
||||
get_additional_definiton = function(def, preset, suffix, rotation)
|
||||
return {
|
||||
after_place_node = atlatc.active.after_place_node,
|
||||
after_dig_node = atlatc.active.after_dig_node,
|
||||
on_receive_fields = function(pos, ...)
|
||||
atlatc.active.on_receive_fields(pos, ...)
|
||||
--set arrowconn (for ATC)
|
||||
local ph=minetest.pos_to_string(pos)
|
||||
local _, conns=advtrains.get_rail_info_at(pos, advtrains.all_tracktypes)
|
||||
atlatc.active.nodes[ph].arrowconn=conns[1].c
|
||||
advtrains.register_tracks("default", {
|
||||
nodename_prefix="advtrains_luaautomation:dtrack",
|
||||
texture_prefix="advtrains_dtrack_atc",
|
||||
models_prefix="advtrains_dtrack",
|
||||
models_suffix=".b3d",
|
||||
shared_texture="advtrains_dtrack_shared_atc.png",
|
||||
description=atltrans("LuaAutomation ATC Rail"),
|
||||
formats={},
|
||||
get_additional_definiton = function(def, preset, suffix, rotation)
|
||||
return {
|
||||
after_place_node = atlatc.active.after_place_node,
|
||||
after_dig_node = atlatc.active.after_dig_node,
|
||||
|
||||
on_receive_fields = function(pos, ...)
|
||||
atlatc.active.on_receive_fields(pos, ...)
|
||||
|
||||
--set arrowconn (for ATC)
|
||||
local ph=minetest.pos_to_string(pos)
|
||||
local _, conns=advtrains.get_rail_info_at(pos, advtrains.all_tracktypes)
|
||||
atlatc.active.nodes[ph].arrowconn=conns[1].c
|
||||
end,
|
||||
|
||||
advtrains = {
|
||||
on_train_enter = function(pos, train_id, train, index)
|
||||
--do async. Event is fired in train steps
|
||||
atlatc.interrupt.add(0, pos, {type="train", train=true, id=train_id,
|
||||
_train_id = train_id, _train_arrow = (train.path_cn[index] == 1)})
|
||||
end,
|
||||
advtrains = atlatc.active.trackdef_advtrains_defs,
|
||||
luaautomation = {
|
||||
fire_event=r.fire_event
|
||||
on_train_approach = function(pos, train_id, train, index, has_entered, lzbdata)
|
||||
-- Insert an event only if the rail indicated that it supports approach callbacks
|
||||
local ph=minetest.pos_to_string(pos)
|
||||
local railtbl = atlatc.active.nodes[ph]
|
||||
-- uses a "magic variable" in the local environment of the node
|
||||
-- This hack is necessary because code might not be prepared to get approach events...
|
||||
if railtbl and railtbl.data and railtbl.data.__approach_callback_mode then
|
||||
local acm = railtbl.data.__approach_callback_mode
|
||||
local in_arrow = (train.path_cn[index] == 1)
|
||||
if acm==2 or (acm==1 and in_arrow) then
|
||||
local evtdata = {type="approach", approach=true, id=train_id, has_entered = has_entered,
|
||||
_train_id = train_id, _train_arrow = in_arrow} -- reuses code from train_enter
|
||||
-- This event is *required* to run synchronously, because it might set the ars_disable flag on the train and add LZB checkpoints,
|
||||
-- although this is generally discouraged because this happens right in a train step
|
||||
-- At this moment, I am not aware whether this may cause side effects, and I must encourage users not to do expensive calculations here.
|
||||
r.fire_event(pos, evtdata, {train_id = train_id, train = train, index = index, lzbdata = lzbdata})
|
||||
end
|
||||
end
|
||||
end,
|
||||
},
|
||||
luaautomation = {
|
||||
fire_event=r.fire_event
|
||||
},
|
||||
digiline = {
|
||||
receptor = {},
|
||||
effector = {
|
||||
action = atlatc.active.on_digiline_receive
|
||||
},
|
||||
digiline = {
|
||||
receptor = {},
|
||||
effector = {
|
||||
action = atlatc.active.on_digiline_receive
|
||||
},
|
||||
},
|
||||
}
|
||||
end,
|
||||
}, advtrains.trackpresets.t_30deg_straightonly)
|
||||
end
|
||||
},
|
||||
}
|
||||
end,
|
||||
}, advtrains.trackpresets.t_30deg_straightonly)
|
||||
|
||||
|
||||
atlatc.rail = r
|
||||
|
|
|
@ -43,12 +43,78 @@ core.register_chatcommand("env_create", {
|
|||
privs = {atlatc=true},
|
||||
func = function(name, param)
|
||||
if not param or param=="" then return false, "Name required!" end
|
||||
if string.find(param, "[^a-zA-Z0-9-_]") then return false, "Invalid name (only common characters)" end
|
||||
if atlatc.envs[param] then return false, "Environment already exists!" end
|
||||
atlatc.envs[param] = atlatc.env_new(param)
|
||||
atlatc.envs[param].subscribers = {name}
|
||||
return true, "Created environment '"..param.."'. Use '/env_setup "..param.."' to define global initialization code, or start building LuaATC components!"
|
||||
end,
|
||||
})
|
||||
|
||||
core.register_chatcommand("env_subscribe", {
|
||||
params = "<environment name>",
|
||||
description = "Subscribe to the log of an Advtrains LuaATC environment",
|
||||
privs = {atlatc=true},
|
||||
func = function(name, param)
|
||||
local env=atlatc.envs[param]
|
||||
if not env then return false,"Invalid environment name!" end
|
||||
for _,pname in ipairs(env.subscribers) do
|
||||
if pname==name then
|
||||
return false, "Already subscribed!"
|
||||
end
|
||||
end
|
||||
table.insert(env.subscribers, name)
|
||||
return true, "Subscribed to environment '"..param.."'."
|
||||
end,
|
||||
})
|
||||
core.register_chatcommand("env_unsubscribe", {
|
||||
params = "<environment name>",
|
||||
description = "Unubscribe to the log of an Advtrains LuaATC environment",
|
||||
privs = {atlatc=true},
|
||||
func = function(name, param)
|
||||
local env=atlatc.envs[param]
|
||||
if not env then return false,"Invalid environment name!" end
|
||||
for index,pname in ipairs(env.subscribers) do
|
||||
if pname==name then
|
||||
table.remove(env.subscribers, index)
|
||||
return true, "Successfully unsubscribed!"
|
||||
end
|
||||
end
|
||||
return false, "Not subscribed to environment '"..param.."'."
|
||||
end,
|
||||
})
|
||||
core.register_chatcommand("env_subscriptions", {
|
||||
params = "[environment name]",
|
||||
description = "List Advtrains LuaATC environments you are subscribed to (no parameters) or subscribers of an environment (giving an env name).",
|
||||
privs = {atlatc=true},
|
||||
func = function(name, param)
|
||||
if not param or param=="" then
|
||||
local none=true
|
||||
for envname, env in pairs(atlatc.envs) do
|
||||
for _,pname in ipairs(env.subscribers) do
|
||||
if pname==name then
|
||||
none=false
|
||||
minetest.chat_send_player(name, envname)
|
||||
end
|
||||
end
|
||||
end
|
||||
if none then
|
||||
return false, "Not subscribed to any!"
|
||||
end
|
||||
return true
|
||||
end
|
||||
local env=atlatc.envs[param]
|
||||
if not env then return false,"Invalid environment name!" end
|
||||
local none=true
|
||||
for index,pname in ipairs(env.subscribers) do
|
||||
none=false
|
||||
minetest.chat_send_player(name, pname)
|
||||
end
|
||||
if none then
|
||||
return false, "No subscribers!"
|
||||
end
|
||||
return true
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
|
||||
|
|
|
@ -33,12 +33,12 @@ local env_proto={
|
|||
self.sdata=data.sdata and atlatc.remove_invalid_data(data.sdata) or {}
|
||||
self.fdata={}
|
||||
self.init_code=data.init_code or ""
|
||||
self.step_code=data.step_code or ""
|
||||
self.subscribers=data.subscribers or {}
|
||||
end,
|
||||
save = function(self)
|
||||
-- throw any function values out of the sdata table
|
||||
self.sdata = atlatc.remove_invalid_data(self.sdata)
|
||||
return {sdata = self.sdata, init_code=self.init_code, step_code=self.step_code}
|
||||
return {sdata = self.sdata, init_code=self.init_code, subscribers=self.subscribers}
|
||||
end,
|
||||
}
|
||||
|
||||
|
@ -50,14 +50,6 @@ local safe_globals = {
|
|||
"tonumber", "tostring", "type", "unpack", "_VERSION"
|
||||
}
|
||||
|
||||
--print is actually minetest.chat_send_all()
|
||||
--using advtrains.print_concat_table because it's cool
|
||||
local function safe_print(t, ...)
|
||||
local str=advtrains.print_concat_table({t, ...})
|
||||
minetest.log("action", "[atlatc] "..str)
|
||||
minetest.chat_send_all(str)
|
||||
end
|
||||
|
||||
local function safe_date(f, t)
|
||||
if not f then
|
||||
-- fall back to old behavior
|
||||
|
@ -95,7 +87,6 @@ local mp=minetest.get_modpath("advtrains_luaautomation")
|
|||
|
||||
local static_env = {
|
||||
--core LUA functions
|
||||
print = safe_print,
|
||||
string = {
|
||||
byte = string.byte,
|
||||
char = string.char,
|
||||
|
@ -252,7 +243,6 @@ for _, name in pairs(safe_globals) do
|
|||
static_env[name] = _G[name]
|
||||
end
|
||||
|
||||
|
||||
--The environment all code calls get is a table that has set static_env as metatable.
|
||||
--In general, every variable is local to a single code chunk, but kept persistent over code re-runs. Data is also saved, but functions and userdata and circular references are removed
|
||||
--Init code and step code's environments are not saved
|
||||
|
@ -265,6 +255,14 @@ local proxy_env={}
|
|||
|
||||
-- returns: true, fenv if successful; nil, error if error
|
||||
function env_proto:execute_code(localenv, code, evtdata, customfct)
|
||||
-- create us a print function specific for this environment
|
||||
if not self.safe_print_func then
|
||||
local myenv = self
|
||||
self.safe_print_func = function(...)
|
||||
myenv:log("info", ...)
|
||||
end
|
||||
end
|
||||
|
||||
local metatbl ={
|
||||
__index = function(t, i)
|
||||
if i=="S" then
|
||||
|
@ -277,6 +275,8 @@ function env_proto:execute_code(localenv, code, evtdata, customfct)
|
|||
return customfct[i]
|
||||
elseif localenv and localenv[i] then
|
||||
return localenv[i]
|
||||
elseif i=="print" then
|
||||
return self.safe_print_func
|
||||
end
|
||||
return static_env[i]
|
||||
end,
|
||||
|
@ -306,35 +306,39 @@ function env_proto:run_initcode()
|
|||
if self.init_code and self.init_code~="" then
|
||||
local old_fdata=self.fdata
|
||||
self.fdata = {}
|
||||
atprint("[atlatc]Running initialization code for environment '"..self.name.."'")
|
||||
--atprint("[atlatc]Running initialization code for environment '"..self.name.."'")
|
||||
local succ, err = self:execute_code({}, self.init_code, {type="init", init=true})
|
||||
if not succ then
|
||||
atwarn("[atlatc]Executing InitCode for '"..self.name.."' failed:"..err)
|
||||
self:log("error", "Executing InitCode for '"..self.name.."' failed:"..err)
|
||||
self.init_err=err
|
||||
if old_fdata then
|
||||
self.fdata=old_fdata
|
||||
atwarn("[atlatc]The 'F' table has been restored to the previous state.")
|
||||
self:log("warning", "The 'F' table has been restored to the previous state.")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
function env_proto:run_stepcode()
|
||||
if self.step_code and self.step_code~="" then
|
||||
local succ, err = self:execute_code({}, self.step_code, nil, {})
|
||||
if not succ then
|
||||
--TODO
|
||||
end
|
||||
|
||||
-- log to environment subscribers. severity can be "error", "warning" or "info" (used by internal print)
|
||||
function env_proto:log(severity, ...)
|
||||
local text=advtrains.print_concat_table({"[atlatc "..self.name.." "..severity.."]", ...})
|
||||
minetest.log("action", text)
|
||||
for _, pname in ipairs(self.subscribers) do
|
||||
minetest.chat_send_player(pname, text)
|
||||
end
|
||||
end
|
||||
|
||||
-- env.subscribers table may be directly altered by callers.
|
||||
|
||||
|
||||
--- class interface
|
||||
|
||||
function atlatc.env_new(name)
|
||||
local newenv={
|
||||
name=name,
|
||||
init_code="",
|
||||
step_code="",
|
||||
sdata={}
|
||||
sdata={},
|
||||
subscribers={},
|
||||
}
|
||||
setmetatable(newenv, {__index=env_proto})
|
||||
return newenv
|
||||
|
@ -351,11 +355,6 @@ function atlatc.run_initcode()
|
|||
env:run_initcode()
|
||||
end
|
||||
end
|
||||
function atlatc.run_stepcode()
|
||||
for envname, env in pairs(atlatc.envs) do
|
||||
env:run_stepcode()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -16,6 +16,30 @@ function iq.save()
|
|||
return {queue = queue, timer=timer}
|
||||
end
|
||||
|
||||
function iq.has_at_pos(pos)
|
||||
for i=1,#queue do
|
||||
local qe=queue[i]
|
||||
if vector.equals(pos, qe.p) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function iq.clear_ints_at_pos(pos)
|
||||
local i=1
|
||||
while i<=#queue do
|
||||
local qe=queue[i]
|
||||
if not qe then
|
||||
table.remove(queue, i)
|
||||
elseif vector.equals(pos, qe.p) and (qe.e.int or qe.e.ext_int) then
|
||||
table.remove(queue, i)
|
||||
else
|
||||
i=i+1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function iq.add(t, pos, evtdata)
|
||||
queue[#queue+1]={t=t+timer, p=pos, e=evtdata}
|
||||
run=true
|
||||
|
@ -23,13 +47,14 @@ end
|
|||
|
||||
function iq.mainloop(dtime)
|
||||
timer=timer + math.min(dtime, 0.2)
|
||||
for i=1,#queue do
|
||||
local i=1
|
||||
while i<=#queue do
|
||||
local qe=queue[i]
|
||||
if not qe then
|
||||
table.remove(queue, i)
|
||||
i=i-1
|
||||
elseif timer>qe.t then
|
||||
local pos, evtdata=queue[i].p, queue[i].e
|
||||
table.remove(queue, i)
|
||||
local pos, evtdata=qe.p, qe.e
|
||||
local node=advtrains.ndb.get_node(pos)
|
||||
local ndef=minetest.registered_nodes[node.name]
|
||||
if ndef and ndef.luaautomation and ndef.luaautomation.fire_event then
|
||||
|
@ -37,8 +62,8 @@ function iq.mainloop(dtime)
|
|||
else
|
||||
atwarn("[atlatc][interrupt] Couldn't run event",evtdata.type,"on",pos,", something wrong with the node",node)
|
||||
end
|
||||
table.remove(queue, i)
|
||||
i=i-1
|
||||
else
|
||||
i=i+1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,7 +7,7 @@ Displays
|
|||
Mesecon Transmitter
|
||||
Those passive components can also be used inside interlocking systems.
|
||||
|
||||
All passive components have a table called 'advtrains' in their node definition and have the group 'save_in_nodedb' set, so they work in unloaded chunks.
|
||||
All passive components have a table called 'advtrains' in their node definition and have the group 'save_in_at_nodedb' set, so they work in unloaded chunks.
|
||||
Example for a switch:
|
||||
advtrains = {
|
||||
getstate = function(pos, node)
|
||||
|
|
|
@ -6,14 +6,14 @@
|
|||
|
||||
local setaspectf = function(rot)
|
||||
return function(pos, node, asp)
|
||||
if not asp.main.free then
|
||||
if asp.shunt.free then
|
||||
if asp.main == 0 then
|
||||
if asp.shunt then
|
||||
advtrains.ndb.swap_node(pos, {name="advtrains_signals_ks:hs_shunt_"..rot, param2 = node.param2})
|
||||
else
|
||||
advtrains.ndb.swap_node(pos, {name="advtrains_signals_ks:hs_danger_"..rot, param2 = node.param2})
|
||||
end
|
||||
else
|
||||
if asp.dst.free and asp.main.speed == -1 then
|
||||
if asp.dst ~= 0 and asp.main == -1 then
|
||||
advtrains.ndb.swap_node(pos, {name="advtrains_signals_ks:hs_free_"..rot, param2 = node.param2})
|
||||
else
|
||||
advtrains.ndb.swap_node(pos, {name="advtrains_signals_ks:hs_slow_"..rot, param2 = node.param2})
|
||||
|
@ -22,19 +22,12 @@ local setaspectf = function(rot)
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
local suppasp = {
|
||||
main = {
|
||||
free = nil,
|
||||
speed = {6, -1},
|
||||
},
|
||||
dst = {
|
||||
free = nil,
|
||||
speed = nil,
|
||||
},
|
||||
shunt = {
|
||||
free = nil,
|
||||
proceed_as_main = true,
|
||||
},
|
||||
main = {0, 6, -1},
|
||||
dst = {0, false},
|
||||
shunt = nil,
|
||||
proceed_as_main = true,
|
||||
info = {
|
||||
call_on = false,
|
||||
dead_end = false,
|
||||
|
@ -45,7 +38,7 @@ local suppasp = {
|
|||
--Rangiersignal
|
||||
local setaspectf_ra = function(rot)
|
||||
return function(pos, node, asp)
|
||||
if asp.shunt.free then
|
||||
if asp.shunt then
|
||||
advtrains.ndb.swap_node(pos, {name="advtrains_signals_ks:ra_shuntd_"..rot, param2 = node.param2})
|
||||
else
|
||||
advtrains.ndb.swap_node(pos, {name="advtrains_signals_ks:ra_danger_"..rot, param2 = node.param2})
|
||||
|
@ -58,17 +51,11 @@ local setaspectf_ra = function(rot)
|
|||
end
|
||||
|
||||
local suppasp_ra = {
|
||||
main = {
|
||||
free = true,
|
||||
},
|
||||
dst = {
|
||||
free = nil,
|
||||
speed = nil,
|
||||
},
|
||||
shunt = {
|
||||
free = nil,
|
||||
proceed_as_main = false,
|
||||
},
|
||||
main = { false },
|
||||
dst = { false },
|
||||
shunt = nil,
|
||||
proceed_as_main = false,
|
||||
|
||||
info = {
|
||||
call_on = false,
|
||||
dead_end = false,
|
||||
|
@ -90,9 +77,9 @@ for _, rtab in ipairs({
|
|||
local rot = rtab.rot
|
||||
for typ, prts in pairs({
|
||||
danger = {asp = advtrains.interlocking.DANGER, n = "slow", ici=true},
|
||||
slow = {asp = { main = { free = true, speed = 6 }, shunt = {proceed_as_main = true}} , n = "free"},
|
||||
free = {asp = { main = { free = true, speed = -1 }, shunt = {proceed_as_main = true}} , n = "shunt"},
|
||||
shunt = {asp = { main = {free = false}, shunt = {free = true} } , n = "danger"},
|
||||
slow = {asp = { main = 6, proceed_as_main = true} , n = "free"},
|
||||
free = {asp = { main = -1, proceed_as_main = true} , n = "shunt"},
|
||||
shunt = {asp = { main = 0, shunt = true} , n = "danger"},
|
||||
}) do
|
||||
minetest.register_node("advtrains_signals_ks:hs_"..typ.."_"..rot, {
|
||||
description = "Ks Main Signal",
|
||||
|
@ -136,8 +123,8 @@ for _, rtab in ipairs({
|
|||
|
||||
--Rangiersignale:
|
||||
for typ, prts in pairs({
|
||||
danger = {asp = { main = {free = true}, shunt = {free = false} }, n = "shuntd", ici=true},
|
||||
shuntd = {asp = { main = {free = true}, shunt = {free = true} } , n = "danger"},
|
||||
danger = {asp = { main = false, shunt = false }, n = "shuntd", ici=true},
|
||||
shuntd = {asp = { main = false, shunt = true } , n = "danger"},
|
||||
}) do
|
||||
minetest.register_node("advtrains_signals_ks:ra_"..typ.."_"..rot, {
|
||||
description = "Ks Shunting Signal",
|
||||
|
@ -181,13 +168,14 @@ for _, rtab in ipairs({
|
|||
--Schilder:
|
||||
for typ, prts in pairs({
|
||||
-- Speed restrictions:
|
||||
["8"] = {asp = { main = {free = true, speed = 8}, shunt = {free = true} }, n = "12", ici=true},
|
||||
["12"] = {asp = { main = {free = true, speed = 12}, shunt = {free = true} }, n = "16"},
|
||||
["16"] = {asp = { main = {free = true, speed = 16}, shunt = {free = true} }, n = "e"},
|
||||
["8"] = {asp = { main = 8, shunt = true }, n = "12", ici=true},
|
||||
["12"] = {asp = { main = 12, shunt = true }, n = "16"},
|
||||
["16"] = {asp = { main = 16, shunt = true }, n = "e"},
|
||||
-- Speed restriction lifted
|
||||
["e"] = {asp = { main = {free = true, speed = -1}, shunt = {free = true} }, n = "hfs"},
|
||||
["e"] = {asp = { main = -1, shunt = true }, n = "hfs"},
|
||||
-- Halt for shunt moves:
|
||||
["hfs"] = {asp = { main = {free = true}, shunt = {free = false} }, n = "8"},
|
||||
["hfs"] = {asp = { main = false, shunt = false }, n = "pam"},
|
||||
["pam"] = {asp = { main = -1, shunt = false, proceed_as_main = true}, n = "8"},
|
||||
}) do
|
||||
minetest.register_node("advtrains_signals_ks:sign_"..typ.."_"..rot, {
|
||||
description = "Signal Sign",
|
||||
|
@ -211,7 +199,7 @@ for _, rtab in ipairs({
|
|||
save_in_at_nodedb = 1,
|
||||
not_in_creative_inventory = (rtab.ici and prts.ici) and 0 or 1,
|
||||
},
|
||||
drop = "advtrains_signals_ks:sign_e_0",
|
||||
drop = "advtrains_signals_ks:sign_8_0",
|
||||
inventory_image = "advtrains_signals_ks_sign_8.png",
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
advtrains = {
|
||||
|
@ -253,3 +241,40 @@ for _, rtab in ipairs({
|
|||
advtrains.trackplacer.add_worked("advtrains_signals_ks:mast","mast", "_"..rot)
|
||||
end
|
||||
|
||||
-- Crafting
|
||||
|
||||
minetest.register_craft({
|
||||
output = "advtrains_signals_ks:hs_danger_0 2",
|
||||
recipe = {
|
||||
{'default:steel_ingot', 'dye:red', 'default:steel_ingot'},
|
||||
{'dye:yellow', 'default:steel_ingot', 'dye:dark_green'},
|
||||
{'default:steel_ingot', 'advtrains_signals_ks:mast_mast_0', 'default:steel_ingot'},
|
||||
},
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "advtrains_signals_ks:mast_mast_0 10",
|
||||
recipe = {
|
||||
{'default:steel_ingot'},
|
||||
{'dye:cyan'},
|
||||
{'default:steel_ingot'},
|
||||
},
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "advtrains_signals_ks:ra_danger_0 2",
|
||||
recipe = {
|
||||
{'dye:red', 'dye:white', 'dye:red'},
|
||||
{'dye:white', 'default:steel_ingot', 'default:steel_ingot'},
|
||||
{'default:steel_ingot', 'advtrains_signals_ks:mast_mast_0', 'default:steel_ingot'},
|
||||
},
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "advtrains_signals_ks:sign_8_0 2",
|
||||
recipe = {
|
||||
{'basic_materials:plastic_sheet', 'dye:black'},
|
||||
{'default:stick', ''},
|
||||
{'default:stick', ''},
|
||||
},
|
||||
})
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
-- Ks Signals for advtrains
|
||||
-- will implement the advtrains signal API (which does not exist yet)
|
||||
-- will implement the advtrains signal API
|
||||
|
||||
local function place_degrotate(pos, placer, itemstack, pointed_thing)
|
||||
local yaw = placer:get_look_horizontal()
|
||||
|
|
After Width: | Height: | Size: 211 B |
|
@ -105,3 +105,45 @@ advtrains.register_wagon("wagon_wood", {
|
|||
box=8*3,
|
||||
},
|
||||
}, S("Industrial wood wagon"), "advtrains_wagon_wood_inv.png")
|
||||
|
||||
-- Craftings
|
||||
|
||||
minetest.register_craft({
|
||||
output = 'advtrains:engine_industrial',
|
||||
recipe = {
|
||||
{'default:steelblock', 'default:steelblock', 'default:steelblock'},
|
||||
{'advtrains:driver_cab', 'default:steelblock', 'default:steelblock'},
|
||||
{'advtrains:wheel', '', 'advtrains:wheel'},
|
||||
},
|
||||
})
|
||||
|
||||
--Engine Industrial Big
|
||||
minetest.register_craft({
|
||||
output = 'advtrains:engine_industrial_big',
|
||||
recipe = {
|
||||
{'default:glass', 'default:steelblock', 'default:steelblock'},
|
||||
{'advtrains:driver_cab', 'default:steelblock', 'default:steelblock'},
|
||||
{'advtrains:wheel', 'advtrains:wheel', 'advtrains:wheel'},
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
--Industrial tank wagon
|
||||
minetest.register_craft({
|
||||
output = 'advtrains:wagon_tank',
|
||||
recipe = {
|
||||
{'default:steelblock', 'default:steel_ingot', 'default:steelblock'},
|
||||
{'advtrains:steelblock', '', 'default:steelblock'},
|
||||
{'advtrains:wheel', 'default:steelblock', 'advtrains:wheel'},
|
||||
},
|
||||
})
|
||||
|
||||
--Industrial wood wagon
|
||||
minetest.register_craft({
|
||||
output = 'advtrains:wagon_wood',
|
||||
recipe = {
|
||||
{'default:steel_ingot', '', 'default:steel_ingot'},
|
||||
{'advtrains:steelblock', 'default:steelblock', 'default:steelblock'},
|
||||
{'advtrains:wheel', '', 'advtrains:wheel'},
|
||||
},
|
||||
})
|
||||
|
|
|
@ -138,3 +138,25 @@ advtrains.register_wagon("wagon_japan", {
|
|||
drops={"default:steelblock 4"},
|
||||
}, S("Japanese Train Wagon"), "advtrains_wagon_japan_inv.png")
|
||||
|
||||
-- Crafting
|
||||
|
||||
minetest.register_craft({
|
||||
output = 'advtrains:engine_japan',
|
||||
recipe = {
|
||||
{'default:steelblock', 'default:steelblock', ''},
|
||||
{'xpanes:pane_flat', 'default:steelblock', 'xpanes:pane_flat'},
|
||||
{'advtrains:wheel', 'advtrains:wheel', 'advtrains:wheel'},
|
||||
},
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = 'advtrains:wagon_japan',
|
||||
recipe = {
|
||||
{'default:steelblock', 'default:steelblock', 'default:steelblock'},
|
||||
{'xpanes:pane_flat', 'default:steelblock', 'xpanes:pane_flat'},
|
||||
{'advtrains:wheel', '', 'advtrains:wheel'},
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -298,6 +298,29 @@ advtrains.register_tracks("default", {
|
|||
end
|
||||
}, advtrains.trackpresets.t_30deg_straightonly)
|
||||
|
||||
minetest.register_craft({
|
||||
type="shapeless",
|
||||
output = 'advtrains:dtrack_load_placer',
|
||||
recipe = {
|
||||
"advtrains:dtrack_placer",
|
||||
"basic_materials:ic",
|
||||
"default:chest"
|
||||
},
|
||||
})
|
||||
minetest.register_craft({
|
||||
type="shapeless",
|
||||
output = 'advtrains:dtrack_unload_placer',
|
||||
recipe = {
|
||||
"advtrains:dtrack_load_placer",
|
||||
},
|
||||
})
|
||||
minetest.register_craft({
|
||||
type="shapeless",
|
||||
output = 'advtrains:dtrack_load_placer',
|
||||
recipe = {
|
||||
"advtrains:dtrack_unload_placer",
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
if mesecon then
|
||||
|
@ -318,9 +341,14 @@ if mesecon then
|
|||
}
|
||||
},
|
||||
advtrains = {
|
||||
on_updated_from_nodedb = function(pos, node)
|
||||
mesecon.receptor_off(pos, advtrains.meseconrules)
|
||||
end,
|
||||
on_train_enter=function(pos, train_id)
|
||||
advtrains.ndb.swap_node(pos, {name="advtrains:dtrack_detector_on".."_"..suffix..rotation, param2=advtrains.ndb.get_node(pos).param2})
|
||||
mesecon.receptor_on(pos, advtrains.meseconrules)
|
||||
if advtrains.is_node_loaded(pos) then
|
||||
mesecon.receptor_on(pos, advtrains.meseconrules)
|
||||
end
|
||||
end
|
||||
}
|
||||
}
|
||||
|
@ -343,15 +371,20 @@ if mesecon then
|
|||
}
|
||||
},
|
||||
advtrains = {
|
||||
on_updated_from_nodedb = function(pos, node)
|
||||
mesecon.receptor_on(pos, advtrains.meseconrules)
|
||||
end,
|
||||
on_train_leave=function(pos, train_id)
|
||||
advtrains.ndb.swap_node(pos, {name="advtrains:dtrack_detector_off".."_"..suffix..rotation, param2=advtrains.ndb.get_node(pos).param2})
|
||||
mesecon.receptor_off(pos, advtrains.meseconrules)
|
||||
if advtrains.is_node_loaded(pos) then
|
||||
mesecon.receptor_off(pos, advtrains.meseconrules)
|
||||
end
|
||||
end
|
||||
}
|
||||
}
|
||||
end
|
||||
}, advtrains.ap.t_30deg_straightonly_noplacer)
|
||||
minetest.register_craft({
|
||||
minetest.register_craft({
|
||||
type="shapeless",
|
||||
output = 'advtrains:dtrack_detector_off_placer',
|
||||
recipe = {
|
||||
|
|
|
@ -72,12 +72,15 @@ If the train drives in the 'wrong' direction, stop and reverse; independently ac
|
|||
I<8 S8 ;
|
||||
If the train is slower than 8, accelerate to 8.
|
||||
|
||||
# ATC controller operation modes
|
||||
static: Only give 1 static command.
|
||||
# Interlocking
|
||||
|
||||
mesecon: Give 2 different commands depending on if the controller is mesecon-powered or not
|
||||
digiline: Don't give any commands by itself. When a train passes, a digiline message in the form of "[+/-][speed]" is sent on the set channel (where +/- means the same as with conditions). Any digiline message sent to the controller will be interpreted as ATC command and sent to the train.
|
||||
** the latter two are not yet implemented.
|
||||
With advtrains_interlocking, there's one more available command:
|
||||
|
||||
A0
|
||||
Disable ARS on the train.
|
||||
A1
|
||||
Enable ARS on the train.
|
||||
When disabled, the train will not trigger automatic route setting on signals based on ARS.
|
||||
|
||||
# Persistence
|
||||
ATC controllers that are configured as 'static' or 'mesecon' are persistent over mapblock unloads and will even command the train when the mapblock is unloaded. This is not possible with digilines since these do not work in unloaded mapchunks.
|
||||
|
|
|
@ -125,7 +125,7 @@ local get_ambience = function(player, tod, name)
|
|||
|
||||
|
||||
-- get foot and head level nodes at player position
|
||||
local pos = player:get_pos()
|
||||
local pos = player:get_pos() ; if not pos then return end
|
||||
|
||||
pos.y = pos.y + 1.4 -- head level
|
||||
|
||||
|
|
|
@ -113,7 +113,7 @@ for _, nodeclass in ipairs(NodeClass) do
|
|||
paramtype = "light",
|
||||
paramtype2 = "color",
|
||||
is_ground_content = true,
|
||||
groups = {cracky=3, ud_param2_colorable = 1},
|
||||
groups = {cracky=3, stone=1, ud_param2_colorable = 1},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
on_construct = unifieddyes.on_construct,
|
||||
on_dig = unifieddyes.on_dig
|
||||
|
@ -131,7 +131,7 @@ for _, nodeclass in ipairs(NodeClass) do
|
|||
paramtype = "light",
|
||||
paramtype2 = "color",
|
||||
is_ground_content = true,
|
||||
groups = {cracky=3, ud_param2_colorable = 1},
|
||||
groups = {cracky=3, stone=2, ud_param2_colorable = 1},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
on_construct = unifieddyes.on_construct,
|
||||
on_dig = unifieddyes.on_dig
|
||||
|
@ -149,7 +149,7 @@ for _, nodeclass in ipairs(NodeClass) do
|
|||
paramtype = "light",
|
||||
paramtype2 = "color",
|
||||
is_ground_content = true,
|
||||
groups = {snappy=2,choppy=2,oddly_breakable_by_hand=2,flammable=3, ud_param2_colorable = 1},
|
||||
groups = {snappy=2, choppy=2, wood=1, oddly_breakable_by_hand=2,flammable=3, ud_param2_colorable = 1},
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
on_construct = unifieddyes.on_construct,
|
||||
on_dig = unifieddyes.on_dig
|
||||
|
@ -169,7 +169,7 @@ minetest.register_node("blox:wood_tinted", {
|
|||
paramtype = "light",
|
||||
paramtype2 = "color",
|
||||
is_ground_content = true,
|
||||
groups = {snappy=2,choppy=2,oddly_breakable_by_hand=2,flammable=3, ud_param2_colorable = 1},
|
||||
groups = {snappy=2, choppy=2, wood=1, oddly_breakable_by_hand=2,flammable=3, ud_param2_colorable = 1},
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
on_construct = unifieddyes.on_construct,
|
||||
on_dig = unifieddyes.on_dig
|
||||
|
@ -182,7 +182,7 @@ minetest.register_node("blox:stone_square", {
|
|||
paramtype = "light",
|
||||
paramtype2 = "color",
|
||||
is_ground_content = true,
|
||||
groups = {snappy=2,choppy=2,oddly_breakable_by_hand=2,flammable=3, ud_param2_colorable = 1},
|
||||
groups = {snappy=2, choppy=2, stone=1, oddly_breakable_by_hand=2,flammable=3, ud_param2_colorable = 1},
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
on_construct = unifieddyes.on_construct,
|
||||
on_dig = unifieddyes.on_dig
|
||||
|
@ -195,7 +195,7 @@ minetest.register_node("blox:cobble_tinted", {
|
|||
paramtype = "light",
|
||||
paramtype2 = "color",
|
||||
is_ground_content = true,
|
||||
groups = {snappy=2,choppy=2,oddly_breakable_by_hand=2,flammable=3, not_in_creative_inventory = 1, ud_param2_colorable = 1},
|
||||
groups = {snappy=2, choppy=2, stone=2, oddly_breakable_by_hand=2,flammable=3, not_in_creative_inventory = 1, ud_param2_colorable = 1},
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
on_construct = unifieddyes.on_construct,
|
||||
on_dig = unifieddyes.on_dig
|
||||
|
@ -208,7 +208,7 @@ minetest.register_node("blox:stone_tinted", {
|
|||
paramtype = "light",
|
||||
paramtype2 = "color",
|
||||
is_ground_content = true,
|
||||
groups = {snappy=2,choppy=2,oddly_breakable_by_hand=2,flammable=3, not_in_creative_inventory = 1, ud_param2_colorable = 1},
|
||||
groups = {snappy=2, choppy=2, stone=1, oddly_breakable_by_hand=2,flammable=3, not_in_creative_inventory = 1, ud_param2_colorable = 1},
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
on_construct = unifieddyes.on_construct,
|
||||
on_dig = unifieddyes.on_dig,
|
||||
|
|
|
@ -63,24 +63,22 @@ local dry_grass = {
|
|||
"default:dry_grass_5", "", ""
|
||||
}
|
||||
|
||||
-- add all in-game flowers except waterlily
|
||||
-- loads mods then add all in-game flowers except waterlily
|
||||
local flowers = {}
|
||||
|
||||
for node, def in pairs(minetest.registered_nodes) do
|
||||
minetest.after(0.1, function()
|
||||
|
||||
if def.groups.flower and not node:find("waterlily") then
|
||||
flowers[#flowers + 1] = node
|
||||
for node, def in pairs(minetest.registered_nodes) do
|
||||
|
||||
if def.groups
|
||||
and def.groups.flower
|
||||
and not node:find("waterlily")
|
||||
and not node:find("xdecor:potted_") then
|
||||
flowers[#flowers + 1] = node
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
-- add additional bakedclay flowers if enabled
|
||||
if minetest.get_modpath("bakedclay") then
|
||||
flowers[#flowers + 1] = "bakedclay:delphinium"
|
||||
flowers[#flowers + 1] = "bakedclay:thistle"
|
||||
flowers[#flowers + 1] = "bakedclay:lazarus"
|
||||
flowers[#flowers + 1] = "bakedclay:mannagrass"
|
||||
flowers[#flowers + 1] = ""
|
||||
end
|
||||
|
||||
-- default biomes deco
|
||||
local deco = {
|
||||
|
|
|
@ -4,7 +4,7 @@ edited by TenPlus1
|
|||
|
||||
Features:
|
||||
- Items are destroyed by lava
|
||||
- Items are pushed along by flowing water (thanks to QwertyMine3)
|
||||
- Items are pushed along by flowing water (thanks to QwertyMine3 and Gustavo6046)
|
||||
- Items are removed after 900 seconds or the time that is specified by
|
||||
remove_items in minetest.conf (-1 disables it)
|
||||
- Particle effects added
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
-- Minetest: builtin/item_entity.lua
|
||||
|
||||
-- override ice to make slippery for 0.4.16
|
||||
minetest.override_item("default:ice", {
|
||||
groups = {cracky = 3, puts_out_fire = 1, cools_lava = 1, slippery = 3}})
|
||||
if not minetest.raycast then
|
||||
minetest.override_item("default:ice", {
|
||||
groups = {cracky = 3, puts_out_fire = 1, cools_lava = 1, slippery = 3}})
|
||||
end
|
||||
|
||||
|
||||
function core.spawn_item(pos, item)
|
||||
|
@ -20,30 +22,37 @@ end
|
|||
|
||||
-- If item_entity_ttl is not set, enity will have default life time
|
||||
-- Setting it to -1 disables the feature
|
||||
|
||||
local time_to_live = tonumber(core.settings:get("item_entity_ttl")) or 900
|
||||
local gravity = tonumber(core.settings:get("movement_gravity")) or 9.81
|
||||
local destroy_item = core.settings:get_bool("destroy_item") ~= false
|
||||
|
||||
|
||||
-- water flow functions by QwertyMine3, edited by TenPlus1
|
||||
-- localize some math functions
|
||||
local abs = math.abs
|
||||
local sqrt = math.sqrt
|
||||
|
||||
|
||||
-- water flow functions by QwertyMine3, edited by TenPlus1 and Gustavo6046
|
||||
local inv_roots = {[0] = 1}
|
||||
|
||||
local function to_unit_vector(dir_vector)
|
||||
|
||||
local inv_roots = {
|
||||
[0] = 1,
|
||||
[1] = 1,
|
||||
[2] = 0.70710678118655,
|
||||
[4] = 0.5,
|
||||
[5] = 0.44721359549996,
|
||||
[8] = 0.35355339059327
|
||||
}
|
||||
|
||||
local sum = dir_vector.x * dir_vector.x + dir_vector.z * dir_vector.z
|
||||
local invr_sum
|
||||
|
||||
-- find inverse square root if possible
|
||||
if inv_roots[sum] ~= nil then
|
||||
invr_sum = inv_roots[sum]
|
||||
else
|
||||
-- not found, compute and save the inverse square root
|
||||
invr_sum = 1.0 / sqrt(sum)
|
||||
inv_roots[sum] = invr_sum
|
||||
end
|
||||
|
||||
return {
|
||||
x = dir_vector.x * inv_roots[sum],
|
||||
x = dir_vector.x * invr_sum,
|
||||
y = dir_vector.y,
|
||||
z = dir_vector.z * inv_roots[sum]
|
||||
z = dir_vector.z * invr_sum
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -63,6 +72,11 @@ end
|
|||
local function quick_flow_logic(node, pos_testing, direction)
|
||||
|
||||
local node_testing = node_ok(pos_testing)
|
||||
local param2 = node.param2
|
||||
|
||||
if not minetest.registered_nodes[node.name].groups.liquid then
|
||||
param2 = 0
|
||||
end
|
||||
|
||||
if minetest.registered_nodes[node_testing.name].liquidtype ~= "flowing"
|
||||
and minetest.registered_nodes[node_testing.name].liquidtype ~= "source" then
|
||||
|
@ -71,17 +85,17 @@ local function quick_flow_logic(node, pos_testing, direction)
|
|||
|
||||
local param2_testing = node_testing.param2
|
||||
|
||||
if param2_testing < node.param2 then
|
||||
if param2_testing < param2 then
|
||||
|
||||
if (node.param2 - param2_testing) > 6 then
|
||||
if (param2 - param2_testing) > 6 then
|
||||
return -direction
|
||||
else
|
||||
return direction
|
||||
end
|
||||
|
||||
elseif param2_testing > node.param2 then
|
||||
elseif param2_testing > param2 then
|
||||
|
||||
if (param2_testing - node.param2) > 6 then
|
||||
if (param2_testing - param2) > 6 then
|
||||
return direction
|
||||
else
|
||||
return -direction
|
||||
|
@ -94,11 +108,7 @@ end
|
|||
|
||||
local function quick_flow(pos, node)
|
||||
|
||||
if not minetest.registered_nodes[node.name].groups.liquid then
|
||||
return {x = 0, y = 0, z = 0}
|
||||
end
|
||||
|
||||
local x, z = 0, 0
|
||||
local x, z = 0.0, 0.0
|
||||
|
||||
x = x + quick_flow_logic(node, {x = pos.x - 1, y = pos.y, z = pos.z},-1)
|
||||
x = x + quick_flow_logic(node, {x = pos.x + 1, y = pos.y, z = pos.z}, 1)
|
||||
|
@ -107,7 +117,6 @@ local function quick_flow(pos, node)
|
|||
|
||||
return to_unit_vector({x = x, y = 0, z = z})
|
||||
end
|
||||
-- END water flow functions
|
||||
|
||||
|
||||
-- particle effects for when item is destroyed
|
||||
|
@ -130,6 +139,19 @@ local function add_effects(pos)
|
|||
})
|
||||
end
|
||||
|
||||
-- print vector, helpful when debugging
|
||||
local function vec_print(head, vec)
|
||||
print(head, vec.x, vec.y, vec.z)
|
||||
end
|
||||
|
||||
|
||||
local water_force = tonumber(minetest.settings:get("builtin_item.waterflow_force") or 1.6)
|
||||
local water_drag = tonumber(minetest.settings:get("builtin_item.waterflow_drag") or 0.8)
|
||||
local dry_friction = tonumber(minetest.settings:get("builtin_item.friction_dry") or 2.5)
|
||||
local air_drag = tonumber(minetest.settings:get("builtin_item.air_drag") or 0.4)
|
||||
local items_collect_on_slippery = tonumber(
|
||||
minetest.settings:get("builtin_item.items_collect_on_slippery") or 1) ~= 0
|
||||
|
||||
|
||||
core.register_entity(":__builtin:item", {
|
||||
|
||||
|
@ -144,14 +166,17 @@ core.register_entity(":__builtin:item", {
|
|||
spritediv = {x = 1, y = 1},
|
||||
initial_sprite_basepos = {x = 0, y = 0},
|
||||
is_visible = false,
|
||||
infotext = "",
|
||||
infotext = ""
|
||||
},
|
||||
|
||||
itemstring = "",
|
||||
moving_state = true,
|
||||
falling_state = true,
|
||||
slippery_state = false,
|
||||
waterflow_state = false,
|
||||
age = 0,
|
||||
|
||||
accel = {x = 0, y = 0, z = 0},
|
||||
|
||||
set_item = function(self, item)
|
||||
|
||||
local stack = ItemStack(item or self.itemstring)
|
||||
|
@ -172,11 +197,12 @@ core.register_entity(":__builtin:item", {
|
|||
local c1, c2 = "",""
|
||||
|
||||
if not(stack:get_count() == 1) then
|
||||
c1 = " x"..tostring(stack:get_count())
|
||||
c2 = " "..tostring(stack:get_count())
|
||||
c1 = " x" .. tostring(stack:get_count())
|
||||
c2 = " " .. tostring(stack:get_count())
|
||||
end
|
||||
|
||||
local name1 = stack:get_meta():get_string("description")
|
||||
local name
|
||||
|
||||
if name1 == "" then
|
||||
name = core.registered_items[itemname].description
|
||||
|
@ -184,28 +210,33 @@ core.register_entity(":__builtin:item", {
|
|||
name = name1
|
||||
end
|
||||
|
||||
-- small random size bias to counter Z-fighting
|
||||
local bias = math.random() * 1e-3
|
||||
|
||||
self.object:set_properties({
|
||||
is_visible = true,
|
||||
visual = "wielditem",
|
||||
textures = {itemname},
|
||||
visual_size = {x = size, y = size},
|
||||
visual_size = {x = size + bias, y = size + bias, z = size + bias},
|
||||
collisionbox = {-size, -col_height, -size, size, col_height, size},
|
||||
selectionbox = {-size, -size, -size, size, size, size},
|
||||
automatic_rotate = 0.314 / size,
|
||||
wield_item = self.itemstring,
|
||||
glow = glow,
|
||||
infotext = name..c1.."\n("..itemname..c2..")"
|
||||
infotext = name .. c1 .. "\n(" .. itemname .. c2 .. ")"
|
||||
})
|
||||
|
||||
end,
|
||||
|
||||
get_staticdata = function(self)
|
||||
|
||||
return core.serialize({
|
||||
local data = {
|
||||
itemstring = self.itemstring,
|
||||
age = self.age,
|
||||
dropped_by = self.dropped_by
|
||||
})
|
||||
}
|
||||
|
||||
return core.serialize(data)
|
||||
end,
|
||||
|
||||
on_activate = function(self, staticdata, dtime_s)
|
||||
|
@ -224,8 +255,6 @@ core.register_entity(":__builtin:item", {
|
|||
end
|
||||
|
||||
self.object:set_armor_groups({immortal = 1})
|
||||
self.object:set_velocity({x = 0, y = 2, z = 0})
|
||||
self.object:set_acceleration({x = 0, y = -gravity, z = 0})
|
||||
self:set_item()
|
||||
end,
|
||||
|
||||
|
@ -255,11 +284,23 @@ core.register_entity(":__builtin:item", {
|
|||
|
||||
-- Merge the remote stack into this one
|
||||
local pos = object:get_pos()
|
||||
|
||||
pos.y = pos.y + ((total_count - count) / max_count) * 0.15
|
||||
|
||||
self.object:move_to(pos)
|
||||
self.age = 0 -- Handle as new entity
|
||||
|
||||
-- Merge velocities
|
||||
local vel_a = self.object:get_velocity()
|
||||
local vel_b = object:get_velocity()
|
||||
|
||||
self.object:set_velocity({
|
||||
x = (vel_a.x + vel_b.x) / 2,
|
||||
y = (vel_a.y + vel_b.y) / 2,
|
||||
z = (vel_a.z + vel_b.z) / 2
|
||||
})
|
||||
|
||||
-- Merge stacks
|
||||
own_stack:set_count(total_count)
|
||||
self:set_item(own_stack)
|
||||
|
||||
|
@ -269,22 +310,10 @@ core.register_entity(":__builtin:item", {
|
|||
return true
|
||||
end,
|
||||
|
||||
on_step = function(self, dtime)
|
||||
step_update_node_state = function(self, moveresult, dtime)
|
||||
|
||||
local pos = self.object:get_pos()
|
||||
|
||||
self.age = self.age + dtime
|
||||
|
||||
if time_to_live > 0 and self.age > time_to_live then
|
||||
|
||||
self.itemstring = ""
|
||||
self.object:remove()
|
||||
|
||||
add_effects(pos)
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
-- get nodes every 1/4 second
|
||||
self.timer = (self.timer or 0) + dtime
|
||||
|
||||
|
@ -294,18 +323,36 @@ core.register_entity(":__builtin:item", {
|
|||
self.def_inside = self.node_inside
|
||||
and core.registered_nodes[self.node_inside.name]
|
||||
|
||||
self.node_under = minetest.get_node_or_nil({
|
||||
x = pos.x,
|
||||
y = pos.y + self.object:get_properties().collisionbox[2] - 0.05,
|
||||
z = pos.z
|
||||
})
|
||||
-- get ground node for collision
|
||||
self.node_under = nil
|
||||
self.falling_state = true
|
||||
|
||||
if moveresult.touching_ground then
|
||||
|
||||
for _, info in ipairs(moveresult.collisions) do
|
||||
|
||||
if info.axis == "y" then
|
||||
|
||||
self.node_under = core.get_node(info.node_pos)
|
||||
self.falling_state = false
|
||||
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self.def_under = self.node_under
|
||||
and core.registered_nodes[self.node_under.name]
|
||||
and core.registered_nodes[self.node_under.name]
|
||||
|
||||
self.timer = 0
|
||||
end
|
||||
end,
|
||||
|
||||
step_node_inside_checks = function(self)
|
||||
|
||||
local pos = self.object:get_pos()
|
||||
local node = self.node_inside
|
||||
local def = self.def_inside
|
||||
|
||||
-- Delete in 'ignore' nodes
|
||||
if node and node.name == "ignore" then
|
||||
|
@ -313,24 +360,23 @@ core.register_entity(":__builtin:item", {
|
|||
self.itemstring = ""
|
||||
self.object:remove()
|
||||
|
||||
return
|
||||
return true
|
||||
end
|
||||
|
||||
-- do custom step function
|
||||
local name = ItemStack(self.itemstring):get_name() or ""
|
||||
local custom = core.registered_items[name]
|
||||
and core.registered_items[name].dropped_step
|
||||
-- item inside block, move to vacant space
|
||||
if def and (def.walkable == nil or def.walkable == true)
|
||||
and (def.collision_box == nil or def.collision_box.type == "regular")
|
||||
and (def.node_box == nil or def.node_box.type == "regular") then
|
||||
|
||||
if custom and custom(self, pos, dtime) == false then
|
||||
return -- skip further checks if false
|
||||
local npos = minetest.find_node_near(pos, 1, "air")
|
||||
|
||||
if npos then
|
||||
self.object:move_to(npos)
|
||||
end
|
||||
|
||||
self.node_inside = nil -- force get_node
|
||||
end
|
||||
|
||||
local vel = self.object:get_velocity()
|
||||
local def = self.def_inside
|
||||
local is_slippery = false
|
||||
local is_moving = (def and not def.walkable) or
|
||||
vel.x ~= 0 or vel.y ~= 0 or vel.z ~= 0
|
||||
|
||||
-- destroy item when dropped into lava (if enabled)
|
||||
if destroy_item and def and def.groups and def.groups.lava then
|
||||
|
||||
|
@ -345,81 +391,156 @@ core.register_entity(":__builtin:item", {
|
|||
|
||||
add_effects(pos)
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
return false
|
||||
end,
|
||||
|
||||
step_check_slippery = function(self)
|
||||
|
||||
-- don't check for slippery ground if we're not on
|
||||
-- any ground to begin with
|
||||
local node = self.node_under
|
||||
local def = self.def_under
|
||||
|
||||
if self.falling_state or not node then
|
||||
self.slippery_state = false
|
||||
return
|
||||
end
|
||||
|
||||
-- water flowing
|
||||
if def and def.liquidtype == "flowing" then
|
||||
|
||||
local vec = quick_flow(pos, node)
|
||||
local v = self.object:get_velocity()
|
||||
|
||||
self.object:set_velocity({x = vec.x, y = v.y, z = vec.z})
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
-- item inside block, move to vacant space
|
||||
if def and (def.walkable == nil or def.walkable == true)
|
||||
and (def.collision_box == nil or def.collision_box.type == "regular")
|
||||
and (def.node_box == nil or def.node_box.type == "regular") then
|
||||
|
||||
local npos = minetest.find_node_near(pos, 1, "air")
|
||||
|
||||
if npos then
|
||||
self.object:move_to(npos)
|
||||
end
|
||||
|
||||
self.node_inside = nil -- force get_node
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
-- Switch locals to node under
|
||||
node = self.node_under
|
||||
def = self.def_under
|
||||
|
||||
|
||||
-- Slippery node check
|
||||
if def and def.walkable then
|
||||
if node and def and def.walkable then
|
||||
|
||||
local slippery = core.get_item_group(node.name, "slippery")
|
||||
|
||||
is_slippery = slippery ~= 0
|
||||
self.slippery_state = slippery ~= 0
|
||||
end
|
||||
end,
|
||||
|
||||
if is_slippery and (math.abs(vel.x) > 0.2 or math.abs(vel.z) > 0.2) then
|
||||
step_water_physics = function(self)
|
||||
|
||||
-- Horizontal deceleration
|
||||
local pos = self.object:get_pos()
|
||||
local vel = self.object:get_velocity()
|
||||
local node = self.node_inside
|
||||
local def = self.def_inside
|
||||
|
||||
self.waterflow_state = def and def.liquidtype == "flowing"
|
||||
|
||||
if self.waterflow_state then
|
||||
|
||||
-- get flow velocity
|
||||
local flow_vel = quick_flow(pos, node)
|
||||
|
||||
-- calculate flow force and drag
|
||||
local flow_force_x = flow_vel.x * water_force
|
||||
local flow_force_z = flow_vel.z * water_force
|
||||
|
||||
local flow_drag_x = (flow_force_x - vel.x) * water_drag
|
||||
local flow_drag_z = (flow_force_z - vel.z) * water_drag
|
||||
|
||||
-- apply water force and friction
|
||||
self.accel.x = self.accel.x + flow_force_x + flow_drag_x
|
||||
self.accel.z = self.accel.z + flow_force_z + flow_drag_z
|
||||
end
|
||||
end,
|
||||
|
||||
step_air_drag_physics = function(self)
|
||||
|
||||
local vel = self.object:get_velocity()
|
||||
|
||||
-- apply air drag
|
||||
if self.falling_state or (self.slippery_state and not self.waterflow_state) then
|
||||
self.accel.x = self.accel.x - vel.x * air_drag
|
||||
self.accel.z = self.accel.z - vel.z * air_drag
|
||||
end
|
||||
end,
|
||||
|
||||
step_gravity = function(self)
|
||||
|
||||
if self.falling_state then
|
||||
self.accel.y = self.accel.y - gravity
|
||||
end
|
||||
end,
|
||||
|
||||
step_ground_friction = function(self)
|
||||
|
||||
-- don't apply ground friction when falling!
|
||||
if self.falling_state then
|
||||
return
|
||||
end
|
||||
|
||||
local vel = self.object:get_velocity()
|
||||
|
||||
if self.slippery_state then
|
||||
|
||||
local node = self.node_under
|
||||
|
||||
-- apply slip factor (tiny friction that depends on the actual block type)
|
||||
if (abs(vel.x) > 0.2 or abs(vel.z) > 0.2) then
|
||||
|
||||
local slippery = core.get_item_group(node.name, "slippery")
|
||||
local slip_factor = 4.0 / (slippery + 4)
|
||||
|
||||
self.object:set_acceleration({
|
||||
x = -vel.x * slip_factor,
|
||||
y = 0,
|
||||
z = -vel.z * slip_factor
|
||||
})
|
||||
|
||||
elseif vel.y == 0 then
|
||||
is_moving = false
|
||||
self.accel.x = self.accel.x - vel.x * slip_factor
|
||||
self.accel.z = self.accel.z - vel.z * slip_factor
|
||||
end
|
||||
end
|
||||
|
||||
if self.moving_state == is_moving
|
||||
and self.slippery_state == is_slippery then
|
||||
return -- No further updates until moving state changes
|
||||
end
|
||||
|
||||
self.moving_state = is_moving
|
||||
self.slippery_state = is_slippery
|
||||
|
||||
if is_moving then
|
||||
self.object:set_acceleration({x = 0, y = -gravity, z = 0})
|
||||
else
|
||||
self.object:set_acceleration({x = 0, y = 0, z = 0})
|
||||
self.object:set_velocity({x = 0, y = 0, z = 0})
|
||||
self.accel.x = self.accel.x - vel.x * dry_friction
|
||||
self.accel.z = self.accel.z - vel.z * dry_friction
|
||||
end
|
||||
end,
|
||||
|
||||
step_apply_forces = function(self)
|
||||
self.object:set_acceleration(self.accel)
|
||||
end,
|
||||
|
||||
step_check_timeout = function(self, dtime)
|
||||
|
||||
local pos = self.object:get_pos()
|
||||
|
||||
self.age = self.age + dtime
|
||||
|
||||
if time_to_live > 0 and self.age > time_to_live then
|
||||
|
||||
self.itemstring = ""
|
||||
self.object:remove()
|
||||
|
||||
add_effects(pos)
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
--Only collect items if not moving
|
||||
if is_moving then
|
||||
return false
|
||||
end,
|
||||
|
||||
step_check_custom_step = function(self, dtime, moveresult)
|
||||
|
||||
local pos = self.object:get_pos()
|
||||
|
||||
-- do custom step function
|
||||
local name = ItemStack(self.itemstring):get_name() or ""
|
||||
local custom = core.registered_items[name]
|
||||
and core.registered_items[name].dropped_step
|
||||
|
||||
if custom and custom(self, pos, dtime, moveresult) == false then
|
||||
return true -- skip further checks if false
|
||||
end
|
||||
|
||||
return false
|
||||
end,
|
||||
|
||||
step_try_collect = function(self)
|
||||
|
||||
local self_pos = self.object:get_pos()
|
||||
|
||||
-- Don't collect items if falling
|
||||
if self.falling_state then
|
||||
return
|
||||
end
|
||||
|
||||
-- Check if we should collect items while sliding
|
||||
if self.slippery_state and not items_collect_on_slippery then
|
||||
return
|
||||
end
|
||||
|
||||
|
@ -430,16 +551,19 @@ core.register_entity(":__builtin:item", {
|
|||
return
|
||||
end
|
||||
|
||||
local objects = core.get_objects_inside_radius(pos, 1.0)
|
||||
local objects = core.get_objects_inside_radius(self_pos, 1.0)
|
||||
|
||||
for k, obj in pairs(objects) do
|
||||
for _, obj in pairs(objects) do
|
||||
|
||||
local entity = obj:get_luaentity()
|
||||
|
||||
if entity and entity.name == "__builtin:item" then
|
||||
if entity and entity.name == "__builtin:item" and not entity.is_falling then
|
||||
|
||||
if self:try_merge_with(own_stack, obj, entity) then
|
||||
|
||||
-- item will be moved up due to try_merge_with
|
||||
self.falling_state = true
|
||||
|
||||
own_stack = ItemStack(self.itemstring)
|
||||
|
||||
if own_stack:get_free_space() == 0 then
|
||||
|
@ -450,6 +574,41 @@ core.register_entity(":__builtin:item", {
|
|||
end
|
||||
end,
|
||||
|
||||
on_step = function(self, dtime, moveresult)
|
||||
|
||||
-- reset acceleration
|
||||
self.accel = {x = 0, y = 0, z = 0}
|
||||
|
||||
-- check item timeout
|
||||
if self:step_check_timeout(dtime) then
|
||||
return -- deleted, stop here
|
||||
end
|
||||
|
||||
-- check custom step function
|
||||
if self:step_check_custom_step(dtime, moveresult) then
|
||||
return -- overriden
|
||||
end
|
||||
|
||||
-- do general checks
|
||||
self:step_update_node_state(moveresult, dtime)
|
||||
|
||||
if self:step_node_inside_checks() then
|
||||
return -- destroyed
|
||||
end
|
||||
|
||||
self:step_check_slippery()
|
||||
|
||||
-- do physics checks, then apply
|
||||
self:step_water_physics()
|
||||
self:step_ground_friction()
|
||||
self:step_gravity()
|
||||
|
||||
self:step_apply_forces()
|
||||
|
||||
-- do item checks
|
||||
self:step_try_collect()
|
||||
end,
|
||||
|
||||
on_punch = function(self, hitter)
|
||||
|
||||
local inv = hitter:get_inventory()
|
||||
|
|
541
mods/builtin_item/init.lua_
Normal file
|
@ -0,0 +1,541 @@
|
|||
-- Minetest: builtin/item_entity.lua
|
||||
|
||||
-- override ice to make slippery for 0.4.16
|
||||
if not minetest.raycast then
|
||||
minetest.override_item("default:ice", {
|
||||
groups = {cracky = 3, puts_out_fire = 1, cools_lava = 1, slippery = 3}})
|
||||
end
|
||||
|
||||
|
||||
function core.spawn_item(pos, item)
|
||||
|
||||
local stack = ItemStack(item)
|
||||
local obj = core.add_entity(pos, "__builtin:item")
|
||||
|
||||
if obj then
|
||||
obj:get_luaentity():set_item(stack:to_string())
|
||||
end
|
||||
|
||||
return obj
|
||||
end
|
||||
|
||||
|
||||
-- If item_entity_ttl is not set, enity will have default life time
|
||||
-- Setting it to -1 disables the feature
|
||||
|
||||
local time_to_live = tonumber(core.settings:get("item_entity_ttl")) or 900
|
||||
local gravity = tonumber(core.settings:get("movement_gravity")) or 9.81
|
||||
local destroy_item = core.settings:get_bool("destroy_item") ~= false
|
||||
|
||||
|
||||
-- water flow functions by QwertyMine3, edited by TenPlus1
|
||||
local inv_roots = {
|
||||
[0] = 1
|
||||
}
|
||||
|
||||
local function to_unit_vector(dir_vector)
|
||||
|
||||
local sum = dir_vector.x * dir_vector.x + dir_vector.z * dir_vector.z
|
||||
local invr_sum = 0
|
||||
|
||||
-- find inverse square root if possible
|
||||
if inv_roots[sum] ~= nil then
|
||||
invr_sum = inv_roots[sum]
|
||||
else
|
||||
-- not found, compute and save the inverse square root
|
||||
invr_sum = 1.0 / math.sqrt(sum)
|
||||
inv_roots[sum] = invr_sum
|
||||
end
|
||||
|
||||
return {
|
||||
x = dir_vector.x * invr_sum,
|
||||
y = dir_vector.y,
|
||||
z = dir_vector.z * invr_sum
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
local function node_ok(pos)
|
||||
|
||||
local node = minetest.get_node_or_nil(pos)
|
||||
|
||||
if node and minetest.registered_nodes[node.name] then
|
||||
return node
|
||||
end
|
||||
|
||||
return minetest.registered_nodes["default:dirt"]
|
||||
end
|
||||
|
||||
|
||||
local function quick_flow_logic(node, pos_testing, direction)
|
||||
|
||||
local node_testing = node_ok(pos_testing)
|
||||
local param2 = node.param2
|
||||
|
||||
if not minetest.registered_nodes[node.name].groups.liquid then
|
||||
param2 = 0
|
||||
end
|
||||
|
||||
if minetest.registered_nodes[node_testing.name].liquidtype ~= "flowing"
|
||||
and minetest.registered_nodes[node_testing.name].liquidtype ~= "source" then
|
||||
return 0
|
||||
end
|
||||
|
||||
local param2_testing = node_testing.param2
|
||||
|
||||
if param2_testing < param2 then
|
||||
|
||||
if (param2 - param2_testing) > 6 then
|
||||
return -direction
|
||||
else
|
||||
return direction
|
||||
end
|
||||
|
||||
elseif param2_testing > param2 then
|
||||
|
||||
if (param2_testing - param2) > 6 then
|
||||
return direction
|
||||
else
|
||||
return -direction
|
||||
end
|
||||
end
|
||||
|
||||
return 0
|
||||
end
|
||||
|
||||
|
||||
-- reciprocal of the length of an unit square's diagonal
|
||||
local DIAG_WEIGHT = 2 / math.sqrt(2)
|
||||
|
||||
local function quick_flow(pos, node)
|
||||
|
||||
local x, z = 0.0, 0.0
|
||||
|
||||
x = x + quick_flow_logic(node, {x = pos.x - 1, y = pos.y, z = pos.z},-1)
|
||||
x = x + quick_flow_logic(node, {x = pos.x + 1, y = pos.y, z = pos.z}, 1)
|
||||
z = z + quick_flow_logic(node, {x = pos.x, y = pos.y, z = pos.z - 1},-1)
|
||||
z = z + quick_flow_logic(node, {x = pos.x, y = pos.y, z = pos.z + 1}, 1)
|
||||
|
||||
return to_unit_vector({x = x, y = 0, z = z})
|
||||
end
|
||||
-- END water flow functions
|
||||
|
||||
|
||||
-- particle effects for when item is destroyed
|
||||
local function add_effects(pos)
|
||||
|
||||
minetest.add_particlespawner({
|
||||
amount = 1,
|
||||
time = 0.25,
|
||||
minpos = pos,
|
||||
maxpos = pos,
|
||||
minvel = {x = -1, y = 2, z = -1},
|
||||
maxvel = {x = 1, y = 4, z = 1},
|
||||
minacc = {x = 0, y = 0, z = 0},
|
||||
maxacc = {x = 0, y = 0, z = 0},
|
||||
minexptime = 1,
|
||||
maxexptime = 3,
|
||||
minsize = 1,
|
||||
maxsize = 4,
|
||||
texture = "tnt_smoke.png",
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
local water_force = 0.8
|
||||
local water_friction = 0.8
|
||||
local dry_friction = 2.5
|
||||
|
||||
core.register_entity(":__builtin:item", {
|
||||
|
||||
initial_properties = {
|
||||
hp_max = 1,
|
||||
physical = true,
|
||||
collide_with_objects = false,
|
||||
collisionbox = {-0.3, -0.3, -0.3, 0.3, 0.3, 0.3},
|
||||
visual = "wielditem",
|
||||
visual_size = {x = 0.4, y = 0.4},
|
||||
textures = {""},
|
||||
spritediv = {x = 1, y = 1},
|
||||
initial_sprite_basepos = {x = 0, y = 0},
|
||||
is_visible = false,
|
||||
infotext = "",
|
||||
},
|
||||
|
||||
itemstring = "",
|
||||
moving_state = true,
|
||||
slippery_state = false,
|
||||
age = 0,
|
||||
|
||||
set_item = function(self, item)
|
||||
|
||||
local stack = ItemStack(item or self.itemstring)
|
||||
|
||||
self.itemstring = stack:to_string()
|
||||
|
||||
if self.itemstring == "" then
|
||||
return
|
||||
end
|
||||
|
||||
local itemname = stack:is_known() and stack:get_name() or "unknown"
|
||||
local max_count = stack:get_stack_max()
|
||||
local count = math.min(stack:get_count(), max_count)
|
||||
local size = 0.2 + 0.1 * (count / max_count) ^ (1 / 3)
|
||||
local col_height = size * 0.75
|
||||
local def = core.registered_nodes[itemname]
|
||||
local glow = def and def.light_source
|
||||
local c1, c2 = "",""
|
||||
|
||||
if not(stack:get_count() == 1) then
|
||||
c1 = " x"..tostring(stack:get_count())
|
||||
c2 = " "..tostring(stack:get_count())
|
||||
end
|
||||
|
||||
local name1 = stack:get_meta():get_string("description")
|
||||
local name
|
||||
|
||||
if name1 == "" then
|
||||
name = core.registered_items[itemname].description
|
||||
else
|
||||
name = name1
|
||||
end
|
||||
|
||||
self.object:set_properties({
|
||||
is_visible = true,
|
||||
visual = "wielditem",
|
||||
textures = {itemname},
|
||||
visual_size = {x = size, y = size},
|
||||
collisionbox = {-size, -col_height, -size, size, col_height, size},
|
||||
selectionbox = {-size, -size, -size, size, size, size},
|
||||
automatic_rotate = 0.314 / size,
|
||||
wield_item = self.itemstring,
|
||||
glow = glow,
|
||||
infotext = name .. c1 .. "\n(" .. itemname .. c2 .. ")"
|
||||
})
|
||||
|
||||
end,
|
||||
|
||||
get_staticdata = function(self)
|
||||
|
||||
return core.serialize({
|
||||
itemstring = self.itemstring,
|
||||
age = self.age,
|
||||
dropped_by = self.dropped_by
|
||||
})
|
||||
end,
|
||||
|
||||
on_activate = function(self, staticdata, dtime_s)
|
||||
|
||||
if string.sub(staticdata, 1, string.len("return")) == "return" then
|
||||
|
||||
local data = core.deserialize(staticdata)
|
||||
|
||||
if data and type(data) == "table" then
|
||||
self.itemstring = data.itemstring
|
||||
self.age = (data.age or 0) + dtime_s
|
||||
self.dropped_by = data.dropped_by
|
||||
end
|
||||
else
|
||||
self.itemstring = staticdata
|
||||
end
|
||||
|
||||
self.object:set_armor_groups({immortal = 1})
|
||||
self.object:set_velocity({x = 0, y = 2, z = 0})
|
||||
self.object:set_acceleration({x = 0, y = -gravity, z = 0})
|
||||
self:set_item()
|
||||
end,
|
||||
|
||||
try_merge_with = function(self, own_stack, object, entity)
|
||||
|
||||
if self.age == entity.age then
|
||||
return false -- Can not merge with itself
|
||||
end
|
||||
|
||||
local stack = ItemStack(entity.itemstring)
|
||||
local name = stack:get_name()
|
||||
|
||||
if own_stack:get_name() ~= name
|
||||
or own_stack:get_meta() ~= stack:get_meta()
|
||||
or own_stack:get_wear() ~= stack:get_wear()
|
||||
or own_stack:get_free_space() == 0 then
|
||||
return false -- Can not merge different or full stack
|
||||
end
|
||||
|
||||
local count = own_stack:get_count()
|
||||
local total_count = stack:get_count() + count
|
||||
local max_count = stack:get_stack_max()
|
||||
|
||||
if total_count > max_count then
|
||||
return false
|
||||
end
|
||||
|
||||
-- Merge the remote stack into this one
|
||||
local pos = object:get_pos()
|
||||
pos.y = pos.y + ((total_count - count) / max_count) * 0.15
|
||||
|
||||
self.object:move_to(pos)
|
||||
self.age = 0 -- Handle as new entity
|
||||
|
||||
own_stack:set_count(total_count)
|
||||
self:set_item(own_stack)
|
||||
|
||||
entity.itemstring = ""
|
||||
object:remove()
|
||||
|
||||
return true
|
||||
end,
|
||||
|
||||
on_step = function(self, dtime, moveresult)
|
||||
|
||||
local pos = self.object:get_pos()
|
||||
|
||||
self.age = self.age + dtime
|
||||
|
||||
if time_to_live > 0 and self.age > time_to_live then
|
||||
|
||||
self.itemstring = ""
|
||||
self.object:remove()
|
||||
|
||||
add_effects(pos)
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
-- get nodes every 1/4 second
|
||||
self.timer = (self.timer or 0) + dtime
|
||||
|
||||
if self.timer > 0.25 or not self.node_inside then
|
||||
|
||||
self.node_inside = minetest.get_node_or_nil(pos)
|
||||
self.def_inside = self.node_inside
|
||||
and core.registered_nodes[self.node_inside.name]
|
||||
|
||||
-- get ground node for collision
|
||||
self.node_under = nil
|
||||
|
||||
if moveresult.touching_ground then
|
||||
|
||||
for _, info in ipairs(moveresult.collisions) do
|
||||
|
||||
if info.axis == "y" then
|
||||
|
||||
self.node_under = core.get_node(info.node_pos)
|
||||
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self.def_under = self.node_under
|
||||
and core.registered_nodes[self.node_under.name]
|
||||
|
||||
self.timer = 0
|
||||
end
|
||||
|
||||
local node = self.node_inside
|
||||
|
||||
-- Delete in 'ignore' nodes
|
||||
if node and node.name == "ignore" then
|
||||
|
||||
self.itemstring = ""
|
||||
self.object:remove()
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
-- do custom step function
|
||||
local name = ItemStack(self.itemstring):get_name() or ""
|
||||
local custom = core.registered_items[name]
|
||||
and core.registered_items[name].dropped_step
|
||||
|
||||
if custom and custom(self, pos, dtime) == false then
|
||||
return -- skip further checks if false
|
||||
end
|
||||
|
||||
local vel = self.object:get_velocity()
|
||||
local def = self.def_inside
|
||||
local is_slippery = false
|
||||
local is_moving = (def and not def.walkable) or
|
||||
vel.x ~= 0 or vel.y ~= 0 or vel.z ~= 0
|
||||
|
||||
-- destroy item when dropped into lava (if enabled)
|
||||
if destroy_item and def and def.groups and def.groups.lava then
|
||||
|
||||
minetest.sound_play("builtin_item_lava", {
|
||||
pos = pos,
|
||||
max_hear_distance = 6,
|
||||
gain = 0.5
|
||||
})
|
||||
|
||||
self.itemstring = ""
|
||||
self.object:remove()
|
||||
|
||||
add_effects(pos)
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
-- water flowing
|
||||
if def and def.liquidtype == "flowing" then
|
||||
|
||||
-- force applies on acceleration over time, thus multiply
|
||||
local force = water_force * dtime
|
||||
-- friction applies on velocity over time, thus exponentiate
|
||||
local friction = (1.0 + water_friction) ^ dtime
|
||||
|
||||
-- get flow velocity and current vel/acc state
|
||||
local vec = quick_flow(pos, node)
|
||||
local a = self.object:get_acceleration()
|
||||
|
||||
self.object:set_acceleration({
|
||||
x = a.x + vec.x * force,
|
||||
y = a.y,
|
||||
z = a.z + vec.z * force
|
||||
})
|
||||
|
||||
-- apply friction to prevent items going too fast, and also to make
|
||||
-- water flow override previous horizontal momentum more quickly
|
||||
|
||||
local v = self.object:get_velocity()
|
||||
|
||||
-- adjust friction for going against the current
|
||||
local v_horz = { x = v.x, y = 0, z = v.z }
|
||||
local v_dir = to_unit_vector(v_horz)
|
||||
local flow_dot = v_dir.x * vec.x + v_dir.y * vec.y
|
||||
|
||||
-- also maps flow_dot from [-1,0] to [0.5,2.5]
|
||||
friction = 1.0 + ((friction - 1.0) * (flow_dot + 1.5))
|
||||
|
||||
self.object:set_velocity({
|
||||
x = v.x / friction,
|
||||
y = v.y / friction,
|
||||
z = v.z / friction
|
||||
})
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
-- item inside block, move to vacant space
|
||||
if def and (def.walkable == nil or def.walkable == true)
|
||||
and (def.collision_box == nil or def.collision_box.type == "regular")
|
||||
and (def.node_box == nil or def.node_box.type == "regular") then
|
||||
|
||||
local npos = minetest.find_node_near(pos, 1, "air")
|
||||
|
||||
if npos then
|
||||
self.object:move_to(npos)
|
||||
end
|
||||
|
||||
self.node_inside = nil -- force get_node
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
-- Switch locals to node under
|
||||
node = self.node_under
|
||||
def = self.def_under
|
||||
|
||||
|
||||
-- Slippery node check
|
||||
if def and def.walkable then
|
||||
|
||||
local slippery = core.get_item_group(node.name, "slippery")
|
||||
|
||||
is_slippery = slippery ~= 0
|
||||
|
||||
if is_slippery and (math.abs(vel.x) > 0.2 or math.abs(vel.z) > 0.2) then
|
||||
|
||||
-- Horizontal deceleration
|
||||
local slip_factor = 4.0 / (slippery + 4)
|
||||
|
||||
self.object:set_acceleration({
|
||||
x = -vel.x * slip_factor,
|
||||
y = 0,
|
||||
z = -vel.z * slip_factor
|
||||
})
|
||||
|
||||
elseif vel.y == 0 then
|
||||
is_moving = false
|
||||
end
|
||||
end
|
||||
|
||||
if self.moving_state == is_moving
|
||||
and self.slippery_state == is_slippery then
|
||||
return -- No further updates until moving state changes
|
||||
end
|
||||
|
||||
self.moving_state = is_moving
|
||||
self.slippery_state = is_slippery
|
||||
|
||||
local a_curr = self.object:get_acceleration()
|
||||
local v_curr = self.object:get_velocity()
|
||||
|
||||
if is_moving then
|
||||
|
||||
self.object:set_acceleration({
|
||||
x = a_curr.x,
|
||||
y = a_curr.y - gravity,
|
||||
z = a_curr.z
|
||||
})
|
||||
else
|
||||
self.object:set_acceleration({x = 0, y = 0, z = 0})
|
||||
|
||||
-- preserve *some* velocity so items don't get stuck on the very ledges
|
||||
-- of nodes once they move just enough to leave the hitbox of flowing water
|
||||
self.object:set_velocity({
|
||||
x = v_curr.x / dry_friction,
|
||||
y = v_curr.y / dry_friction,
|
||||
z = v_curr.z / dry_friction
|
||||
})
|
||||
end
|
||||
|
||||
--Only collect items if not moving
|
||||
if is_moving then
|
||||
return
|
||||
end
|
||||
|
||||
-- Collect the items around to merge with
|
||||
local own_stack = ItemStack(self.itemstring)
|
||||
|
||||
if own_stack:get_free_space() == 0 then
|
||||
return
|
||||
end
|
||||
|
||||
local objects = core.get_objects_inside_radius(pos, 1.0)
|
||||
|
||||
for k, obj in pairs(objects) do
|
||||
|
||||
local entity = obj:get_luaentity()
|
||||
|
||||
if entity and entity.name == "__builtin:item" then
|
||||
|
||||
if self:try_merge_with(own_stack, obj, entity) then
|
||||
|
||||
own_stack = ItemStack(self.itemstring)
|
||||
|
||||
if own_stack:get_free_space() == 0 then
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
on_punch = function(self, hitter)
|
||||
|
||||
local inv = hitter:get_inventory()
|
||||
|
||||
if inv and self.itemstring ~= "" then
|
||||
|
||||
local left = inv:add_item("main", self.itemstring)
|
||||
|
||||
if left and not left:is_empty() then
|
||||
self:set_item(left)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
self.itemstring = ""
|
||||
self.object:remove()
|
||||
end,
|
||||
})
|
40
mods/builtin_item/settingtypes.txt
Normal file
|
@ -0,0 +1,40 @@
|
|||
# Controls how much force should be exerted on
|
||||
# dropped items when they are pushed by flowing water.
|
||||
#
|
||||
# The larger this number, the faster items become when
|
||||
# carried by water.
|
||||
waterflow_force (Force of water flow on dropped items) float 1.6
|
||||
|
||||
# Controls how much drag force should be exerted on
|
||||
# dropped items when they are submerged in flowing water
|
||||
# but have a different momentum vector than the flow velocity.
|
||||
#
|
||||
# The larger this number, the larger the resistance of water
|
||||
# to the push of an item. In other words, items thrown at
|
||||
# flowing water in a direction opposite that of the flow will
|
||||
# be pushed more quickly in the other direction with higher drag,
|
||||
# even if the actual flow force remains the same.
|
||||
waterflow_drag (Drag of water flow on dropped items) float 0.8
|
||||
|
||||
# Controls how much friction force should be exerted on
|
||||
# dropped items when they move horizontally on the
|
||||
# ground.
|
||||
#
|
||||
# The larger this number, the quickier items will come to
|
||||
# a halt horizontally after falling on the floor.
|
||||
friction_dry (Friction of dry ground on dropped items) float 2.5
|
||||
|
||||
# Controls how much horizontal drag force should be exerted on
|
||||
# dropped items when they move horizontally in air.
|
||||
#
|
||||
# The larger this number, the quickier horizontal velocity tends
|
||||
# toward zero.
|
||||
air_drag (Horizontal drag of air on falling items) float 0.4
|
||||
|
||||
# Allow items on the floor to collect even on slippery floors.
|
||||
#
|
||||
# Dropped items can 'collect', that is, to have their
|
||||
# stacks merged if they're close enough and are of the same item
|
||||
# type. By default they can do this even on a slippery floor, like
|
||||
# ice, but this setting allows disabling that.
|
||||
builtin_item.items_collect_on_slippery (Collect items on slippery ground) bool true
|
|
@ -229,9 +229,9 @@ minetest.register_node("cottages:anvil", {
|
|||
end
|
||||
minetest.after(2, function()
|
||||
if( puncher ) then
|
||||
puncher:hud_remove(hud1);
|
||||
puncher:hud_remove(hud2);
|
||||
puncher:hud_remove(hud3);
|
||||
if(hud1) then puncher:hud_remove(hud1); end
|
||||
if(hud2) then puncher:hud_remove(hud2); end
|
||||
if(hud3) then puncher:hud_remove(hud3); end
|
||||
end
|
||||
end)
|
||||
|
||||
|
|
|
@ -333,13 +333,13 @@ minetest.register_node("cottages:threshing_floor", {
|
|||
|
||||
minetest.after(2, function()
|
||||
if( puncher ) then
|
||||
puncher:hud_remove(hud1);
|
||||
puncher:hud_remove(hud2);
|
||||
puncher:hud_remove(hud3);
|
||||
puncher:hud_remove(hud4);
|
||||
puncher:hud_remove(hud5);
|
||||
puncher:hud_remove(hud6);
|
||||
puncher:hud_remove(hud0);
|
||||
if(hud1) then puncher:hud_remove(hud1); end
|
||||
if(hud2) then puncher:hud_remove(hud2); end
|
||||
if(hud3) then puncher:hud_remove(hud3); end
|
||||
if(hud4) then puncher:hud_remove(hud4); end
|
||||
if(hud5) then puncher:hud_remove(hud5); end
|
||||
if(hud6) then puncher:hud_remove(hud6); end
|
||||
if(hud0) then puncher:hud_remove(hud0); end
|
||||
end
|
||||
end)
|
||||
end,
|
||||
|
|
|
@ -13,7 +13,7 @@ This mod works by adding your new plant to the {growing=1} group and numbering t
|
|||
|
||||
### Changelog:
|
||||
|
||||
- 1.46 - Added min/max default light settings, added lettuce and blackberries with food items (thanks OgelGames), added soya and vanilla (thanks Felfa), added tofu
|
||||
- 1.46 - Added min/max default light settings, added lettuce and blackberries with food items (thanks OgelGames), added soya and vanilla (thanks Felfa), added tofu, added salt crystals (thanks gorlock)
|
||||
- 1.45 - Dirt and Hoes are more in line with default by using dry/wet/base, added cactus juice, added pasta, spaghetti, cabbage, korean bibimbap, code tidy
|
||||
options, onion soup added (thanks edcrypt), Added apple pie, added wild cotton to savanna
|
||||
- 1.44 - Added 'farming_stage_length' in mod settings for speed of crop growth, also thanks to TheDarkTiger for translation updates
|
||||
|
|
|
@ -33,7 +33,48 @@ minetest.register_node("farming:salt", {
|
|||
selection_box = {
|
||||
type = "fixed",
|
||||
fixed = {-0.25, -0.5, -0.25, 0.25, 0.3, 0.25}
|
||||
}
|
||||
},
|
||||
-- special function to make salt crystals form inside water
|
||||
dropped_step = function(self, pos, dtime)
|
||||
|
||||
self.ctimer = (self.ctimer or 0) + dtime
|
||||
if self.ctimer < 15.0 then return end
|
||||
self.ctimer = 0
|
||||
|
||||
local needed
|
||||
|
||||
if self.node_inside
|
||||
and self.node_inside.name == "default:water_source" then
|
||||
needed = 8
|
||||
|
||||
elseif self.node_inside
|
||||
and self.node_inside.name == "default:river_water_source" then
|
||||
needed = 9
|
||||
end
|
||||
|
||||
if not needed then return end
|
||||
|
||||
local objs = core.get_objects_inside_radius(pos, 0.5)
|
||||
|
||||
if not objs or #objs ~= 1 then return end
|
||||
|
||||
local salt, ent = nil, nil
|
||||
|
||||
for k, obj in pairs(objs) do
|
||||
|
||||
ent = obj:get_luaentity()
|
||||
|
||||
if ent and ent.name == "__builtin:item"
|
||||
and ent.itemstring == "farming:salt " .. needed then
|
||||
|
||||
obj:remove()
|
||||
|
||||
core.add_item(pos, "farming:salt_crystal")
|
||||
|
||||
return false -- return with no further action
|
||||
end
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
|
@ -44,6 +85,40 @@ minetest.register_craft({
|
|||
replacements = {{"bucket:bucket_water", "bucket:bucket_empty"}}
|
||||
})
|
||||
|
||||
--= Salt Crystal
|
||||
|
||||
minetest.register_node("farming:salt_crystal", {
|
||||
description = ("Salt crystal"),
|
||||
inventory_image = "farming_salt_crystal.png",
|
||||
wield_image = "farming_salt_crystal.png",
|
||||
drawtype = "plantlike",
|
||||
visual_scale = 0.8,
|
||||
paramtype = "light",
|
||||
light_source = 1,
|
||||
tiles = {"farming_salt_crystal.png"},
|
||||
groups = { dig_immediate = 3, attached_node = 1},
|
||||
sounds = default.node_sound_defaults(),
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
fixed = {-0.25, -0.5, -0.25, 0.25, 0.3, 0.25}
|
||||
},
|
||||
})
|
||||
minetest.register_craft({
|
||||
type = "shapeless",
|
||||
output = "farming:salt 9",
|
||||
recipe = {"farming:salt_crystal", "farming:mortar_pestle"},
|
||||
replacements = {{"farming:mortar_pestle", "farming:mortar_pestle"}}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "farming:salt_crystal",
|
||||
recipe = {
|
||||
{"farming:salt", "farming:salt", "farming:salt"},
|
||||
{"farming:salt", "farming:salt", "farming:salt"},
|
||||
{"farming:salt", "farming:salt", "farming:salt"}
|
||||
}
|
||||
})
|
||||
|
||||
--= Rose Water
|
||||
|
||||
minetest.register_node("farming:rose_water", {
|
||||
|
@ -273,7 +348,6 @@ minetest.register_craftitem("farming:pasta", {
|
|||
groups = {food_pasta = 1}
|
||||
})
|
||||
|
||||
if minetest.get_modpath("mobs_animal") or minetest.get_modpath("xanadu")then
|
||||
minetest.register_craft({
|
||||
output = "farming:pasta",
|
||||
type = "shapeless",
|
||||
|
@ -283,7 +357,7 @@ minetest.register_craft({
|
|||
},
|
||||
replacements = {{"group:food_mixing_bowl", "farming:mixing_bowl"}}
|
||||
})
|
||||
else
|
||||
|
||||
minetest.register_craft({
|
||||
output = "farming:pasta",
|
||||
type = "shapeless",
|
||||
|
@ -296,7 +370,6 @@ minetest.register_craft({
|
|||
{"group:food_oil", "vessels:glass_bottle"}
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
-- Spaghetti
|
||||
|
||||
|
@ -324,7 +397,6 @@ minetest.register_craftitem("farming:bibimbap", {
|
|||
on_use = minetest.item_eat(8, "farming:bowl")
|
||||
})
|
||||
|
||||
if minetest.get_modpath("mobs_animal") or minetest.get_modpath("xanadu")then
|
||||
minetest.register_craft({
|
||||
output = "farming:bibimbap",
|
||||
type = "shapeless",
|
||||
|
@ -335,7 +407,7 @@ minetest.register_craft({
|
|||
},
|
||||
replacements = {{"group:food_skillet", "farming:skillet"}}
|
||||
})
|
||||
else
|
||||
|
||||
minetest.register_craft({
|
||||
output = "farming:bibimbap",
|
||||
type = "shapeless",
|
||||
|
@ -346,7 +418,6 @@ minetest.register_craft({
|
|||
},
|
||||
replacements = {{"group:food_skillet", "farming:skillet"}}
|
||||
})
|
||||
end
|
||||
|
||||
-- Burger
|
||||
|
||||
|
|
|
@ -145,13 +145,16 @@ Created by mDiyo (Natura), modified by TenPlus1 (License: CC BY-SA 3.0):
|
|||
farming_barley.png
|
||||
|
||||
Created by OgelGames (CC BY-SA 4.0)
|
||||
farming_berry_smoothie.png
|
||||
farming_cactus_juice.png
|
||||
farming_salad.png
|
||||
farming_berry_smoothie.png
|
||||
farming_cactus_juice.png
|
||||
farming_salad.png
|
||||
|
||||
Created by Felfa (CC0)
|
||||
farming_blackberry*.png
|
||||
farming_lettuce*.png
|
||||
farming_burger.png
|
||||
farming_soy*.png
|
||||
farming_vanilla*.png
|
||||
farming_blackberry*.png
|
||||
farming_lettuce*.png
|
||||
farming_burger.png
|
||||
farming_soy*.png
|
||||
farming_vanilla*.png
|
||||
|
||||
Created by gorlock (CC0)
|
||||
farming_salt_crystal.png
|
||||
|
|
|
@ -59,7 +59,7 @@ Cornstarch=玉米淀粉
|
|||
Bottle of Ethanol=一瓶乙醇
|
||||
Cotton Seed=棉籽
|
||||
Cotton=棉花
|
||||
String=字符串
|
||||
String=线
|
||||
Cucumber=黄瓜
|
||||
Garlic clove=蒜瓣
|
||||
Garlic=大蒜
|
||||
|
|
|
@ -148,7 +148,7 @@ minetest.register_abm({
|
|||
if minetest.registered_nodes[nn]
|
||||
and minetest.registered_nodes[nn].walkable
|
||||
and minetest.get_item_group(nn, "plant") == 0 then
|
||||
minetest.set_node(pos, {name = "default:dirt"})
|
||||
minetest.set_node(pos, {name = ndef.soil.base})
|
||||
return
|
||||
end
|
||||
|
||||
|
|
BIN
mods/farming/textures/farming_salt_crystal.png
Normal file
After Width: | Height: | Size: 175 B |
|
@ -298,6 +298,21 @@ minetest.register_craft({
|
|||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
type = "shapeless",
|
||||
output = "default:cobble 8",
|
||||
recipe = {
|
||||
"gloopblocks:pumice",
|
||||
"gloopblocks:pumice",
|
||||
"gloopblocks:pumice",
|
||||
"gloopblocks:pumice",
|
||||
"gloopblocks:basalt",
|
||||
"gloopblocks:basalt",
|
||||
"gloopblocks:basalt",
|
||||
"gloopblocks:basalt"
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "gloopblocks:fence_steel 1",
|
||||
recipe = {
|
||||
|
|
|
@ -13,3 +13,10 @@ nyancat?
|
|||
usesdirt?
|
||||
worldedit?
|
||||
signs_lib?
|
||||
bakedclay?
|
||||
farming?
|
||||
wool?
|
||||
bushes_classic?
|
||||
dryplants?
|
||||
bedrock?
|
||||
cottages?
|
||||
|
|
|
@ -12,942 +12,8 @@ gloopblocks = {}
|
|||
local MP = minetest.get_modpath(minetest.get_current_modname())
|
||||
local S, NS = dofile(MP.."/intllib.lua")
|
||||
|
||||
-- Nodes
|
||||
|
||||
minetest.register_node("gloopblocks:rainbow_block_diagonal", {
|
||||
description = S("Diagonal Rainbow Block"),
|
||||
tiles = {"gloopblocks_rainbow_block.png"},
|
||||
is_ground_content = true,
|
||||
groups = {cracky=3},
|
||||
sounds = default.node_sound_defaults(),
|
||||
})
|
||||
minetest.register_alias("gloopblocks:rainbow_block", "gloopblocks:rainbow_block_diagonal")
|
||||
|
||||
minetest.register_node("gloopblocks:rainbow_block_horizontal", {
|
||||
description = S("Horizontal Rainbow Block"),
|
||||
tiles = {
|
||||
"gloopblocks_rainbow_horizontal.png^[transformR90",
|
||||
"gloopblocks_rainbow_horizontal.png^[transformR90",
|
||||
"gloopblocks_rainbow_horizontal.png"
|
||||
},
|
||||
paramtype = "light",
|
||||
light_source = default.LIGHT_MAX,
|
||||
paramtype2 = "facedir",
|
||||
groups = {cracky = 2},
|
||||
is_ground_content = false,
|
||||
sounds = default.node_sound_defaults(),
|
||||
})
|
||||
|
||||
minetest.register_node("gloopblocks:evil_block", {
|
||||
description = S("Evil Block"),
|
||||
tiles = {"gloopblocks_evil_block.png"},
|
||||
light_source = 5,
|
||||
is_ground_content = true,
|
||||
groups = {cracky=2},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
})
|
||||
|
||||
minetest.register_node("gloopblocks:basalt", {
|
||||
description = S("Basalt"),
|
||||
tiles = {"gloopblocks_basalt.png"},
|
||||
groups = {cracky=2},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
})
|
||||
|
||||
minetest.register_node("gloopblocks:pumice", {
|
||||
description = S("Pumice"),
|
||||
tiles = {"gloopblocks_pumice.png"},
|
||||
groups = {cracky=3},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
})
|
||||
|
||||
|
||||
minetest.register_node("gloopblocks:pavement", {
|
||||
description = S("Pavement"),
|
||||
tiles = {"gloopblocks_pavement.png"},
|
||||
groups = {cracky=3, oddly_breakable_by_hand=3},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
})
|
||||
|
||||
minetest.register_node("gloopblocks:oerkki_block", {
|
||||
drawtype = "nodebox",
|
||||
description = S("Oerkki Block"),
|
||||
paramtype = "light",
|
||||
paramtype2 = "facedir",
|
||||
tiles = {
|
||||
"gloopblocks_oerkkiblock_tb.png",
|
||||
"gloopblocks_oerkkiblock_tb.png",
|
||||
"gloopblocks_oerkkiblock_sides.png",
|
||||
"gloopblocks_oerkkiblock_sides.png",
|
||||
"gloopblocks_oerkkiblock_sides.png",
|
||||
"gloopblocks_oerkkiblock_front.png"
|
||||
},
|
||||
groups = {cracky=3, oddly_breakable_by_hand=3},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
fixed = { -0.5, -0.5, -0.5, 0.5, 0.5, 0.5 }
|
||||
},
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{-0.5, -0.5, -0.1875, 0.5, 0.5, 0.1875}, -- NodeBox1
|
||||
{-0.5, -0.5, -0.5, -0.4375, 0.5, 0.5}, -- NodeBox2
|
||||
{0.4375, -0.5, -0.5, 0.5, 0.5, 0.5}, -- NodeBox3
|
||||
{-0.5, 0.4375, -0.5, 0.5, 0.5, 0.5}, -- NodeBox4
|
||||
{-0.5, -0.5, -0.5, 0.5, -0.4375, 0.5}, -- NodeBox5
|
||||
{-0.5, -0.0625, -0.5, 0.5, 0.0625, 0.5}, -- NodeBox6
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_node("gloopblocks:stone_brick_mossy", {
|
||||
description = S("Mossy Stone Brick"),
|
||||
tiles = {"gloopblocks_stone_brick_mossy.png"},
|
||||
groups = {cracky=3, stone=1},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
})
|
||||
|
||||
minetest.register_node("gloopblocks:stone_mossy", {
|
||||
description = S("Mossy Stone"),
|
||||
tiles = {"gloopblocks_stone_mossy.png"},
|
||||
groups = {cracky=3, stone=1},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
drop = "default:mossycobble"
|
||||
})
|
||||
|
||||
minetest.register_node("gloopblocks:cobble_road", {
|
||||
description = S("Cobblestone Road Bed"),
|
||||
tiles = {"gloopblocks_cobble_road.png"},
|
||||
groups = {cracky=3, stone=1},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
})
|
||||
|
||||
minetest.register_node("gloopblocks:cobble_road_mossy", {
|
||||
description = S("Mossy Cobblestone Road Bed"),
|
||||
tiles = {"gloopblocks_cobble_road_mossy.png"},
|
||||
groups = {cracky=3, stone=1},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
})
|
||||
|
||||
minetest.register_node("gloopblocks:scaffolding", {
|
||||
description = S("Wooden Scaffold"),
|
||||
drawtype = "allfaces",
|
||||
paramtype = "light",
|
||||
sunlight_propagates = true,
|
||||
tiles = {"gloopblocks_scaffold.png"},
|
||||
groups = {choppy=3, oddly_breakable_by_hand=3},
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
})
|
||||
|
||||
minetest.register_alias("moreblocks:oerkkiblock", "gloopblocks:oerkki_block")
|
||||
minetest.register_alias("gloopblocks:obsidian", "default:obsidian")
|
||||
|
||||
-- Nodes imported from Usesdirt ================================================================================
|
||||
|
||||
if not minetest.get_modpath("usesdirt") then
|
||||
|
||||
local dirt_brick_tex = "default_dirt.png^gloopblocks_dirt_brick_overlay.png"
|
||||
local dirt_cobble_tex = "default_cobble.png^(default_dirt.png^[mask:gloopblocks_dirt_cobble_mask.png)"
|
||||
local dirt_stone_tex = "default_stone.png^(default_dirt.png^[mask:gloopblocks_dirt_stone_mask.png)"
|
||||
|
||||
local dirt_ladder_tex = "(default_dirt.png^[mask:gloopblocks_ladder_mask.png)^gloopblocks_ladder_overlay.png"
|
||||
local dirt_brick_ladder_tex = "(("..dirt_brick_tex..")^[mask:gloopblocks_ladder_mask.png)^gloopblocks_ladder_overlay.png"
|
||||
local dirt_cobble_ladder_tex = "(("..dirt_cobble_tex..")^[mask:gloopblocks_ladder_mask.png)^gloopblocks_ladder_overlay.png"
|
||||
local dirt_stone_ladder_tex = "(("..dirt_stone_tex..")^[mask:gloopblocks_ladder_mask.png)^gloopblocks_ladder_overlay.png"
|
||||
|
||||
minetest.register_node(":usesdirt:dirt_brick", {
|
||||
tiles = { dirt_brick_tex },
|
||||
description = "Dirt Brick",
|
||||
groups = {snappy=2,choppy=1,oddly_breakable_by_hand=2},
|
||||
})
|
||||
|
||||
minetest.register_node(":usesdirt:dirt_brick_ladder", {
|
||||
description = "Dirt Brick Ladder",
|
||||
drawtype = "signlike",
|
||||
tiles = { dirt_brick_ladder_tex },
|
||||
inventory_image = dirt_brick_ladder_tex,
|
||||
wield_image = dirt_brick_ladder_tex,
|
||||
paramtype = "light",
|
||||
paramtype2 = "wallmounted",
|
||||
is_ground_content = true,
|
||||
walkable = false,
|
||||
climbable = true,
|
||||
selection_box = {
|
||||
type = "wallmounted",
|
||||
--wall_top = = <default>
|
||||
--wall_bottom = = <default>
|
||||
--wall_side = = <default>
|
||||
},
|
||||
groups = {cracky=3, stone=2},
|
||||
legacy_wallmounted = true,
|
||||
})
|
||||
minetest.register_craft({
|
||||
output = 'usesdirt:dirt_brick_ladder 3',
|
||||
recipe = {
|
||||
{'usesdirt:dirt_brick', '', 'usesdirt:dirt_brick'},
|
||||
{'usesdirt:dirt_brick', 'usesdirt:dirt_brick','usesdirt:dirt_brick'},
|
||||
{'usesdirt:dirt_brick','','usesdirt:dirt_brick'},
|
||||
}
|
||||
})
|
||||
|
||||
default.register_fence(":usesdirt:dirt_brick_fence", {
|
||||
description = "Dirt Brick Fence",
|
||||
texture = dirt_brick_tex,
|
||||
inventory_image = "default_fence_overlay.png^("..dirt_brick_tex..")^default_fence_overlay.png^[makealpha:255,126,126",
|
||||
wield_image = "default_fence_overlay.png^("..dirt_brick_tex..")^default_fence_overlay.png^[makealpha:255,126,126",
|
||||
material = "usesdirt:dirt_brick",
|
||||
groups = {cracky=3, stone=2},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
check_for_pole = true
|
||||
})
|
||||
|
||||
if minetest.get_modpath("moreblocks") then
|
||||
minetest.register_craft({
|
||||
output = 'usesdirt:dirt_brick 24',
|
||||
recipe = {
|
||||
{'moreblocks:dirt_compressed', 'moreblocks:dirt_compressed', '' },
|
||||
{'moreblocks:dirt_compressed', 'moreblocks:dirt_compressed', '' }
|
||||
}
|
||||
})
|
||||
else
|
||||
minetest.register_craft({
|
||||
output = 'usesdirt:dirt_brick 6',
|
||||
recipe = {
|
||||
{'default:dirt', 'default:dirt', 'default:dirt'},
|
||||
{'default:dirt', 'default:dirt', 'default:dirt'},
|
||||
{'default:dirt', 'default:dirt', 'default:dirt'},
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
minetest.register_node(":usesdirt:dirt_ladder", {
|
||||
description = "Dirt Ladder",
|
||||
drawtype = "signlike",
|
||||
tiles = { dirt_ladder_tex },
|
||||
inventory_image = dirt_ladder_tex,
|
||||
wield_image = dirt_ladder_tex,
|
||||
paramtype = "light",
|
||||
paramtype2 = "wallmounted",
|
||||
is_ground_content = true,
|
||||
walkable = false,
|
||||
climbable = true,
|
||||
selection_box = {
|
||||
type = "wallmounted",
|
||||
--wall_top = = <default>
|
||||
--wall_bottom = = <default>
|
||||
--wall_side = = <default>
|
||||
},
|
||||
groups = {snappy=2,choppy=2,oddly_breakable_by_hand=3},
|
||||
legacy_wallmounted = true,
|
||||
})
|
||||
minetest.register_craft({
|
||||
output = 'usesdirt:dirt_ladder 3',
|
||||
recipe = {
|
||||
{'usesdirt:dirt_brick', '', 'usesdirt:dirt_brick'},
|
||||
{'usesdirt:dirt_brick', 'usesdirt:dirt_brick','usesdirt:dirt_brick'},
|
||||
{'usesdirt:dirt_brick','','usesdirt:dirt_brick'},
|
||||
}
|
||||
})
|
||||
|
||||
default.register_fence(":usesdirt:dirt_fence", {
|
||||
description = "Dirt Fence",
|
||||
texture = "default_dirt.png",
|
||||
inventory_image = "default_fence_overlay.png^default_dirt.png^default_fence_overlay.png^[makealpha:255,126,126",
|
||||
wield_image = "default_fence_overlay.png^default_dirt.png^default_fence_overlay.png^[makealpha:255,126,126",
|
||||
material = "default:dirt",
|
||||
groups = {snappy=2,choppy=1,oddly_breakable_by_hand=3},
|
||||
sounds = default.node_sound_dirt_defaults(),
|
||||
check_for_pole = true
|
||||
})
|
||||
|
||||
----
|
||||
|
||||
minetest.register_node(":usesdirt:dirt_cobble_stone", {
|
||||
tiles = { dirt_cobble_tex },
|
||||
description = "Dirt Cobble Stone",
|
||||
is_ground_content = true,
|
||||
groups = {cracky=3, stone=2},
|
||||
})
|
||||
minetest.register_craft({
|
||||
output = '"usesdirt:dirt_cobble_stone" 3',
|
||||
recipe = {
|
||||
{'usesdirt:dirt_brick', 'usesdirt:dirt_brick', 'usesdirt:dirt_brick'},
|
||||
{'usesdirt:dirt_brick', 'usesdirt:dirt_brick', 'usesdirt:dirt_brick'},
|
||||
{'usesdirt:dirt_brick', 'usesdirt:dirt_brick', 'usesdirt:dirt_brick'},
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_node(":usesdirt:dirt_cobble_stone_ladder", {
|
||||
description = "Dirt Cobble Stone Ladder",
|
||||
drawtype = "signlike",
|
||||
tiles = { dirt_cobble_ladder_tex },
|
||||
inventory_image = dirt_cobble_ladder_tex,
|
||||
wield_image = dirt_cobble_ladder_tex,
|
||||
paramtype = "light",
|
||||
paramtype2 = "wallmounted",
|
||||
is_ground_content = true,
|
||||
walkable = false,
|
||||
climbable = true,
|
||||
selection_box = {
|
||||
type = "wallmounted",
|
||||
--wall_top = = <default>
|
||||
--wall_bottom = = <default>
|
||||
--wall_side = = <default>
|
||||
},
|
||||
groups = {cracky=3, stone=2},
|
||||
legacy_wallmounted = true,
|
||||
})
|
||||
minetest.register_craft({
|
||||
output = 'usesdirt:dirt_cobble_stone_ladder 3',
|
||||
recipe = {
|
||||
{'usesdirt:dirt_cobble_stone', '', 'usesdirt:dirt_cobble_stone'},
|
||||
{'usesdirt:dirt_cobble_stone', 'usesdirt:dirt_cobble_stone','usesdirt:dirt_cobble_stone'},
|
||||
{'usesdirt:dirt_cobble_stone','','usesdirt:dirt_cobble_stone'},
|
||||
}
|
||||
})
|
||||
|
||||
default.register_fence(":usesdirt:dirt_cobble_stone_fence", {
|
||||
description = "Dirt Cobble Stone Fence",
|
||||
texture = dirt_cobble_tex,
|
||||
inventory_image = "default_fence_overlay.png^("..dirt_cobble_tex..")^default_fence_overlay.png^[makealpha:255,126,126",
|
||||
wield_image = "default_fence_overlay.png^("..dirt_cobble_tex..")^default_fence_overlay.png^[makealpha:255,126,126",
|
||||
material = "usesdirt:dirt_cobble_stone",
|
||||
groups = {cracky=3, stone=2},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
check_for_pole = true
|
||||
})
|
||||
|
||||
----
|
||||
|
||||
minetest.register_node(":usesdirt:dirt_stone", {
|
||||
tiles = { dirt_stone_tex },
|
||||
description = "Dirt Stone",
|
||||
is_ground_content = true,
|
||||
groups = {cracky=3, stone=2},
|
||||
})
|
||||
|
||||
minetest.register_node(":usesdirt:dirt_stone_ladder", {
|
||||
description = "Dirt Stone Ladder",
|
||||
drawtype = "signlike",
|
||||
tiles = { dirt_stone_ladder_tex },
|
||||
inventory_image = dirt_stone_ladder_tex,
|
||||
wield_image = dirt_stone_ladder_tex,
|
||||
paramtype = "light",
|
||||
paramtype2 = "wallmounted",
|
||||
is_ground_content = true,
|
||||
walkable = false,
|
||||
climbable = true,
|
||||
selection_box = {
|
||||
type = "wallmounted",
|
||||
--wall_top = = <default>
|
||||
--wall_bottom = = <default>
|
||||
--wall_side = = <default>
|
||||
},
|
||||
groups = {cracky=3, stone=2},
|
||||
legacy_wallmounted = true,
|
||||
})
|
||||
minetest.register_craft({
|
||||
output = 'usesdirt:dirt_stone_ladder 3',
|
||||
recipe = {
|
||||
{'usesdirt:dirt_stone', '', 'usesdirt:dirt_stone'},
|
||||
{'usesdirt:dirt_stone', 'usesdirt:dirt_stone','usesdirt:dirt_stone'},
|
||||
{'usesdirt:dirt_stone','','usesdirt:dirt_stone'},
|
||||
}
|
||||
})
|
||||
|
||||
default.register_fence(":usesdirt:dirt_stone_fence", {
|
||||
description = "Dirt Stone Fence",
|
||||
texture = dirt_stone_tex,
|
||||
inventory_image = "default_fence_overlay.png^("..dirt_stone_tex..")^default_fence_overlay.png^[makealpha:255,126,126",
|
||||
wield_image = "default_fence_overlay.png^("..dirt_stone_tex..")^default_fence_overlay.png^[makealpha:255,126,126",
|
||||
material = "usesdirt:dirt_stone",
|
||||
groups = {cracky=3, stone=2},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
check_for_pole = true
|
||||
})
|
||||
end
|
||||
|
||||
-- Stairs/slabs defs, conversion of normal -> mossy items
|
||||
|
||||
if minetest.setting_getbool("gloopblocks_mossy_conversion") ~= false then
|
||||
|
||||
function gloopblocks_register_mossy_conversion(mossyobjects)
|
||||
for i in ipairs(mossyobjects) do
|
||||
minetest.register_abm({
|
||||
nodenames = { mossyobjects[i][1] },
|
||||
neighbors = {"default:water_source", "default:water_flowing"},
|
||||
interval = 120,
|
||||
chance = 50,
|
||||
action = function(pos, node)
|
||||
if minetest.find_node_near(pos, 2, "air") then
|
||||
local fdir = node.param2
|
||||
minetest.add_node(pos, {name = mossyobjects[i][2], param2 = fdir})
|
||||
end
|
||||
end,
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if minetest.get_modpath("moreblocks") then
|
||||
|
||||
stairsplus:register_all("gloopblocks", "oerkki_block", "gloopblocks:oerkki_block", {
|
||||
description = S("Oerkki Block"),
|
||||
tiles = {
|
||||
"gloopblocks_oerkkiblock_tb.png",
|
||||
"gloopblocks_oerkkiblock_tb.png",
|
||||
"gloopblocks_oerkkiblock_sides.png",
|
||||
"gloopblocks_oerkkiblock_sides.png",
|
||||
"gloopblocks_oerkkiblock_sides.png",
|
||||
"gloopblocks_oerkkiblock_front.png"
|
||||
},
|
||||
groups = {cracky=2, not_in_creative_inventory=1},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
sunlight_propagates = true,
|
||||
})
|
||||
|
||||
stairsplus:register_all("gloopblocks", "stone_brick_mossy", "gloopblocks:stone_brick_mossy", {
|
||||
description = S("Mossy Stone Brick"),
|
||||
tiles = {"gloopblocks_stone_brick_mossy.png"},
|
||||
groups = {cracky=1, not_in_creative_inventory=1},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
sunlight_propagates = true,
|
||||
})
|
||||
|
||||
stairsplus:register_all("gloopblocks", "stone_mossy", "gloopblocks:stone_mossy", {
|
||||
description = S("Mossy Stone"),
|
||||
tiles = {"gloopblocks_stone_mossy.png"},
|
||||
groups = {cracky=1, not_in_creative_inventory=1},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
sunlight_propagates = true,
|
||||
})
|
||||
|
||||
stairsplus:register_all("gloopblocks", "cobble_road", "gloopblocks:cobble_road", {
|
||||
description = S("Cobblestone Roadbed"),
|
||||
tiles = {"gloopblocks_cobble_road.png"},
|
||||
groups = {cracky=3, stone=1, not_in_creative_inventory=1},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
sunlight_propagates = true,
|
||||
})
|
||||
|
||||
stairsplus:register_all("gloopblocks", "cobble_road_mossy", "gloopblocks:cobble_road_mossy", {
|
||||
description = S("Mossy Cobblestone Roadbed"),
|
||||
tiles = {"gloopblocks_cobble_road_mossy.png"},
|
||||
groups = {cracky=3, stone=1, not_in_creative_inventory=1},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
sunlight_propagates = true,
|
||||
})
|
||||
|
||||
stairsplus:register_all("gloopblocks", "pavement", "gloopblocks:pavement", {
|
||||
description = S("Pavement"),
|
||||
tiles = {"gloopblocks_pavement.png"},
|
||||
groups = {cracky=2, not_in_creative_inventory=1},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
sunlight_propagates = true,
|
||||
})
|
||||
|
||||
stairsplus:register_all("gloopblocks", "rainbow_block", "gloopblocks:rainbow_block", {
|
||||
description = S("Rainbow Block"),
|
||||
tiles = {"gloopblocks_rainbow_block.png"},
|
||||
groups = {cracky=3, not_in_creative_inventory=1},
|
||||
sounds = default.node_sound_defaults(),
|
||||
sunlight_propagates = true,
|
||||
})
|
||||
|
||||
stairsplus:register_all("gloopblocks", "evil_block", "gloopblocks:evil_block", {
|
||||
description = S("Evil Block"),
|
||||
tiles = {"gloopblocks_evil_block.png"},
|
||||
groups = {cracky=3, not_in_creative_inventory=1},
|
||||
sounds = default.node_sound_defaults(),
|
||||
light_source = 5,
|
||||
sunlight_propagates = true,
|
||||
})
|
||||
|
||||
stairsplus:register_all("gloopblocks", "basalt", "gloopblocks:basalt", {
|
||||
description = S("Basalt"),
|
||||
tiles = {"gloopblocks_basalt.png"},
|
||||
groups = {cracky=2, not_in_creative_inventory=1},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
sunlight_propagates = true,
|
||||
})
|
||||
|
||||
stairsplus:register_all("gloopblocks", "pumice", "gloopblocks:pumice", {
|
||||
description = S("Pumice"),
|
||||
tiles = {"gloopblocks_pumice.png"},
|
||||
groups = {cracky=3, not_in_creative_inventory=1},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
sunlight_propagates = true,
|
||||
})
|
||||
|
||||
stairsplus:register_all("gloopblocks", "gravel", "default:gravel", {
|
||||
description = S("Gravel"),
|
||||
tiles = {"default_gravel.png"},
|
||||
groups = {crumbly = 2, falling_node = 1, not_in_creative_inventory=1},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
sunlight_propagates = false,
|
||||
})
|
||||
|
||||
if minetest.get_modpath("caverealms") then
|
||||
stairsplus:register_all("caverealms", "glow_crystal", "caverealms:glow_crystal", {
|
||||
description = S("Glow Crystal"),
|
||||
tiles = {"caverealms_glow_crystal.png"},
|
||||
groups = {cracky=3, not_in_creative_inventory=1},
|
||||
sounds = default.node_sound_glass_defaults(),
|
||||
light_source = 12,
|
||||
use_texture_alpha = true,
|
||||
paramtype="light",
|
||||
sunlight_propagates = true,
|
||||
})
|
||||
|
||||
stairsplus:register_all("caverealms", "glow_emerald", "caverealms:glow_emerald", {
|
||||
description = S("Glow Emerald"),
|
||||
tiles = {"caverealms_glow_emerald.png"},
|
||||
groups = {cracky=3, not_in_creative_inventory=1},
|
||||
sounds = default.node_sound_glass_defaults(),
|
||||
light_source = 12,
|
||||
use_texture_alpha = true,
|
||||
paramtype="light",
|
||||
sunlight_propagates = true,
|
||||
})
|
||||
|
||||
stairsplus:register_all("caverealms", "glow_mese", "caverealms:glow_mese", {
|
||||
description = S("Glow Mese"),
|
||||
tiles = {"caverealms_glow_mese.png"},
|
||||
groups = {cracky=3, not_in_creative_inventory=1},
|
||||
sounds = default.node_sound_glass_defaults(),
|
||||
light_source = 12,
|
||||
use_texture_alpha = true,
|
||||
paramtype="light",
|
||||
sunlight_propagates = true,
|
||||
})
|
||||
end
|
||||
|
||||
-- ABMs for mossy objects
|
||||
|
||||
if minetest.setting_getbool("gloopblocks_mossy_conversion") ~= false then
|
||||
|
||||
gloopblocks_register_mossy_conversion({
|
||||
{ "moreblocks:stair_cobble", "moreblocks:stair_mossycobble" },
|
||||
{ "moreblocks:stair_cobble_inner", "moreblocks:stair_mossycobble_inner" },
|
||||
{ "moreblocks:stair_cobble_outer", "moreblocks:stair_mossycobble_outer" },
|
||||
{ "moreblocks:stair_cobble_half", "moreblocks:stair_mossycobble_half" },
|
||||
{ "moreblocks:slab_cobble_quarter", "moreblocks:slab_mossycobble_quarter" },
|
||||
{ "moreblocks:slab_cobble", "moreblocks:slab_mossycobble" },
|
||||
{ "moreblocks:slab_cobble_three_quarter", "moreblocks:slab_mossycobble_three_quarter" },
|
||||
{ "moreblocks:panel_cobble", "moreblocks:panel_mossycobble" },
|
||||
{ "moreblocks:micro_cobble", "moreblocks:micro_mossycobble" },
|
||||
{ "moreblocks:stair_cobble_alt", "moreblocks:stair_mossycobble_alt" },
|
||||
|
||||
{ "gloopblocks:cobble_road", "gloopblocks:cobble_road_mossy" },
|
||||
{ "gloopblocks:stair_cobble_road", "gloopblocks:stair_cobble_road_mossy" },
|
||||
{ "gloopblocks:slab_cobble_road", "gloopblocks:slab_cobble_road_mossy" },
|
||||
{ "gloopblocks:stair_cobble_road", "gloopblocks:stair_cobble_road_mossy" },
|
||||
{ "gloopblocks:stair_cobble_road_inner", "gloopblocks:stair_cobble_road_mossy_inner" },
|
||||
{ "gloopblocks:stair_cobble_road_outer", "gloopblocks:stair_cobble_road_mossy_outer" },
|
||||
{ "gloopblocks:stair_cobble_road_half", "gloopblocks:stair_cobble_road_mossy_half" },
|
||||
{ "gloopblocks:slab_cobble_road_quarter", "gloopblocks:slab_cobble_road_mossy_quarter" },
|
||||
{ "gloopblocks:slab_cobble_road", "gloopblocks:slab_cobble_road_mossy" },
|
||||
{ "gloopblocks:slab_cobble_road_three_quarter", "gloopblocks:slab_cobble_road_mossy_three_quarter" },
|
||||
{ "gloopblocks:panel_cobble_road", "gloopblocks:panel_cobble_road_mossy" },
|
||||
{ "gloopblocks:micro_cobble_road", "gloopblocks:micro_cobble_road_mossy" },
|
||||
{ "gloopblocks:stair_cobble_road_alt", "gloopblocks:stair_cobble_road_mossy_alt" },
|
||||
|
||||
{ "default:stonebrick", "gloopblocks:stone_brick_mossy" },
|
||||
{ "default:stair_stonebrick", "gloopblocks:stair_stone_brick_mossy" },
|
||||
{ "default:slab_stonebrick", "gloopblocks:slab_stone_brick_mossy" },
|
||||
{ "moreblocks:stair_stonebrick", "gloopblocks:stair_stone_brick_mossy" },
|
||||
{ "moreblocks:stair_stonebrick_inner", "gloopblocks:stair_stone_brick_mossy_inner" },
|
||||
{ "moreblocks:stair_stonebrick_outer", "gloopblocks:stair_stone_brick_mossy_outer" },
|
||||
{ "moreblocks:stair_stonebrick_half", "gloopblocks:stair_stone_brick_mossy_half" },
|
||||
{ "moreblocks:slab_stonebrick_quarter", "gloopblocks:slab_stone_brick_mossy_quarter" },
|
||||
{ "moreblocks:slab_stonebrick", "gloopblocks:slab_stone_brick_mossy" },
|
||||
{ "moreblocks:slab_stonebrick_three_quarter", "gloopblocks:slab_stone_brick_mossy_three_quarter" },
|
||||
{ "moreblocks:panel_stonebrick", "gloopblocks:panel_stone_brick_mossy" },
|
||||
{ "moreblocks:micro_stonebrick", "gloopblocks:micro_stone_brick_mossy" },
|
||||
{ "moreblocks:stair_stonebrick_alt", "gloopblocks:stair_stone_brick_mossy_alt" },
|
||||
|
||||
{ "default:stone", "gloopblocks:stone_mossy" },
|
||||
{ "default:stair_stone", "gloopblocks:stair_stone_mossy" },
|
||||
{ "default:slab_stone", "gloopblocks:slab_stone_mossy" },
|
||||
{ "moreblocks:stair_stone", "gloopblocks:stair_stone_mossy" },
|
||||
{ "moreblocks:stair_stone_inner", "gloopblocks:stair_stone_mossy_inner" },
|
||||
{ "moreblocks:stair_stone_outer", "gloopblocks:stair_stone_mossy_outer" },
|
||||
{ "moreblocks:stair_stone_half", "gloopblocks:stair_stone_mossy_half" },
|
||||
|
||||
{ "moreblocks:slab_stone_quarter", "gloopblocks:slab_stone_mossy_quarter" },
|
||||
{ "moreblocks:slab_stone", "gloopblocks:slab_stone_mossy" },
|
||||
{ "moreblocks:slab_stone_three_quarter", "gloopblocks:slab_stone_mossy_three_quarter" },
|
||||
{ "moreblocks:panel_stone", "gloopblocks:panel_stone_mossy" },
|
||||
{ "moreblocks:micro_stone", "gloopblocks:micro_stone_mossy" },
|
||||
{ "moreblocks:stair_stone_alt", "gloopblocks:stair_stone_mossy_alt" },
|
||||
})
|
||||
end
|
||||
|
||||
elseif minetest.get_modpath("stairs") then
|
||||
|
||||
--stairs.register_stair(subname, recipeitem, groups, images, description, sounds)
|
||||
|
||||
-- stairs:xxxx_stone_mossy ; xxxx = stair or slab
|
||||
stairs.register_stair_and_slab("stone_mossy", "gloopblocks:stone_mossy",
|
||||
{cracky=3},
|
||||
{"gloopblocks_stone_mossy.png"},
|
||||
S("Mossy Stone Stair"),
|
||||
S("Mossy Stone Slab"),
|
||||
default.node_sound_stone_defaults())
|
||||
|
||||
-- stairs:xxxx_mossycobble
|
||||
stairs.register_stair_and_slab("mossycobble", "default:mossycobble",
|
||||
{cracky=3},
|
||||
{"default_mossycobble.png"},
|
||||
S("Mossy Cobble Stair"),
|
||||
S("Mossy Cobble Slab"),
|
||||
default.node_sound_stone_defaults())
|
||||
|
||||
-- stairs:xxxx_stone_brick_mossy
|
||||
stairs.register_stair_and_slab("stone_brick_mossy", "gloopblocks:stone_brick_mossy",
|
||||
{cracky=3},
|
||||
{"gloopblocks_stone_brick_mossy.png"},
|
||||
S("Mossy Stone Brick Stair"),
|
||||
S("Mossy Stone Brick Slab"),
|
||||
default.node_sound_stone_defaults())
|
||||
|
||||
-- stairs:xxxx_cobble_road
|
||||
stairs.register_stair_and_slab("cobble_road", "gloopblocks:cobble_road",
|
||||
{cracky=3},
|
||||
{"gloopblocks_cobble_road.png"},
|
||||
S("Cobble Roadbed Stair"),
|
||||
S("Cobble Roadbed Slab"),
|
||||
default.node_sound_stone_defaults())
|
||||
|
||||
-- stairs:xxxx_cobble_road_mossy
|
||||
stairs.register_stair_and_slab("cobble_road_mossy", "gloopblocks:cobble_road_mossy",
|
||||
{cracky=3},
|
||||
{"gloopblocks_cobble_road_mossy.png"},
|
||||
S("Mossy Cobble Roadbed Stair"),
|
||||
S("Mossy Cobble Roadbed Slab"),
|
||||
default.node_sound_stone_defaults())
|
||||
|
||||
-- stairs:xxxx_cement
|
||||
stairs.register_stair_and_slab("cement", "gloopblocks:cement",
|
||||
{cracky=2},
|
||||
{"basic_materials_cement_block.png"},
|
||||
S("Cement Stair"),
|
||||
S("Cement Slab"),
|
||||
default.node_sound_stone_defaults())
|
||||
|
||||
-- stairs:xxxx_pavement
|
||||
stairs.register_stair_and_slab("pavement", "gloopblocks:pavement",
|
||||
{cracky=3, oddly_breakable_by_hand=3},
|
||||
{"gloopblocks_pavement.png"},
|
||||
S("Pavement Stair"),
|
||||
S("Pavement Slab"),
|
||||
default.node_sound_stone_defaults())
|
||||
|
||||
stairs.register_stair_and_slab("basalt", "gloopblocks:basalt",
|
||||
{cracky=2},
|
||||
{"gloopblocks_basalt.png"},
|
||||
S("Basalt Stair"),
|
||||
S("Basalt Slab"),
|
||||
default.node_sound_stone_defaults())
|
||||
|
||||
stairs.register_stair_and_slab("pumice", "gloopblocks:pumice",
|
||||
{cracky=3},
|
||||
{"gloopblocks_pumice.png"},
|
||||
S("Pumice Stair"),
|
||||
S("Pumice Slab"),
|
||||
default.node_sound_stone_defaults())
|
||||
|
||||
stairs.register_stair_and_slab("rainbow_block", "gloopblocks:rainbow_block",
|
||||
{cracky=3},
|
||||
{"gloopblocks_rainbow_block.png"},
|
||||
S("Rainbow Block Stair"),
|
||||
S("Rainbow Block Slab"),
|
||||
default.node_sound_defaults())
|
||||
|
||||
if minetest.setting_getbool("gloopblocks_mossy_conversion") ~= false then
|
||||
|
||||
gloopblocks_register_mossy_conversion({
|
||||
{ "default:cobble", "default:mossycobble" },
|
||||
{ "stairs:stair_cobble", "stairs:stair_mossycobble" },
|
||||
{ "stairs:slab_cobble", "stairs:slab_mossycobble" },
|
||||
{ "gloopblocks:cobble_road", "gloopblocks:cobble_road_mossy" },
|
||||
{ "stairs:stair_cobble_road", "stairs:stair_cobble_road_mossy" },
|
||||
{ "stairs:slab_cobble_road", "stairs:slab_cobble_road_mossy" },
|
||||
{ "default:stonebrick", "gloopblocks:stone_brick_mossy" },
|
||||
{ "stairs:stair_stonebrick", "stairs:stair_stone_brick_mossy" },
|
||||
{ "stairs:slab_stonebrick", "stairs:slab_stone_brick_mossy" },
|
||||
{ "default:stone", "gloopblocks:stone_mossy" },
|
||||
{ "stairs:stair_stone", "stairs:stair_stone_mossy" },
|
||||
{ "stairs:slab_stone", "stairs:slab_stone_mossy" },
|
||||
})
|
||||
end
|
||||
|
||||
minetest.register_alias("default:stair_mossycobble", "stairs:stair_mossycobble")
|
||||
minetest.register_alias("default:slab_mossycobble", "stairs:slab_mossycobble")
|
||||
minetest.register_alias("gloopblocks:stair_cobble_road", "stairs:stair_cobble_road")
|
||||
minetest.register_alias("gloopblocks:slab_cobble_road", "stairs:slab_cobble_road")
|
||||
minetest.register_alias("gloopblocks:stair_cobble_road_mossy", "stairs:stair_cobble_road_mossy")
|
||||
minetest.register_alias("gloopblocks:slab_cobble_road_mossy", "stairs:slab_cobble_road_mossy")
|
||||
minetest.register_alias("gloopblocks:stair_stone_brick_mossy", "stairs:stair_stone_brick_mossy")
|
||||
minetest.register_alias("gloopblocks:slab_stone_brick_mossy", "stairs:slab_stone_brick_mossy")
|
||||
minetest.register_alias("gloopblocks:stair_stone_mossy", "stairs:stair_stone_mossy")
|
||||
minetest.register_alias("gloopblocks:slab_stone_mossy", "stairs:slab_stone_mossy")
|
||||
minetest.register_alias("gloopblocks:stair_cement", "stairs:stair_cement")
|
||||
minetest.register_alias("gloopblocks:slab_cement", "stairs:slab_cement")
|
||||
minetest.register_alias("gloopblocks:stair_pavement", "stairs:stair_pavement")
|
||||
minetest.register_alias("gloopblocks:slab_pavement", "stairs:slab_pavement")
|
||||
minetest.register_alias("gloopblocks:stair_pumice", "stairs:stair_pumice")
|
||||
minetest.register_alias("gloopblocks:slab_pumice", "stairs:slab_pumice")
|
||||
minetest.register_alias("gloopblocks:stair_basalt", "stairs:stair_basalt")
|
||||
minetest.register_alias("gloopblocks:slab_basalt", "stairs:slab_basalt")
|
||||
minetest.register_alias("gloopblocks:stair_rainbow_block", "stairs:stair_rainbow_block")
|
||||
minetest.register_alias("gloopblocks:slab_rainbow_block", "stairs:slab_rainbow_block")
|
||||
end
|
||||
|
||||
-- Tools
|
||||
|
||||
minetest.register_tool("gloopblocks:pick_cement", {
|
||||
description = S("Cement Pickaxe"),
|
||||
inventory_image = "gloopblocks_cement_pick.png",
|
||||
tool_capabilities = {
|
||||
full_punch_interval = 1.0,
|
||||
max_drop_level=1,
|
||||
groupcaps={
|
||||
cracky={times={[1]=3.50, [2]=1.40, [3]=0.90}, uses=25, maxlevel=2}
|
||||
},
|
||||
damage_groups = {fleshy=4},
|
||||
},
|
||||
})
|
||||
|
||||
minetest.register_tool("gloopblocks:shovel_cement", {
|
||||
description = S("Cement Shovel"),
|
||||
inventory_image = "gloopblocks_cement_shovel.png",
|
||||
tool_capabilities = {
|
||||
full_punch_interval = 1.0,
|
||||
max_drop_level=1,
|
||||
groupcaps={
|
||||
crumbly={times={[1]=1.50, [2]=0.60, [3]=0.45}, uses=25, maxlevel=2}
|
||||
},
|
||||
damage_groups = {fleshy=4},
|
||||
},
|
||||
})
|
||||
|
||||
minetest.register_tool("gloopblocks:axe_cement", {
|
||||
description = S("Cement Axe"),
|
||||
inventory_image = "gloopblocks_cement_axe.png",
|
||||
tool_capabilities = {
|
||||
full_punch_interval = 1.0,
|
||||
max_drop_level=1,
|
||||
groupcaps={
|
||||
choppy={times={[1]=3.00, [2]=1.30, [3]=0.80}, uses=25, maxlevel=2},
|
||||
fleshy={times={[2]=1.20, [3]=0.65}, uses=30, maxlevel=1}
|
||||
},
|
||||
damage_groups = {fleshy=4},
|
||||
},
|
||||
})
|
||||
|
||||
minetest.register_tool("gloopblocks:sword_cement", {
|
||||
description = S("Cement Sword"),
|
||||
inventory_image = "gloopblocks_cement_sword.png",
|
||||
tool_capabilities = {
|
||||
full_punch_interval = 1.0,
|
||||
max_drop_level=1,
|
||||
groupcaps={
|
||||
fleshy={times={[1]=1.60, [2]=0.80, [3]=0.40}, uses=15, maxlevel=2},
|
||||
snappy={times={[2]=0.75, [3]=0.35}, uses=30, maxlevel=1},
|
||||
choppy={times={[3]=0.80}, uses=30, maxlevel=0}
|
||||
},
|
||||
damage_groups = {fleshy=6},
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_tool("gloopblocks:pick_evil", {
|
||||
description = S("Evil Pickaxe"),
|
||||
inventory_image = "gloopblocks_evil_pick.png",
|
||||
tool_capabilities = {
|
||||
full_punch_interval = 1.0,
|
||||
max_drop_level=3,
|
||||
groupcaps={
|
||||
cracky={times={[1]=0.10, [2]=0.10, [3]=0.10}, uses=10, maxlevel=2}
|
||||
},
|
||||
damage_groups = {fleshy=6},
|
||||
},
|
||||
})
|
||||
|
||||
minetest.register_tool("gloopblocks:shovel_evil", {
|
||||
description = S("Evil Shovel"),
|
||||
inventory_image = "gloopblocks_evil_shovel.png",
|
||||
tool_capabilities = {
|
||||
full_punch_interval = 1.0,
|
||||
max_drop_level=3,
|
||||
groupcaps={
|
||||
crumbly={times={[1]=0.05, [2]=0.05, [3]=0.05}, uses=10, maxlevel=2}
|
||||
},
|
||||
damage_groups = {fleshy=6},
|
||||
},
|
||||
})
|
||||
|
||||
minetest.register_tool("gloopblocks:axe_evil", {
|
||||
description = S("Evil Axe"),
|
||||
inventory_image = "gloopblocks_evil_axe.png",
|
||||
tool_capabilities = {
|
||||
full_punch_interval = 1.0,
|
||||
max_drop_level=3,
|
||||
groupcaps={
|
||||
choppy={times={[1]=0.15, [2]=0.15, [3]=0.15}, uses=10, maxlevel=2},
|
||||
fleshy={times={[1]=0.15, [2]=0.15, [3]=0.15}, uses=10, maxlevel=2}
|
||||
},
|
||||
damage_groups = {fleshy=6},
|
||||
},
|
||||
})
|
||||
|
||||
minetest.register_tool("gloopblocks:sword_evil", {
|
||||
description = S("Evil Sword"),
|
||||
inventory_image = "gloopblocks_evil_sword.png",
|
||||
tool_capabilities = {
|
||||
full_punch_interval = 1.0,
|
||||
max_drop_level=3,
|
||||
groupcaps={
|
||||
fleshy={times={[1]=0.20, [2]=0.20, [3]=0.20}, uses=10, maxlevel=2},
|
||||
snappy={times={[1]=0.20, [2]=0.20, [3]=0.20}, uses=10, maxlevel=2},
|
||||
choppy={times={[1]=0.20, [2]=0.20, [3]=0.20}, uses=10, maxlevel=2}
|
||||
},
|
||||
damage_groups = {fleshy=8},
|
||||
}
|
||||
})
|
||||
|
||||
-- Other items
|
||||
|
||||
minetest.register_craftitem("gloopblocks:evil_stick", {
|
||||
description = S("Evil Stick"),
|
||||
inventory_image = "gloopblocks_evil_stick.png",
|
||||
})
|
||||
|
||||
-- define lava-cooling-based nodes and hook into the default lavacooling
|
||||
-- functions to generate basalt, pumice, and obsidian
|
||||
|
||||
if minetest.setting_getbool("gloopblocks_lavacooling") ~= false then
|
||||
|
||||
minetest.register_node("gloopblocks:obsidian_cooled", {
|
||||
description = S("Obsidian"),
|
||||
tiles = {"default_obsidian.png"},
|
||||
is_ground_content = true,
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
groups = {cracky=1, level=2, not_in_creative_inventory=1},
|
||||
drop = "default:obsidian",
|
||||
after_place_node = function(pos, placer, itemstack, pointed_thing)
|
||||
minetest.add_node(pos, {name = "default:obsidian"})
|
||||
end
|
||||
})
|
||||
|
||||
minetest.register_node("gloopblocks:basalt_cooled", {
|
||||
description = S("Basalt"),
|
||||
tiles = {"gloopblocks_basalt.png"},
|
||||
groups = {cracky=2, not_in_creative_inventory=1},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
drop = "gloopblocks:basalt",
|
||||
after_place_node = function(pos, placer, itemstack, pointed_thing)
|
||||
minetest.add_node(pos, {name = "gloopblocks:basalt"})
|
||||
end
|
||||
})
|
||||
|
||||
minetest.register_node("gloopblocks:pumice_cooled", {
|
||||
description = S("Pumice"),
|
||||
tiles = {"gloopblocks_pumice.png"},
|
||||
groups = {cracky=3, not_in_creative_inventory=1},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
drop = "gloopblocks:pumice",
|
||||
after_place_node = function(pos, placer, itemstack, pointed_thing)
|
||||
minetest.add_node(pos, {name = "gloopblocks:pumice"})
|
||||
end
|
||||
})
|
||||
|
||||
local gloopblocks_search_nearby_nodes = function(pos, node)
|
||||
if minetest.get_node({x=pos.x-1, y=pos.y, z=pos.z}).name == node then return true end
|
||||
if minetest.get_node({x=pos.x+1, y=pos.y, z=pos.z}).name == node then return true end
|
||||
if minetest.get_node({x=pos.x, y=pos.y-1, z=pos.z}).name == node then return true end
|
||||
if minetest.get_node({x=pos.x, y=pos.y+1, z=pos.z}).name == node then return true end
|
||||
if minetest.get_node({x=pos.x, y=pos.y, z=pos.z-1}).name == node then return true end
|
||||
if minetest.get_node({x=pos.x, y=pos.y, z=pos.z+1}).name == node then return true end
|
||||
return false
|
||||
end
|
||||
|
||||
default.cool_lava = function(pos, node)
|
||||
if node.name == "default:lava_source" then
|
||||
if gloopblocks_search_nearby_nodes(pos,"default:water_source")
|
||||
or gloopblocks_search_nearby_nodes(pos,"default:water_flowing") then
|
||||
minetest.set_node(pos, {name="gloopblocks:obsidian_cooled"})
|
||||
end
|
||||
else -- Lava flowing
|
||||
if gloopblocks_search_nearby_nodes(pos,"default:water_source") then
|
||||
minetest.set_node(pos, {name="gloopblocks:basalt_cooled"})
|
||||
elseif gloopblocks_search_nearby_nodes(pos,"default:water_flowing") then
|
||||
minetest.set_node(pos, {name="gloopblocks:pumice_cooled"})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local fence_texture =
|
||||
"default_fence_overlay.png^default_steel_block.png^default_fence_overlay.png^[makealpha:255,126,126"
|
||||
|
||||
minetest.register_node("gloopblocks:fence_steel", {
|
||||
description = S("Steel Fence"),
|
||||
drawtype = "fencelike",
|
||||
tiles = {"default_steel_block.png"},
|
||||
inventory_image = fence_texture,
|
||||
wield_image = fence_texture,
|
||||
paramtype = "light",
|
||||
sunlight_propagates = true,
|
||||
is_ground_content = false,
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
fixed = {-1/7, -1/2, -1/7, 1/7, 1/2, 1/7},
|
||||
},
|
||||
groups = {choppy = 2, oddly_breakable_by_hand = 2 },
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
})
|
||||
|
||||
if minetest.get_modpath("worldedit") then
|
||||
function gloopblocks.liquid_ungrief(pos1, pos2, name)
|
||||
local count
|
||||
local p1to2 = minetest.pos_to_string(pos1).." and "..minetest.pos_to_string(pos2)
|
||||
local volume = worldedit.volume(pos1, pos2)
|
||||
minetest.chat_send_player(name, "Cleaning-up lava/water griefing between "..p1to2.."...")
|
||||
if volume > 1000000 then
|
||||
minetest.chat_send_player(name, "This operation could affect up to "..volume.." nodes. It may take a while.")
|
||||
end
|
||||
minetest.log("action", name.." performs lava/water greifing cleanup between "..p1to2..".")
|
||||
count = worldedit.replace(pos1, pos2, "default:lava_source", "air")
|
||||
count = worldedit.replace(pos1, pos2, "default:lava_flowing", "air")
|
||||
count = worldedit.replace(pos1, pos2, "default:water_source", "air")
|
||||
count = worldedit.replace(pos1, pos2, "default:water_flowing", "air")
|
||||
count = worldedit.replace(pos1, pos2, "default:river_water_source", "air")
|
||||
count = worldedit.replace(pos1, pos2, "default:river_water_flowing", "air")
|
||||
count = worldedit.replace(pos1, pos2, "gloopblocks:pumice_cooled", "air")
|
||||
count = worldedit.replace(pos1, pos2, "gloopblocks:basalt_cooled", "air")
|
||||
count = worldedit.replace(pos1, pos2, "gloopblocks:obsidian_cooled", "air")
|
||||
count = worldedit.fixlight(pos1, pos2)
|
||||
minetest.chat_send_player(name, "Operation completed.")
|
||||
end
|
||||
|
||||
minetest.register_chatcommand("/liquid_ungrief", {
|
||||
params = "[size]",
|
||||
privs = {worldedit = true},
|
||||
description = "Repairs greifing caused by spilling lava and water (and their \"cooling\" results)",
|
||||
func = function(name, params)
|
||||
local pos1 = worldedit.pos1[name]
|
||||
local pos2 = worldedit.pos2[name]
|
||||
if not pos1 or not pos2 then return end
|
||||
gloopblocks.liquid_ungrief(pos1, pos2, name)
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
dofile(minetest.get_modpath("gloopblocks").."/crafts.lua")
|
||||
|
||||
minetest.register_alias("nyancat:nyancat_rainbow", "gloopblocks:rainbow_block_horizontal")
|
||||
minetest.register_alias("default:nyancat_rainbow", "gloopblocks:rainbow_block_horizontal")
|
||||
dofile(MP.."/main.lua")
|
||||
dofile(MP.."/crafts.lua")
|
||||
dofile(MP.."/lava-handling.lua")
|
||||
|
||||
print(S("Gloopblocks Loaded!"))
|
||||
|
|
306
mods/gloopblocks/lava-handling.lua
Normal file
|
@ -0,0 +1,306 @@
|
|||
-- Load support for intllib.
|
||||
local MP = minetest.get_modpath(minetest.get_current_modname())
|
||||
local S, NS = dofile(MP.."/intllib.lua")
|
||||
|
||||
-- define lava-cooling-based nodes and hook into the default lavacooling
|
||||
-- functions to generate basalt, pumice, and obsidian
|
||||
|
||||
if minetest.setting_getbool("gloopblocks_lavacooling") ~= false then
|
||||
|
||||
minetest.register_node("gloopblocks:obsidian_cooled", {
|
||||
description = S("Obsidian"),
|
||||
tiles = {"default_obsidian.png"},
|
||||
is_ground_content = true,
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
groups = {cracky=1, level=2, not_in_creative_inventory=1},
|
||||
drop = "default:obsidian",
|
||||
after_place_node = function(pos, placer, itemstack, pointed_thing)
|
||||
minetest.add_node(pos, {name = "default:obsidian"})
|
||||
end
|
||||
})
|
||||
|
||||
minetest.register_node("gloopblocks:basalt_cooled", {
|
||||
description = S("Basalt"),
|
||||
tiles = {"gloopblocks_basalt.png"},
|
||||
groups = {cracky=2, not_in_creative_inventory=1},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
drop = "gloopblocks:basalt",
|
||||
after_place_node = function(pos, placer, itemstack, pointed_thing)
|
||||
minetest.add_node(pos, {name = "gloopblocks:basalt"})
|
||||
end
|
||||
})
|
||||
|
||||
minetest.register_node("gloopblocks:pumice_cooled", {
|
||||
description = S("Pumice"),
|
||||
tiles = {"gloopblocks_pumice.png"},
|
||||
groups = {cracky=3, not_in_creative_inventory=1},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
drop = "gloopblocks:pumice",
|
||||
after_place_node = function(pos, placer, itemstack, pointed_thing)
|
||||
minetest.add_node(pos, {name = "gloopblocks:pumice"})
|
||||
end
|
||||
})
|
||||
|
||||
local gloopblocks_search_nearby_nodes = function(pos, node)
|
||||
if minetest.get_node({x=pos.x-1, y=pos.y, z=pos.z}).name == node then return true end
|
||||
if minetest.get_node({x=pos.x+1, y=pos.y, z=pos.z}).name == node then return true end
|
||||
if minetest.get_node({x=pos.x, y=pos.y-1, z=pos.z}).name == node then return true end
|
||||
if minetest.get_node({x=pos.x, y=pos.y+1, z=pos.z}).name == node then return true end
|
||||
if minetest.get_node({x=pos.x, y=pos.y, z=pos.z-1}).name == node then return true end
|
||||
if minetest.get_node({x=pos.x, y=pos.y, z=pos.z+1}).name == node then return true end
|
||||
return false
|
||||
end
|
||||
|
||||
default.cool_lava = function(pos, node)
|
||||
if node.name == "default:lava_source" then
|
||||
if gloopblocks_search_nearby_nodes(pos,"default:water_source")
|
||||
or gloopblocks_search_nearby_nodes(pos,"default:water_flowing") then
|
||||
minetest.set_node(pos, {name="gloopblocks:obsidian_cooled"})
|
||||
end
|
||||
else -- Lava flowing
|
||||
if gloopblocks_search_nearby_nodes(pos,"default:water_source") then
|
||||
minetest.set_node(pos, {name="gloopblocks:basalt_cooled"})
|
||||
elseif gloopblocks_search_nearby_nodes(pos,"default:water_flowing") then
|
||||
minetest.set_node(pos, {name="gloopblocks:pumice_cooled"})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Allows lava to "bake" neighboring nodes (or reduce them to ashes)
|
||||
-- disabled by default. You probably don't want this on a creative server :-P
|
||||
|
||||
if minetest.setting_getbool("gloopblocks_lava_damage") then
|
||||
minetest.register_node("gloopblocks:ash_block", {
|
||||
description = S("Block of ashes"),
|
||||
tiles = {"gloopblocks_ashes.png"},
|
||||
groups = {crumbly = 3},
|
||||
sounds = default.node_sound_dirt_defaults(),
|
||||
})
|
||||
|
||||
local cbox = {
|
||||
type = "fixed",
|
||||
fixed = { -0.5, -0.5, -0.5, 0.5, -0.125, 0.5}
|
||||
}
|
||||
|
||||
minetest.register_node("gloopblocks:ash_pile", {
|
||||
description = S("Pile of ashes"),
|
||||
drawtype = "mesh",
|
||||
mesh = "gloopblocks_ash_pile.obj",
|
||||
tiles = {"gloopblocks_ashes.png"},
|
||||
selection_box = cbox,
|
||||
collision_box = cbox,
|
||||
groups = {crumbly = 3},
|
||||
sounds = default.node_sound_dirt_defaults(),
|
||||
})
|
||||
|
||||
gloopblocks.lava_damage_nodes = {
|
||||
["default:cactus"] = "gloopblocks:ash_block",
|
||||
["default:coalblock"] = "gloopblocks:ash_block",
|
||||
["default:desert_cobble"] = "default:desert_stone",
|
||||
["default:desert_sandstone"] = "default:desert_sandstone_block",
|
||||
["default:gravel"] = "default:cobble",
|
||||
["default:ice"] = "default:snowblock",
|
||||
["default:permafrost"] = "default:dirt",
|
||||
["default:permafrost_with_moss"] = "default:dirt",
|
||||
["default:sandstone"] = "default:sandstone_block",
|
||||
["default:silver_sandstone"] = "default:silver_sandstone_block",
|
||||
["default:snowblock"] = "default:water_source",
|
||||
|
||||
["basic_materials:cement_block"] = "basic_materials:concrete_block",
|
||||
["bedrock:deepstone"] = "default:stone",
|
||||
["building_blocks:hardwood"] = "default:coalblock",
|
||||
["building_blocks:Tar"] = "gloopblocks:pavement",
|
||||
["bushes:basket_empty"] = "gloopblocks:ash_pile",
|
||||
["bushes:basket_blackberry"] = "gloopblocks:ash_pile",
|
||||
["bushes:basket_blueberry"] = "gloopblocks:ash_pile",
|
||||
["bushes:basket_gooseberry"] = "gloopblocks:ash_pile",
|
||||
["bushes:basket_mixed_berry"] = "gloopblocks:ash_pile",
|
||||
["bushes:basket_raspberry"] = "gloopblocks:ash_pile",
|
||||
["bushes:basket_strawberry"] = "gloopblocks:ash_pile",
|
||||
["caverealms:thin_ice"] = "default:water_source",
|
||||
["castle_masonry:rubble"] = "default:desert_stone",
|
||||
["usesdirt:dirt_stone"] = "default:stone",
|
||||
["usesdirt:dirt_cobble_stone"] = "default:stone",
|
||||
["wool:dark_grey"] = "gloopblocks:ash_pile"
|
||||
}
|
||||
|
||||
gloopblocks.lava_damage_groups = {
|
||||
["wood"] = "default:coalblock",
|
||||
["tree"] = "default:coalblock",
|
||||
["soil"] = "gloopblocks:basalt",
|
||||
["leaves"] = "gloopblocks:ash_pile",
|
||||
["fence"] = "gloopblocks:ash_pile",
|
||||
["stone"] = "default:stone",
|
||||
}
|
||||
|
||||
if minetest.get_modpath("cottages") then
|
||||
gloopblocks.lava_damage_nodes["cottages:hay"] = "cottages:reet"
|
||||
gloopblocks.lava_damage_nodes["cottages:hay_bale"] = "cottages:reet"
|
||||
gloopblocks.lava_damage_nodes["cottages:hay_mat"] = "cottages:straw_mat"
|
||||
gloopblocks.lava_damage_nodes["cottages:reet"] = "gloopblocks:ash_pile"
|
||||
gloopblocks.lava_damage_nodes["cottages:roof_black"] = "gloopblocks:ash_pile"
|
||||
gloopblocks.lava_damage_nodes["cottages:roof_brown"] = "gloopblocks:ash_pile"
|
||||
gloopblocks.lava_damage_nodes["cottages:roof_red"] = "gloopblocks:ash_pile"
|
||||
gloopblocks.lava_damage_nodes["cottages:roof_reet"] = "gloopblocks:ash_pile"
|
||||
gloopblocks.lava_damage_nodes["cottages:roof_straw"] = "cottages:roof_reet"
|
||||
gloopblocks.lava_damage_nodes["cottages:roof_wood"] = "gloopblocks:ash_pile"
|
||||
gloopblocks.lava_damage_nodes["cottages:roof_connector_black"] = "gloopblocks:ash_pile"
|
||||
gloopblocks.lava_damage_nodes["cottages:roof_connector_brown"] = "gloopblocks:ash_pile"
|
||||
gloopblocks.lava_damage_nodes["cottages:roof_connector_red"] = "gloopblocks:ash_pile"
|
||||
gloopblocks.lava_damage_nodes["cottages:roof_connector_reet"] = "gloopblocks:ash_pile"
|
||||
gloopblocks.lava_damage_nodes["cottages:roof_connector_straw"] = "cottages:roof_connector_reet"
|
||||
gloopblocks.lava_damage_nodes["cottages:roof_connector_wood"] = "gloopblocks:ash_pile"
|
||||
gloopblocks.lava_damage_nodes["cottages:roof_flat_black"] = "gloopblocks:ash_pile"
|
||||
gloopblocks.lava_damage_nodes["cottages:roof_flat_brown"] = "gloopblocks:ash_pile"
|
||||
gloopblocks.lava_damage_nodes["cottages:roof_flat_red"] = "gloopblocks:ash_pile"
|
||||
gloopblocks.lava_damage_nodes["cottages:roof_flat_reet"] = "gloopblocks:ash_pile"
|
||||
gloopblocks.lava_damage_nodes["cottages:roof_flat_straw"] = "cottages:roof_flat_reet"
|
||||
gloopblocks.lava_damage_nodes["cottages:roof_flat_wood"] = "gloopblocks:ash_pile"
|
||||
gloopblocks.lava_damage_nodes["cottages:straw_ground"] = "cottages:loam"
|
||||
gloopblocks.lava_damage_nodes["cottages:loam"] = "default:dirt"
|
||||
gloopblocks.lava_damage_nodes["cottages:feldweg"] = "default:dirt"
|
||||
gloopblocks.lava_damage_nodes["cottages:feldweg_crossing"] = "default:dirt"
|
||||
gloopblocks.lava_damage_nodes["cottages:feldweg_curve"] = "default:dirt"
|
||||
gloopblocks.lava_damage_nodes["cottages:feldweg_end"] = "default:dirt"
|
||||
gloopblocks.lava_damage_nodes["cottages:feldweg_slope"] = "default:dirt"
|
||||
gloopblocks.lava_damage_nodes["cottages:feldweg_slope_long"] = "default:dirt"
|
||||
gloopblocks.lava_damage_nodes["cottages:feldweg_t_junction"] = "default:dirt"
|
||||
end
|
||||
|
||||
if minetest.get_modpath("dryplants") then
|
||||
gloopblocks.lava_damage_nodes["dryplants:wetreed"] = "dryplants:reed"
|
||||
gloopblocks.lava_damage_nodes["dryplants:wetreed_slab"] = "dryplants:reed_slab"
|
||||
gloopblocks.lava_damage_nodes["dryplants:wetreed_roof"] = "dryplants:reed_roof"
|
||||
gloopblocks.lava_damage_nodes["dryplants:wetreed_roof_corner"] = "dryplants:reed_roof_corner"
|
||||
gloopblocks.lava_damage_nodes["dryplants:wetreed_roof_corner_2"] = "dryplants:reed_roof_corner_2"
|
||||
gloopblocks.lava_damage_nodes["dryplants:reed"] = "gloopblocks:ash_pile"
|
||||
gloopblocks.lava_damage_nodes["dryplants:reed_slab"] = "gloopblocks:ash_pile"
|
||||
gloopblocks.lava_damage_nodes["dryplants:reed_roof"] = "gloopblocks:ash_pile"
|
||||
gloopblocks.lava_damage_nodes["dryplants:reed_roof_corner"] = "gloopblocks:ash_pile"
|
||||
gloopblocks.lava_damage_nodes["dryplants:reed_roof_corner_2"] = "gloopblocks:ash_pile"
|
||||
end
|
||||
|
||||
if minetest.get_modpath("wool") then
|
||||
gloopblocks.lava_damage_groups["wool"] = "wool:dark_grey"
|
||||
end
|
||||
|
||||
if minetest.get_modpath("bakedclay") then
|
||||
gloopblocks.lava_damage_nodes["default:clay"] = "bakedclay:dark_grey"
|
||||
gloopblocks.lava_damage_groups["bakedclay"] = "bakedclay:dark_grey"
|
||||
else
|
||||
gloopblocks.lava_damage_nodes["default:clay"] = "gloopblocks:basalt"
|
||||
end
|
||||
|
||||
if minetest.get_modpath("moreblocks") then
|
||||
gloopblocks.lava_damage_groups["sand"] = "moreblocks:coal_glass"
|
||||
else
|
||||
gloopblocks.lava_damage_groups["sand"] = "default:obsidian_glass"
|
||||
end
|
||||
|
||||
if minetest.get_modpath("farming") then
|
||||
gloopblocks.lava_damage_nodes["farming:soil_wet"] = "farming:soil"
|
||||
end
|
||||
|
||||
gloopblocks.lava_neighbors = {
|
||||
{ x=-1, y=-1, z=-1 },
|
||||
{ x=-1, y=-1, z= 0 },
|
||||
{ x=-1, y=-1, z= 1 },
|
||||
{ x=-1, y= 0, z=-1 },
|
||||
{ x=-1, y= 0, z= 0 },
|
||||
{ x=-1, y= 0, z= 1 },
|
||||
{ x=-1, y= 1, z=-1 },
|
||||
{ x=-1, y= 1, z= 0 },
|
||||
{ x=-1, y= 1, z= 1 },
|
||||
|
||||
{ x= 0, y=-1, z=-1 },
|
||||
{ x= 0, y=-1, z= 0 },
|
||||
{ x= 0, y=-1, z= 1 },
|
||||
{ x= 0, y= 0, z=-1 },
|
||||
-- { x= 0, y= 0, z= 0 }, -- will always be the lava node, so ignore this space
|
||||
{ x= 0, y= 0, z= 1 },
|
||||
{ x= 0, y= 1, z=-1 },
|
||||
{ x= 0, y= 1, z= 0 },
|
||||
{ x= 0, y= 1, z= 1 },
|
||||
|
||||
{ x= 1, y=-1, z=-1 },
|
||||
{ x= 1, y=-1, z= 0 },
|
||||
{ x= 1, y=-1, z= 1 },
|
||||
{ x= 1, y= 0, z=-1 },
|
||||
{ x= 1, y= 0, z= 0 },
|
||||
{ x= 1, y= 0, z= 1 },
|
||||
{ x= 1, y= 1, z=-1 },
|
||||
{ x= 1, y= 1, z= 0 },
|
||||
{ x= 1, y= 1, z= 1 },
|
||||
}
|
||||
|
||||
minetest.register_abm({
|
||||
nodenames = {"default:lava_source", "default:lava_flowing"},
|
||||
interval = 5,
|
||||
chance = 2,
|
||||
action = function(pos, node, active_object_count, active_object_count_wider)
|
||||
local r=gloopblocks.lava_neighbors[math.random(1, 26)]
|
||||
local pos2 = {
|
||||
x = pos.x + r.x,
|
||||
y = pos.y + r.y,
|
||||
z = pos.z + r.z
|
||||
}
|
||||
local newnode
|
||||
local chknode = minetest.get_node(pos2)
|
||||
local def = minetest.registered_items[chknode.name]
|
||||
|
||||
if gloopblocks.lava_damage_nodes[chknode.name] then
|
||||
newnode = gloopblocks.lava_damage_nodes[chknode.name]
|
||||
elseif def and def.drawtype == "plantlike" then
|
||||
newnode = "air"
|
||||
else
|
||||
for group, new in pairs(gloopblocks.lava_damage_groups) do
|
||||
if minetest.get_item_group(chknode.name, group) > 0 then
|
||||
newnode = new
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if newnode then
|
||||
minetest.set_node(pos2, {name = newnode, param2 = chknode.param2})
|
||||
end
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
if minetest.get_modpath("worldedit") then
|
||||
function gloopblocks.liquid_ungrief(pos1, pos2, name)
|
||||
local count
|
||||
local p1to2 = minetest.pos_to_string(pos1).." and "..minetest.pos_to_string(pos2)
|
||||
local volume = worldedit.volume(pos1, pos2)
|
||||
minetest.chat_send_player(name, "Cleaning-up lava/water griefing between "..p1to2.."...")
|
||||
if volume > 1000000 then
|
||||
minetest.chat_send_player(name, "This operation could affect up to "..volume.." nodes. It may take a while.")
|
||||
end
|
||||
minetest.log("action", name.." performs lava/water greifing cleanup between "..p1to2..".")
|
||||
count = worldedit.replace(pos1, pos2, "default:lava_source", "air")
|
||||
count = worldedit.replace(pos1, pos2, "default:lava_flowing", "air")
|
||||
count = worldedit.replace(pos1, pos2, "default:water_source", "air")
|
||||
count = worldedit.replace(pos1, pos2, "default:water_flowing", "air")
|
||||
count = worldedit.replace(pos1, pos2, "default:river_water_source", "air")
|
||||
count = worldedit.replace(pos1, pos2, "default:river_water_flowing", "air")
|
||||
count = worldedit.replace(pos1, pos2, "gloopblocks:pumice_cooled", "air")
|
||||
count = worldedit.replace(pos1, pos2, "gloopblocks:basalt_cooled", "air")
|
||||
count = worldedit.replace(pos1, pos2, "gloopblocks:obsidian_cooled", "air")
|
||||
count = worldedit.fixlight(pos1, pos2)
|
||||
minetest.chat_send_player(name, "Operation completed.")
|
||||
end
|
||||
|
||||
minetest.register_chatcommand("/liquid_ungrief", {
|
||||
params = "[size]",
|
||||
privs = {worldedit = true},
|
||||
description = "Repairs greifing caused by spilling lava and water (and their \"cooling\" results)",
|
||||
func = function(name, params)
|
||||
local pos1 = worldedit.pos1[name]
|
||||
local pos2 = worldedit.pos2[name]
|
||||
if not pos1 or not pos2 then return end
|
||||
gloopblocks.liquid_ungrief(pos1, pos2, name)
|
||||
end
|
||||
})
|
||||
end
|
839
mods/gloopblocks/main.lua
Normal file
|
@ -0,0 +1,839 @@
|
|||
-- Load support for intllib.
|
||||
local MP = minetest.get_modpath(minetest.get_current_modname())
|
||||
local S, NS = dofile(MP.."/intllib.lua")
|
||||
|
||||
-- Nodes
|
||||
|
||||
minetest.register_node("gloopblocks:rainbow_block_diagonal", {
|
||||
description = S("Diagonal Rainbow Block"),
|
||||
tiles = {"gloopblocks_rainbow_block.png"},
|
||||
is_ground_content = true,
|
||||
groups = {cracky=3},
|
||||
sounds = default.node_sound_defaults(),
|
||||
})
|
||||
minetest.register_alias("gloopblocks:rainbow_block", "gloopblocks:rainbow_block_diagonal")
|
||||
|
||||
minetest.register_node("gloopblocks:rainbow_block_horizontal", {
|
||||
description = S("Horizontal Rainbow Block"),
|
||||
tiles = {
|
||||
"gloopblocks_rainbow_horizontal.png^[transformR90",
|
||||
"gloopblocks_rainbow_horizontal.png^[transformR90",
|
||||
"gloopblocks_rainbow_horizontal.png"
|
||||
},
|
||||
paramtype = "light",
|
||||
light_source = default.LIGHT_MAX,
|
||||
paramtype2 = "facedir",
|
||||
groups = {cracky = 2},
|
||||
is_ground_content = false,
|
||||
sounds = default.node_sound_defaults(),
|
||||
})
|
||||
|
||||
minetest.register_node("gloopblocks:evil_block", {
|
||||
description = S("Evil Block"),
|
||||
tiles = {"gloopblocks_evil_block.png"},
|
||||
light_source = 5,
|
||||
is_ground_content = true,
|
||||
groups = {cracky=2},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
})
|
||||
|
||||
minetest.register_node("gloopblocks:basalt", {
|
||||
description = S("Basalt"),
|
||||
tiles = {"gloopblocks_basalt.png"},
|
||||
groups = {cracky=2},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
})
|
||||
|
||||
minetest.register_node("gloopblocks:pumice", {
|
||||
description = S("Pumice"),
|
||||
tiles = {"gloopblocks_pumice.png"},
|
||||
groups = {cracky=3},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
})
|
||||
|
||||
|
||||
minetest.register_node("gloopblocks:pavement", {
|
||||
description = S("Pavement"),
|
||||
tiles = {"gloopblocks_pavement.png"},
|
||||
groups = {cracky=3, oddly_breakable_by_hand=3},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
})
|
||||
|
||||
minetest.register_node("gloopblocks:oerkki_block", {
|
||||
drawtype = "nodebox",
|
||||
description = S("Oerkki Block"),
|
||||
paramtype = "light",
|
||||
paramtype2 = "facedir",
|
||||
tiles = {
|
||||
"gloopblocks_oerkkiblock_tb.png",
|
||||
"gloopblocks_oerkkiblock_tb.png",
|
||||
"gloopblocks_oerkkiblock_sides.png",
|
||||
"gloopblocks_oerkkiblock_sides.png",
|
||||
"gloopblocks_oerkkiblock_sides.png",
|
||||
"gloopblocks_oerkkiblock_front.png"
|
||||
},
|
||||
groups = {cracky=3, oddly_breakable_by_hand=3},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
fixed = { -0.5, -0.5, -0.5, 0.5, 0.5, 0.5 }
|
||||
},
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{-0.5, -0.5, -0.1875, 0.5, 0.5, 0.1875}, -- NodeBox1
|
||||
{-0.5, -0.5, -0.5, -0.4375, 0.5, 0.5}, -- NodeBox2
|
||||
{0.4375, -0.5, -0.5, 0.5, 0.5, 0.5}, -- NodeBox3
|
||||
{-0.5, 0.4375, -0.5, 0.5, 0.5, 0.5}, -- NodeBox4
|
||||
{-0.5, -0.5, -0.5, 0.5, -0.4375, 0.5}, -- NodeBox5
|
||||
{-0.5, -0.0625, -0.5, 0.5, 0.0625, 0.5}, -- NodeBox6
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_node("gloopblocks:stone_brick_mossy", {
|
||||
description = S("Mossy Stone Brick"),
|
||||
tiles = {"gloopblocks_stone_brick_mossy.png"},
|
||||
groups = {cracky=3, stone=1},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
})
|
||||
|
||||
minetest.register_node("gloopblocks:stone_mossy", {
|
||||
description = S("Mossy Stone"),
|
||||
tiles = {"gloopblocks_stone_mossy.png"},
|
||||
groups = {cracky=3, stone=1},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
drop = "default:mossycobble"
|
||||
})
|
||||
|
||||
minetest.register_node("gloopblocks:cobble_road", {
|
||||
description = S("Cobblestone Road Bed"),
|
||||
tiles = {"gloopblocks_cobble_road.png"},
|
||||
groups = {cracky=3, stone=1},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
})
|
||||
|
||||
minetest.register_node("gloopblocks:cobble_road_mossy", {
|
||||
description = S("Mossy Cobblestone Road Bed"),
|
||||
tiles = {"gloopblocks_cobble_road_mossy.png"},
|
||||
groups = {cracky=3, stone=1},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
})
|
||||
|
||||
minetest.register_node("gloopblocks:scaffolding", {
|
||||
description = S("Wooden Scaffold"),
|
||||
drawtype = "allfaces",
|
||||
paramtype = "light",
|
||||
sunlight_propagates = true,
|
||||
tiles = {"gloopblocks_scaffold.png"},
|
||||
groups = {choppy=3, oddly_breakable_by_hand=3},
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
})
|
||||
|
||||
minetest.register_alias("moreblocks:oerkkiblock", "gloopblocks:oerkki_block")
|
||||
minetest.register_alias("gloopblocks:obsidian", "default:obsidian")
|
||||
|
||||
-- Nodes imported from Usesdirt ================================================================================
|
||||
|
||||
if not minetest.get_modpath("usesdirt") then
|
||||
|
||||
local dirt_brick_tex = "default_dirt.png^gloopblocks_dirt_brick_overlay.png"
|
||||
local dirt_cobble_tex = "default_cobble.png^(default_dirt.png^[mask:gloopblocks_dirt_cobble_mask.png)"
|
||||
local dirt_stone_tex = "default_stone.png^(default_dirt.png^[mask:gloopblocks_dirt_stone_mask.png)"
|
||||
|
||||
local dirt_ladder_tex = "(default_dirt.png^[mask:gloopblocks_ladder_mask.png)^gloopblocks_ladder_overlay.png"
|
||||
local dirt_brick_ladder_tex = "(("..dirt_brick_tex..")^[mask:gloopblocks_ladder_mask.png)^gloopblocks_ladder_overlay.png"
|
||||
local dirt_cobble_ladder_tex = "(("..dirt_cobble_tex..")^[mask:gloopblocks_ladder_mask.png)^gloopblocks_ladder_overlay.png"
|
||||
local dirt_stone_ladder_tex = "(("..dirt_stone_tex..")^[mask:gloopblocks_ladder_mask.png)^gloopblocks_ladder_overlay.png"
|
||||
|
||||
minetest.register_node(":usesdirt:dirt_brick", {
|
||||
tiles = { dirt_brick_tex },
|
||||
description = "Dirt Brick",
|
||||
groups = {snappy=2,choppy=1,oddly_breakable_by_hand=2},
|
||||
})
|
||||
|
||||
minetest.register_node(":usesdirt:dirt_brick_ladder", {
|
||||
description = "Dirt Brick Ladder",
|
||||
drawtype = "signlike",
|
||||
tiles = { dirt_brick_ladder_tex },
|
||||
inventory_image = dirt_brick_ladder_tex,
|
||||
wield_image = dirt_brick_ladder_tex,
|
||||
paramtype = "light",
|
||||
paramtype2 = "wallmounted",
|
||||
is_ground_content = true,
|
||||
walkable = false,
|
||||
climbable = true,
|
||||
selection_box = {
|
||||
type = "wallmounted",
|
||||
--wall_top = = <default>
|
||||
--wall_bottom = = <default>
|
||||
--wall_side = = <default>
|
||||
},
|
||||
groups = {cracky=3, stone=2},
|
||||
legacy_wallmounted = true,
|
||||
})
|
||||
minetest.register_craft({
|
||||
output = 'usesdirt:dirt_brick_ladder 3',
|
||||
recipe = {
|
||||
{'usesdirt:dirt_brick', '', 'usesdirt:dirt_brick'},
|
||||
{'usesdirt:dirt_brick', 'usesdirt:dirt_brick','usesdirt:dirt_brick'},
|
||||
{'usesdirt:dirt_brick','','usesdirt:dirt_brick'},
|
||||
}
|
||||
})
|
||||
|
||||
default.register_fence(":usesdirt:dirt_brick_fence", {
|
||||
description = "Dirt Brick Fence",
|
||||
texture = dirt_brick_tex,
|
||||
inventory_image = "default_fence_overlay.png^("..dirt_brick_tex..")^default_fence_overlay.png^[makealpha:255,126,126",
|
||||
wield_image = "default_fence_overlay.png^("..dirt_brick_tex..")^default_fence_overlay.png^[makealpha:255,126,126",
|
||||
material = "usesdirt:dirt_brick",
|
||||
groups = {cracky=3, stone=2},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
check_for_pole = true
|
||||
})
|
||||
|
||||
if minetest.get_modpath("moreblocks") then
|
||||
minetest.register_craft({
|
||||
output = 'usesdirt:dirt_brick 24',
|
||||
recipe = {
|
||||
{'moreblocks:dirt_compressed', 'moreblocks:dirt_compressed', '' },
|
||||
{'moreblocks:dirt_compressed', 'moreblocks:dirt_compressed', '' }
|
||||
}
|
||||
})
|
||||
else
|
||||
minetest.register_craft({
|
||||
output = 'usesdirt:dirt_brick 6',
|
||||
recipe = {
|
||||
{'default:dirt', 'default:dirt', 'default:dirt'},
|
||||
{'default:dirt', 'default:dirt', 'default:dirt'},
|
||||
{'default:dirt', 'default:dirt', 'default:dirt'},
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
minetest.register_node(":usesdirt:dirt_ladder", {
|
||||
description = "Dirt Ladder",
|
||||
drawtype = "signlike",
|
||||
tiles = { dirt_ladder_tex },
|
||||
inventory_image = dirt_ladder_tex,
|
||||
wield_image = dirt_ladder_tex,
|
||||
paramtype = "light",
|
||||
paramtype2 = "wallmounted",
|
||||
is_ground_content = true,
|
||||
walkable = false,
|
||||
climbable = true,
|
||||
selection_box = {
|
||||
type = "wallmounted",
|
||||
--wall_top = = <default>
|
||||
--wall_bottom = = <default>
|
||||
--wall_side = = <default>
|
||||
},
|
||||
groups = {snappy=2,choppy=2,oddly_breakable_by_hand=3},
|
||||
legacy_wallmounted = true,
|
||||
})
|
||||
minetest.register_craft({
|
||||
output = 'usesdirt:dirt_ladder 3',
|
||||
recipe = {
|
||||
{'usesdirt:dirt_brick', '', 'usesdirt:dirt_brick'},
|
||||
{'usesdirt:dirt_brick', 'usesdirt:dirt_brick','usesdirt:dirt_brick'},
|
||||
{'usesdirt:dirt_brick','','usesdirt:dirt_brick'},
|
||||
}
|
||||
})
|
||||
|
||||
default.register_fence(":usesdirt:dirt_fence", {
|
||||
description = "Dirt Fence",
|
||||
texture = "default_dirt.png",
|
||||
inventory_image = "default_fence_overlay.png^default_dirt.png^default_fence_overlay.png^[makealpha:255,126,126",
|
||||
wield_image = "default_fence_overlay.png^default_dirt.png^default_fence_overlay.png^[makealpha:255,126,126",
|
||||
material = "default:dirt",
|
||||
groups = {snappy=2,choppy=1,oddly_breakable_by_hand=3},
|
||||
sounds = default.node_sound_dirt_defaults(),
|
||||
check_for_pole = true
|
||||
})
|
||||
|
||||
----
|
||||
|
||||
minetest.register_node(":usesdirt:dirt_cobble_stone", {
|
||||
tiles = { dirt_cobble_tex },
|
||||
description = "Dirt Cobble Stone",
|
||||
is_ground_content = true,
|
||||
groups = {cracky=3, stone=2},
|
||||
})
|
||||
minetest.register_craft({
|
||||
output = '"usesdirt:dirt_cobble_stone" 3',
|
||||
recipe = {
|
||||
{'usesdirt:dirt_brick', 'usesdirt:dirt_brick', 'usesdirt:dirt_brick'},
|
||||
{'usesdirt:dirt_brick', 'usesdirt:dirt_brick', 'usesdirt:dirt_brick'},
|
||||
{'usesdirt:dirt_brick', 'usesdirt:dirt_brick', 'usesdirt:dirt_brick'},
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_node(":usesdirt:dirt_cobble_stone_ladder", {
|
||||
description = "Dirt Cobble Stone Ladder",
|
||||
drawtype = "signlike",
|
||||
tiles = { dirt_cobble_ladder_tex },
|
||||
inventory_image = dirt_cobble_ladder_tex,
|
||||
wield_image = dirt_cobble_ladder_tex,
|
||||
paramtype = "light",
|
||||
paramtype2 = "wallmounted",
|
||||
is_ground_content = true,
|
||||
walkable = false,
|
||||
climbable = true,
|
||||
selection_box = {
|
||||
type = "wallmounted",
|
||||
--wall_top = = <default>
|
||||
--wall_bottom = = <default>
|
||||
--wall_side = = <default>
|
||||
},
|
||||
groups = {cracky=3, stone=2},
|
||||
legacy_wallmounted = true,
|
||||
})
|
||||
minetest.register_craft({
|
||||
output = 'usesdirt:dirt_cobble_stone_ladder 3',
|
||||
recipe = {
|
||||
{'usesdirt:dirt_cobble_stone', '', 'usesdirt:dirt_cobble_stone'},
|
||||
{'usesdirt:dirt_cobble_stone', 'usesdirt:dirt_cobble_stone','usesdirt:dirt_cobble_stone'},
|
||||
{'usesdirt:dirt_cobble_stone','','usesdirt:dirt_cobble_stone'},
|
||||
}
|
||||
})
|
||||
|
||||
default.register_fence(":usesdirt:dirt_cobble_stone_fence", {
|
||||
description = "Dirt Cobble Stone Fence",
|
||||
texture = dirt_cobble_tex,
|
||||
inventory_image = "default_fence_overlay.png^("..dirt_cobble_tex..")^default_fence_overlay.png^[makealpha:255,126,126",
|
||||
wield_image = "default_fence_overlay.png^("..dirt_cobble_tex..")^default_fence_overlay.png^[makealpha:255,126,126",
|
||||
material = "usesdirt:dirt_cobble_stone",
|
||||
groups = {cracky=3, stone=2},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
check_for_pole = true
|
||||
})
|
||||
|
||||
----
|
||||
|
||||
minetest.register_node(":usesdirt:dirt_stone", {
|
||||
tiles = { dirt_stone_tex },
|
||||
description = "Dirt Stone",
|
||||
is_ground_content = true,
|
||||
groups = {cracky=3, stone=2},
|
||||
})
|
||||
|
||||
minetest.register_node(":usesdirt:dirt_stone_ladder", {
|
||||
description = "Dirt Stone Ladder",
|
||||
drawtype = "signlike",
|
||||
tiles = { dirt_stone_ladder_tex },
|
||||
inventory_image = dirt_stone_ladder_tex,
|
||||
wield_image = dirt_stone_ladder_tex,
|
||||
paramtype = "light",
|
||||
paramtype2 = "wallmounted",
|
||||
is_ground_content = true,
|
||||
walkable = false,
|
||||
climbable = true,
|
||||
selection_box = {
|
||||
type = "wallmounted",
|
||||
--wall_top = = <default>
|
||||
--wall_bottom = = <default>
|
||||
--wall_side = = <default>
|
||||
},
|
||||
groups = {cracky=3, stone=2},
|
||||
legacy_wallmounted = true,
|
||||
})
|
||||
minetest.register_craft({
|
||||
output = 'usesdirt:dirt_stone_ladder 3',
|
||||
recipe = {
|
||||
{'usesdirt:dirt_stone', '', 'usesdirt:dirt_stone'},
|
||||
{'usesdirt:dirt_stone', 'usesdirt:dirt_stone','usesdirt:dirt_stone'},
|
||||
{'usesdirt:dirt_stone','','usesdirt:dirt_stone'},
|
||||
}
|
||||
})
|
||||
|
||||
default.register_fence(":usesdirt:dirt_stone_fence", {
|
||||
description = "Dirt Stone Fence",
|
||||
texture = dirt_stone_tex,
|
||||
inventory_image = "default_fence_overlay.png^("..dirt_stone_tex..")^default_fence_overlay.png^[makealpha:255,126,126",
|
||||
wield_image = "default_fence_overlay.png^("..dirt_stone_tex..")^default_fence_overlay.png^[makealpha:255,126,126",
|
||||
material = "usesdirt:dirt_stone",
|
||||
groups = {cracky=3, stone=2},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
check_for_pole = true
|
||||
})
|
||||
end
|
||||
|
||||
-- Stairs/slabs defs, conversion of normal -> mossy items
|
||||
|
||||
if minetest.setting_getbool("gloopblocks_mossy_conversion") ~= false then
|
||||
|
||||
function gloopblocks_register_mossy_conversion(mossyobjects)
|
||||
for i in ipairs(mossyobjects) do
|
||||
minetest.register_abm({
|
||||
nodenames = { mossyobjects[i][1] },
|
||||
neighbors = {"default:water_source", "default:water_flowing"},
|
||||
interval = 120,
|
||||
chance = 50,
|
||||
action = function(pos, node)
|
||||
if minetest.find_node_near(pos, 2, "air") then
|
||||
local fdir = node.param2
|
||||
minetest.add_node(pos, {name = mossyobjects[i][2], param2 = fdir})
|
||||
end
|
||||
end,
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if minetest.get_modpath("moreblocks") then
|
||||
|
||||
stairsplus:register_all("gloopblocks", "oerkki_block", "gloopblocks:oerkki_block", {
|
||||
description = S("Oerkki Block"),
|
||||
tiles = {
|
||||
"gloopblocks_oerkkiblock_tb.png",
|
||||
"gloopblocks_oerkkiblock_tb.png",
|
||||
"gloopblocks_oerkkiblock_sides.png",
|
||||
"gloopblocks_oerkkiblock_sides.png",
|
||||
"gloopblocks_oerkkiblock_sides.png",
|
||||
"gloopblocks_oerkkiblock_front.png"
|
||||
},
|
||||
groups = {cracky=2, not_in_creative_inventory=1},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
sunlight_propagates = true,
|
||||
})
|
||||
|
||||
stairsplus:register_all("gloopblocks", "stone_brick_mossy", "gloopblocks:stone_brick_mossy", {
|
||||
description = S("Mossy Stone Brick"),
|
||||
tiles = {"gloopblocks_stone_brick_mossy.png"},
|
||||
groups = {cracky=1, not_in_creative_inventory=1},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
sunlight_propagates = true,
|
||||
})
|
||||
|
||||
stairsplus:register_all("gloopblocks", "stone_mossy", "gloopblocks:stone_mossy", {
|
||||
description = S("Mossy Stone"),
|
||||
tiles = {"gloopblocks_stone_mossy.png"},
|
||||
groups = {cracky=1, not_in_creative_inventory=1},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
sunlight_propagates = true,
|
||||
})
|
||||
|
||||
stairsplus:register_all("gloopblocks", "cobble_road", "gloopblocks:cobble_road", {
|
||||
description = S("Cobblestone Roadbed"),
|
||||
tiles = {"gloopblocks_cobble_road.png"},
|
||||
groups = {cracky=3, stone=1, not_in_creative_inventory=1},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
sunlight_propagates = true,
|
||||
})
|
||||
|
||||
stairsplus:register_all("gloopblocks", "cobble_road_mossy", "gloopblocks:cobble_road_mossy", {
|
||||
description = S("Mossy Cobblestone Roadbed"),
|
||||
tiles = {"gloopblocks_cobble_road_mossy.png"},
|
||||
groups = {cracky=3, stone=1, not_in_creative_inventory=1},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
sunlight_propagates = true,
|
||||
})
|
||||
|
||||
stairsplus:register_all("gloopblocks", "pavement", "gloopblocks:pavement", {
|
||||
description = S("Pavement"),
|
||||
tiles = {"gloopblocks_pavement.png"},
|
||||
groups = {cracky=2, not_in_creative_inventory=1},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
sunlight_propagates = true,
|
||||
})
|
||||
|
||||
stairsplus:register_all("gloopblocks", "rainbow_block", "gloopblocks:rainbow_block", {
|
||||
description = S("Rainbow Block"),
|
||||
tiles = {"gloopblocks_rainbow_block.png"},
|
||||
groups = {cracky=3, not_in_creative_inventory=1},
|
||||
sounds = default.node_sound_defaults(),
|
||||
sunlight_propagates = true,
|
||||
})
|
||||
|
||||
stairsplus:register_all("gloopblocks", "evil_block", "gloopblocks:evil_block", {
|
||||
description = S("Evil Block"),
|
||||
tiles = {"gloopblocks_evil_block.png"},
|
||||
groups = {cracky=3, not_in_creative_inventory=1},
|
||||
sounds = default.node_sound_defaults(),
|
||||
light_source = 5,
|
||||
sunlight_propagates = true,
|
||||
})
|
||||
|
||||
stairsplus:register_all("gloopblocks", "basalt", "gloopblocks:basalt", {
|
||||
description = S("Basalt"),
|
||||
tiles = {"gloopblocks_basalt.png"},
|
||||
groups = {cracky=2, not_in_creative_inventory=1},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
sunlight_propagates = true,
|
||||
})
|
||||
|
||||
stairsplus:register_all("gloopblocks", "pumice", "gloopblocks:pumice", {
|
||||
description = S("Pumice"),
|
||||
tiles = {"gloopblocks_pumice.png"},
|
||||
groups = {cracky=3, not_in_creative_inventory=1},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
sunlight_propagates = true,
|
||||
})
|
||||
|
||||
stairsplus:register_all("gloopblocks", "gravel", "default:gravel", {
|
||||
description = S("Gravel"),
|
||||
tiles = {"default_gravel.png"},
|
||||
groups = {crumbly = 2, falling_node = 1, not_in_creative_inventory=1},
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
sunlight_propagates = false,
|
||||
})
|
||||
|
||||
if minetest.get_modpath("caverealms") then
|
||||
stairsplus:register_all("caverealms", "glow_crystal", "caverealms:glow_crystal", {
|
||||
description = S("Glow Crystal"),
|
||||
tiles = {"caverealms_glow_crystal.png"},
|
||||
groups = {cracky=3, not_in_creative_inventory=1},
|
||||
sounds = default.node_sound_glass_defaults(),
|
||||
light_source = 12,
|
||||
use_texture_alpha = true,
|
||||
paramtype="light",
|
||||
sunlight_propagates = true,
|
||||
})
|
||||
|
||||
stairsplus:register_all("caverealms", "glow_emerald", "caverealms:glow_emerald", {
|
||||
description = S("Glow Emerald"),
|
||||
tiles = {"caverealms_glow_emerald.png"},
|
||||
groups = {cracky=3, not_in_creative_inventory=1},
|
||||
sounds = default.node_sound_glass_defaults(),
|
||||
light_source = 12,
|
||||
use_texture_alpha = true,
|
||||
paramtype="light",
|
||||
sunlight_propagates = true,
|
||||
})
|
||||
|
||||
stairsplus:register_all("caverealms", "glow_mese", "caverealms:glow_mese", {
|
||||
description = S("Glow Mese"),
|
||||
tiles = {"caverealms_glow_mese.png"},
|
||||
groups = {cracky=3, not_in_creative_inventory=1},
|
||||
sounds = default.node_sound_glass_defaults(),
|
||||
light_source = 12,
|
||||
use_texture_alpha = true,
|
||||
paramtype="light",
|
||||
sunlight_propagates = true,
|
||||
})
|
||||
end
|
||||
|
||||
-- ABMs for mossy objects
|
||||
|
||||
if minetest.setting_getbool("gloopblocks_mossy_conversion") ~= false then
|
||||
|
||||
gloopblocks_register_mossy_conversion({
|
||||
{ "moreblocks:stair_cobble", "moreblocks:stair_mossycobble" },
|
||||
{ "moreblocks:stair_cobble_inner", "moreblocks:stair_mossycobble_inner" },
|
||||
{ "moreblocks:stair_cobble_outer", "moreblocks:stair_mossycobble_outer" },
|
||||
{ "moreblocks:stair_cobble_half", "moreblocks:stair_mossycobble_half" },
|
||||
{ "moreblocks:slab_cobble_quarter", "moreblocks:slab_mossycobble_quarter" },
|
||||
{ "moreblocks:slab_cobble", "moreblocks:slab_mossycobble" },
|
||||
{ "moreblocks:slab_cobble_three_quarter", "moreblocks:slab_mossycobble_three_quarter" },
|
||||
{ "moreblocks:panel_cobble", "moreblocks:panel_mossycobble" },
|
||||
{ "moreblocks:micro_cobble", "moreblocks:micro_mossycobble" },
|
||||
{ "moreblocks:stair_cobble_alt", "moreblocks:stair_mossycobble_alt" },
|
||||
|
||||
{ "gloopblocks:cobble_road", "gloopblocks:cobble_road_mossy" },
|
||||
{ "gloopblocks:stair_cobble_road", "gloopblocks:stair_cobble_road_mossy" },
|
||||
{ "gloopblocks:slab_cobble_road", "gloopblocks:slab_cobble_road_mossy" },
|
||||
{ "gloopblocks:stair_cobble_road", "gloopblocks:stair_cobble_road_mossy" },
|
||||
{ "gloopblocks:stair_cobble_road_inner", "gloopblocks:stair_cobble_road_mossy_inner" },
|
||||
{ "gloopblocks:stair_cobble_road_outer", "gloopblocks:stair_cobble_road_mossy_outer" },
|
||||
{ "gloopblocks:stair_cobble_road_half", "gloopblocks:stair_cobble_road_mossy_half" },
|
||||
{ "gloopblocks:slab_cobble_road_quarter", "gloopblocks:slab_cobble_road_mossy_quarter" },
|
||||
{ "gloopblocks:slab_cobble_road", "gloopblocks:slab_cobble_road_mossy" },
|
||||
{ "gloopblocks:slab_cobble_road_three_quarter", "gloopblocks:slab_cobble_road_mossy_three_quarter" },
|
||||
{ "gloopblocks:panel_cobble_road", "gloopblocks:panel_cobble_road_mossy" },
|
||||
{ "gloopblocks:micro_cobble_road", "gloopblocks:micro_cobble_road_mossy" },
|
||||
{ "gloopblocks:stair_cobble_road_alt", "gloopblocks:stair_cobble_road_mossy_alt" },
|
||||
|
||||
{ "default:stonebrick", "gloopblocks:stone_brick_mossy" },
|
||||
{ "default:stair_stonebrick", "gloopblocks:stair_stone_brick_mossy" },
|
||||
{ "default:slab_stonebrick", "gloopblocks:slab_stone_brick_mossy" },
|
||||
{ "moreblocks:stair_stonebrick", "gloopblocks:stair_stone_brick_mossy" },
|
||||
{ "moreblocks:stair_stonebrick_inner", "gloopblocks:stair_stone_brick_mossy_inner" },
|
||||
{ "moreblocks:stair_stonebrick_outer", "gloopblocks:stair_stone_brick_mossy_outer" },
|
||||
{ "moreblocks:stair_stonebrick_half", "gloopblocks:stair_stone_brick_mossy_half" },
|
||||
{ "moreblocks:slab_stonebrick_quarter", "gloopblocks:slab_stone_brick_mossy_quarter" },
|
||||
{ "moreblocks:slab_stonebrick", "gloopblocks:slab_stone_brick_mossy" },
|
||||
{ "moreblocks:slab_stonebrick_three_quarter", "gloopblocks:slab_stone_brick_mossy_three_quarter" },
|
||||
{ "moreblocks:panel_stonebrick", "gloopblocks:panel_stone_brick_mossy" },
|
||||
{ "moreblocks:micro_stonebrick", "gloopblocks:micro_stone_brick_mossy" },
|
||||
{ "moreblocks:stair_stonebrick_alt", "gloopblocks:stair_stone_brick_mossy_alt" },
|
||||
|
||||
{ "default:stone", "gloopblocks:stone_mossy" },
|
||||
{ "default:stair_stone", "gloopblocks:stair_stone_mossy" },
|
||||
{ "default:slab_stone", "gloopblocks:slab_stone_mossy" },
|
||||
{ "moreblocks:stair_stone", "gloopblocks:stair_stone_mossy" },
|
||||
{ "moreblocks:stair_stone_inner", "gloopblocks:stair_stone_mossy_inner" },
|
||||
{ "moreblocks:stair_stone_outer", "gloopblocks:stair_stone_mossy_outer" },
|
||||
{ "moreblocks:stair_stone_half", "gloopblocks:stair_stone_mossy_half" },
|
||||
|
||||
{ "moreblocks:slab_stone_quarter", "gloopblocks:slab_stone_mossy_quarter" },
|
||||
{ "moreblocks:slab_stone", "gloopblocks:slab_stone_mossy" },
|
||||
{ "moreblocks:slab_stone_three_quarter", "gloopblocks:slab_stone_mossy_three_quarter" },
|
||||
{ "moreblocks:panel_stone", "gloopblocks:panel_stone_mossy" },
|
||||
{ "moreblocks:micro_stone", "gloopblocks:micro_stone_mossy" },
|
||||
{ "moreblocks:stair_stone_alt", "gloopblocks:stair_stone_mossy_alt" },
|
||||
})
|
||||
end
|
||||
|
||||
elseif minetest.get_modpath("stairs") then
|
||||
|
||||
--stairs.register_stair(subname, recipeitem, groups, images, description, sounds)
|
||||
|
||||
-- stairs:xxxx_stone_mossy ; xxxx = stair or slab
|
||||
stairs.register_stair_and_slab("stone_mossy", "gloopblocks:stone_mossy",
|
||||
{cracky=3},
|
||||
{"gloopblocks_stone_mossy.png"},
|
||||
S("Mossy Stone Stair"),
|
||||
S("Mossy Stone Slab"),
|
||||
default.node_sound_stone_defaults())
|
||||
|
||||
-- stairs:xxxx_mossycobble
|
||||
stairs.register_stair_and_slab("mossycobble", "default:mossycobble",
|
||||
{cracky=3},
|
||||
{"default_mossycobble.png"},
|
||||
S("Mossy Cobble Stair"),
|
||||
S("Mossy Cobble Slab"),
|
||||
default.node_sound_stone_defaults())
|
||||
|
||||
-- stairs:xxxx_stone_brick_mossy
|
||||
stairs.register_stair_and_slab("stone_brick_mossy", "gloopblocks:stone_brick_mossy",
|
||||
{cracky=3},
|
||||
{"gloopblocks_stone_brick_mossy.png"},
|
||||
S("Mossy Stone Brick Stair"),
|
||||
S("Mossy Stone Brick Slab"),
|
||||
default.node_sound_stone_defaults())
|
||||
|
||||
-- stairs:xxxx_cobble_road
|
||||
stairs.register_stair_and_slab("cobble_road", "gloopblocks:cobble_road",
|
||||
{cracky=3},
|
||||
{"gloopblocks_cobble_road.png"},
|
||||
S("Cobble Roadbed Stair"),
|
||||
S("Cobble Roadbed Slab"),
|
||||
default.node_sound_stone_defaults())
|
||||
|
||||
-- stairs:xxxx_cobble_road_mossy
|
||||
stairs.register_stair_and_slab("cobble_road_mossy", "gloopblocks:cobble_road_mossy",
|
||||
{cracky=3},
|
||||
{"gloopblocks_cobble_road_mossy.png"},
|
||||
S("Mossy Cobble Roadbed Stair"),
|
||||
S("Mossy Cobble Roadbed Slab"),
|
||||
default.node_sound_stone_defaults())
|
||||
|
||||
-- stairs:xxxx_cement
|
||||
stairs.register_stair_and_slab("cement", "gloopblocks:cement",
|
||||
{cracky=2},
|
||||
{"basic_materials_cement_block.png"},
|
||||
S("Cement Stair"),
|
||||
S("Cement Slab"),
|
||||
default.node_sound_stone_defaults())
|
||||
|
||||
-- stairs:xxxx_pavement
|
||||
stairs.register_stair_and_slab("pavement", "gloopblocks:pavement",
|
||||
{cracky=3, oddly_breakable_by_hand=3},
|
||||
{"gloopblocks_pavement.png"},
|
||||
S("Pavement Stair"),
|
||||
S("Pavement Slab"),
|
||||
default.node_sound_stone_defaults())
|
||||
|
||||
stairs.register_stair_and_slab("basalt", "gloopblocks:basalt",
|
||||
{cracky=2},
|
||||
{"gloopblocks_basalt.png"},
|
||||
S("Basalt Stair"),
|
||||
S("Basalt Slab"),
|
||||
default.node_sound_stone_defaults())
|
||||
|
||||
stairs.register_stair_and_slab("pumice", "gloopblocks:pumice",
|
||||
{cracky=3},
|
||||
{"gloopblocks_pumice.png"},
|
||||
S("Pumice Stair"),
|
||||
S("Pumice Slab"),
|
||||
default.node_sound_stone_defaults())
|
||||
|
||||
stairs.register_stair_and_slab("rainbow_block", "gloopblocks:rainbow_block",
|
||||
{cracky=3},
|
||||
{"gloopblocks_rainbow_block.png"},
|
||||
S("Rainbow Block Stair"),
|
||||
S("Rainbow Block Slab"),
|
||||
default.node_sound_defaults())
|
||||
|
||||
if minetest.setting_getbool("gloopblocks_mossy_conversion") ~= false then
|
||||
|
||||
gloopblocks_register_mossy_conversion({
|
||||
{ "default:cobble", "default:mossycobble" },
|
||||
{ "stairs:stair_cobble", "stairs:stair_mossycobble" },
|
||||
{ "stairs:slab_cobble", "stairs:slab_mossycobble" },
|
||||
{ "gloopblocks:cobble_road", "gloopblocks:cobble_road_mossy" },
|
||||
{ "stairs:stair_cobble_road", "stairs:stair_cobble_road_mossy" },
|
||||
{ "stairs:slab_cobble_road", "stairs:slab_cobble_road_mossy" },
|
||||
{ "default:stonebrick", "gloopblocks:stone_brick_mossy" },
|
||||
{ "stairs:stair_stonebrick", "stairs:stair_stone_brick_mossy" },
|
||||
{ "stairs:slab_stonebrick", "stairs:slab_stone_brick_mossy" },
|
||||
{ "default:stone", "gloopblocks:stone_mossy" },
|
||||
{ "stairs:stair_stone", "stairs:stair_stone_mossy" },
|
||||
{ "stairs:slab_stone", "stairs:slab_stone_mossy" },
|
||||
})
|
||||
end
|
||||
|
||||
minetest.register_alias("default:stair_mossycobble", "stairs:stair_mossycobble")
|
||||
minetest.register_alias("default:slab_mossycobble", "stairs:slab_mossycobble")
|
||||
minetest.register_alias("gloopblocks:stair_cobble_road", "stairs:stair_cobble_road")
|
||||
minetest.register_alias("gloopblocks:slab_cobble_road", "stairs:slab_cobble_road")
|
||||
minetest.register_alias("gloopblocks:stair_cobble_road_mossy", "stairs:stair_cobble_road_mossy")
|
||||
minetest.register_alias("gloopblocks:slab_cobble_road_mossy", "stairs:slab_cobble_road_mossy")
|
||||
minetest.register_alias("gloopblocks:stair_stone_brick_mossy", "stairs:stair_stone_brick_mossy")
|
||||
minetest.register_alias("gloopblocks:slab_stone_brick_mossy", "stairs:slab_stone_brick_mossy")
|
||||
minetest.register_alias("gloopblocks:stair_stone_mossy", "stairs:stair_stone_mossy")
|
||||
minetest.register_alias("gloopblocks:slab_stone_mossy", "stairs:slab_stone_mossy")
|
||||
minetest.register_alias("gloopblocks:stair_cement", "stairs:stair_cement")
|
||||
minetest.register_alias("gloopblocks:slab_cement", "stairs:slab_cement")
|
||||
minetest.register_alias("gloopblocks:stair_pavement", "stairs:stair_pavement")
|
||||
minetest.register_alias("gloopblocks:slab_pavement", "stairs:slab_pavement")
|
||||
minetest.register_alias("gloopblocks:stair_pumice", "stairs:stair_pumice")
|
||||
minetest.register_alias("gloopblocks:slab_pumice", "stairs:slab_pumice")
|
||||
minetest.register_alias("gloopblocks:stair_basalt", "stairs:stair_basalt")
|
||||
minetest.register_alias("gloopblocks:slab_basalt", "stairs:slab_basalt")
|
||||
minetest.register_alias("gloopblocks:stair_rainbow_block", "stairs:stair_rainbow_block")
|
||||
minetest.register_alias("gloopblocks:slab_rainbow_block", "stairs:slab_rainbow_block")
|
||||
end
|
||||
|
||||
-- Tools
|
||||
|
||||
minetest.register_tool("gloopblocks:pick_cement", {
|
||||
description = S("Cement Pickaxe"),
|
||||
inventory_image = "gloopblocks_cement_pick.png",
|
||||
tool_capabilities = {
|
||||
full_punch_interval = 1.0,
|
||||
max_drop_level=1,
|
||||
groupcaps={
|
||||
cracky={times={[1]=3.50, [2]=1.40, [3]=0.90}, uses=25, maxlevel=2}
|
||||
},
|
||||
damage_groups = {fleshy=4},
|
||||
},
|
||||
})
|
||||
|
||||
minetest.register_tool("gloopblocks:shovel_cement", {
|
||||
description = S("Cement Shovel"),
|
||||
inventory_image = "gloopblocks_cement_shovel.png",
|
||||
tool_capabilities = {
|
||||
full_punch_interval = 1.0,
|
||||
max_drop_level=1,
|
||||
groupcaps={
|
||||
crumbly={times={[1]=1.50, [2]=0.60, [3]=0.45}, uses=25, maxlevel=2}
|
||||
},
|
||||
damage_groups = {fleshy=4},
|
||||
},
|
||||
})
|
||||
|
||||
minetest.register_tool("gloopblocks:axe_cement", {
|
||||
description = S("Cement Axe"),
|
||||
inventory_image = "gloopblocks_cement_axe.png",
|
||||
tool_capabilities = {
|
||||
full_punch_interval = 1.0,
|
||||
max_drop_level=1,
|
||||
groupcaps={
|
||||
choppy={times={[1]=3.00, [2]=1.30, [3]=0.80}, uses=25, maxlevel=2},
|
||||
fleshy={times={[2]=1.20, [3]=0.65}, uses=30, maxlevel=1}
|
||||
},
|
||||
damage_groups = {fleshy=4},
|
||||
},
|
||||
})
|
||||
|
||||
minetest.register_tool("gloopblocks:sword_cement", {
|
||||
description = S("Cement Sword"),
|
||||
inventory_image = "gloopblocks_cement_sword.png",
|
||||
tool_capabilities = {
|
||||
full_punch_interval = 1.0,
|
||||
max_drop_level=1,
|
||||
groupcaps={
|
||||
fleshy={times={[1]=1.60, [2]=0.80, [3]=0.40}, uses=15, maxlevel=2},
|
||||
snappy={times={[2]=0.75, [3]=0.35}, uses=30, maxlevel=1},
|
||||
choppy={times={[3]=0.80}, uses=30, maxlevel=0}
|
||||
},
|
||||
damage_groups = {fleshy=6},
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_tool("gloopblocks:pick_evil", {
|
||||
description = S("Evil Pickaxe"),
|
||||
inventory_image = "gloopblocks_evil_pick.png",
|
||||
tool_capabilities = {
|
||||
full_punch_interval = 1.0,
|
||||
max_drop_level=3,
|
||||
groupcaps={
|
||||
cracky={times={[1]=0.10, [2]=0.10, [3]=0.10}, uses=10, maxlevel=2}
|
||||
},
|
||||
damage_groups = {fleshy=6},
|
||||
},
|
||||
})
|
||||
|
||||
minetest.register_tool("gloopblocks:shovel_evil", {
|
||||
description = S("Evil Shovel"),
|
||||
inventory_image = "gloopblocks_evil_shovel.png",
|
||||
tool_capabilities = {
|
||||
full_punch_interval = 1.0,
|
||||
max_drop_level=3,
|
||||
groupcaps={
|
||||
crumbly={times={[1]=0.05, [2]=0.05, [3]=0.05}, uses=10, maxlevel=2}
|
||||
},
|
||||
damage_groups = {fleshy=6},
|
||||
},
|
||||
})
|
||||
|
||||
minetest.register_tool("gloopblocks:axe_evil", {
|
||||
description = S("Evil Axe"),
|
||||
inventory_image = "gloopblocks_evil_axe.png",
|
||||
tool_capabilities = {
|
||||
full_punch_interval = 1.0,
|
||||
max_drop_level=3,
|
||||
groupcaps={
|
||||
choppy={times={[1]=0.15, [2]=0.15, [3]=0.15}, uses=10, maxlevel=2},
|
||||
fleshy={times={[1]=0.15, [2]=0.15, [3]=0.15}, uses=10, maxlevel=2}
|
||||
},
|
||||
damage_groups = {fleshy=6},
|
||||
},
|
||||
})
|
||||
|
||||
minetest.register_tool("gloopblocks:sword_evil", {
|
||||
description = S("Evil Sword"),
|
||||
inventory_image = "gloopblocks_evil_sword.png",
|
||||
tool_capabilities = {
|
||||
full_punch_interval = 1.0,
|
||||
max_drop_level=3,
|
||||
groupcaps={
|
||||
fleshy={times={[1]=0.20, [2]=0.20, [3]=0.20}, uses=10, maxlevel=2},
|
||||
snappy={times={[1]=0.20, [2]=0.20, [3]=0.20}, uses=10, maxlevel=2},
|
||||
choppy={times={[1]=0.20, [2]=0.20, [3]=0.20}, uses=10, maxlevel=2}
|
||||
},
|
||||
damage_groups = {fleshy=8},
|
||||
}
|
||||
})
|
||||
|
||||
-- Other items
|
||||
|
||||
minetest.register_craftitem("gloopblocks:evil_stick", {
|
||||
description = S("Evil Stick"),
|
||||
inventory_image = "gloopblocks_evil_stick.png",
|
||||
})
|
||||
|
||||
|
||||
local fence_texture =
|
||||
"default_fence_overlay.png^default_steel_block.png^default_fence_overlay.png^[makealpha:255,126,126"
|
||||
|
||||
minetest.register_node("gloopblocks:fence_steel", {
|
||||
description = S("Steel Fence"),
|
||||
drawtype = "fencelike",
|
||||
tiles = {"default_steel_block.png"},
|
||||
inventory_image = fence_texture,
|
||||
wield_image = fence_texture,
|
||||
paramtype = "light",
|
||||
sunlight_propagates = true,
|
||||
is_ground_content = false,
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
fixed = {-1/7, -1/2, -1/7, 1/7, 1/2, 1/7},
|
||||
},
|
||||
groups = {choppy = 2, oddly_breakable_by_hand = 2 },
|
||||
sounds = default.node_sound_stone_defaults(),
|
||||
})
|
||||
|
||||
minetest.register_alias("nyancat:nyancat_rainbow", "gloopblocks:rainbow_block_horizontal")
|
||||
minetest.register_alias("default:nyancat_rainbow", "gloopblocks:rainbow_block_horizontal")
|
197
mods/gloopblocks/models/gloopblocks_ash_pile.obj
Normal file
|
@ -0,0 +1,197 @@
|
|||
# Blender v2.73 (sub 0) OBJ File: 'anthill.blend'
|
||||
# www.blender.org
|
||||
o Cylinder_Cylinder.001
|
||||
v 0.099056 -0.499969 -0.498228
|
||||
v 0.038417 -0.200463 -0.141682
|
||||
v 0.255808 -0.499933 -0.402046
|
||||
v 0.095605 -0.174690 -0.147239
|
||||
v 0.423075 -0.499913 -0.296918
|
||||
v 0.102439 -0.169033 -0.075679
|
||||
v 0.444026 -0.499843 -0.095234
|
||||
v 0.125298 -0.217477 -0.063343
|
||||
v 0.468682 -0.499958 0.074790
|
||||
v 0.157655 -0.214352 0.001348
|
||||
v 0.396548 -0.500000 0.246048
|
||||
v 0.133778 -0.189245 0.108513
|
||||
v 0.280708 -0.500000 0.383197
|
||||
v 0.070517 -0.218946 0.104754
|
||||
v 0.089852 -0.499943 0.434316
|
||||
v 0.048523 -0.205247 0.128681
|
||||
v -0.093309 -0.499902 0.467111
|
||||
v -0.039037 -0.211895 0.149030
|
||||
v -0.272965 -0.499875 0.396496
|
||||
v -0.108297 -0.175918 0.104100
|
||||
v -0.388317 -0.499877 0.239075
|
||||
v -0.139068 -0.179051 0.073370
|
||||
v -0.437531 -0.499999 0.063918
|
||||
v -0.141812 -0.255882 0.005117
|
||||
v -0.458429 -0.499805 -0.104397
|
||||
v -0.189265 -0.217436 -0.065303
|
||||
v -0.385597 -0.499914 -0.288584
|
||||
v -0.112692 -0.207830 -0.096879
|
||||
v -0.248347 -0.499927 -0.384586
|
||||
v -0.083136 -0.202256 -0.170048
|
||||
v -0.095346 -0.499958 -0.514449
|
||||
v -0.023049 -0.216681 -0.204058
|
||||
v 0.071880 -0.343843 -0.343933
|
||||
v 0.189128 -0.354687 -0.277980
|
||||
v 0.311273 -0.378789 -0.248498
|
||||
v 0.296760 -0.346318 -0.056661
|
||||
v 0.332231 -0.342427 0.044933
|
||||
v 0.259921 -0.360316 0.147910
|
||||
v 0.213270 -0.362883 0.253745
|
||||
v 0.059007 -0.360067 0.351374
|
||||
v -0.068448 -0.357957 0.335642
|
||||
v -0.164888 -0.343166 0.232553
|
||||
v -0.269761 -0.352370 0.140734
|
||||
v -0.367168 -0.370891 0.062326
|
||||
v -0.294491 -0.324099 -0.079712
|
||||
v -0.276314 -0.352585 -0.236032
|
||||
v -0.206169 -0.372829 -0.314307
|
||||
v -0.065547 -0.371444 -0.355380
|
||||
v 0.000709 -0.156135 -0.047193
|
||||
vt 0.572002 0.826281
|
||||
vt 0.535907 0.620231
|
||||
vt 0.597591 0.625892
|
||||
vt 0.604963 0.552988
|
||||
vt 0.830215 0.729053
|
||||
vt 0.629619 0.540419
|
||||
vt 0.814561 0.533613
|
||||
vt 0.664520 0.474514
|
||||
vt 0.638766 0.365335
|
||||
vt 0.774826 0.325198
|
||||
vt 0.570532 0.369165
|
||||
vt 0.724507 0.217375
|
||||
vt 0.546808 0.344788
|
||||
vt 0.452364 0.324057
|
||||
vt 0.420641 0.133939
|
||||
vt 0.377660 0.369831
|
||||
vt 0.316619 0.238965
|
||||
vt 0.344469 0.401138
|
||||
vt 0.203502 0.332509
|
||||
vt 0.341509 0.470674
|
||||
vt 0.290325 0.542416
|
||||
vt 0.176827 0.557096
|
||||
vt 0.372919 0.574586
|
||||
vt 0.196433 0.716353
|
||||
vt 0.404798 0.649130
|
||||
vt 0.469609 0.683778
|
||||
vt 0.272092 0.796098
|
||||
vt 0.770390 0.885486
|
||||
vt 0.973405 0.572910
|
||||
vt 0.591386 0.033412
|
||||
vt 0.226599 0.867698
|
||||
vt 0.423770 0.837943
|
||||
vt 0.601314 0.983475
|
||||
vt 0.078559 0.769893
|
||||
vt 0.000000 0.582245
|
||||
vt 0.098436 0.412390
|
||||
vt 0.075624 0.232320
|
||||
vt 0.200045 0.071942
|
||||
vt 0.558116 0.117912
|
||||
vt 0.922195 0.225217
|
||||
vt 0.852821 0.430110
|
||||
vt 0.698467 0.759089
|
||||
vt 0.495235 0.523967
|
||||
vt 0.391629 1.000000
|
||||
vt 0.022541 0.410768
|
||||
vt 0.797247 0.085491
|
||||
vt 0.393825 0.000000
|
||||
vt 0.950807 0.778383
|
||||
vt 1.000000 0.399692
|
||||
g Cylinder_Cylinder.001_None
|
||||
s 1
|
||||
f 33/1 2/2 4/3
|
||||
f 4/3 6/4 35/5
|
||||
f 35/5 6/4 8/6
|
||||
f 36/7 8/6 10/8
|
||||
f 10/8 12/9 38/10
|
||||
f 12/9 14/11 39/12
|
||||
f 39/12 14/11 16/13
|
||||
f 16/13 18/14 41/15
|
||||
f 18/14 20/16 42/17
|
||||
f 20/16 22/18 43/19
|
||||
f 43/19 22/18 24/20
|
||||
f 24/20 26/21 45/22
|
||||
f 26/21 28/23 46/24
|
||||
f 46/24 28/23 30/25
|
||||
f 32/26 2/2 33/1
|
||||
f 47/27 30/25 32/26
|
||||
f 3/28 7/29 15/30
|
||||
f 29/31 47/27 48/32
|
||||
f 48/32 33/1 1/33
|
||||
f 27/34 46/24 47/27
|
||||
f 25/35 45/22 46/24
|
||||
f 44/36 45/22 25/35
|
||||
f 21/37 43/19 44/36
|
||||
f 42/17 43/19 21/37
|
||||
f 41/15 42/17 19/38
|
||||
f 15/30 40/39 41/15
|
||||
f 39/12 40/39 15/30
|
||||
f 11/40 38/10 39/12
|
||||
f 37/41 38/10 11/40
|
||||
f 7/29 36/7 37/41
|
||||
f 35/5 36/7 7/29
|
||||
f 3/28 34/42 35/5
|
||||
f 33/1 34/42 3/28
|
||||
f 4/3 2/2 49/43
|
||||
f 2/2 32/26 49/43
|
||||
f 32/26 30/25 49/43
|
||||
f 30/25 28/23 49/43
|
||||
f 28/23 26/21 49/43
|
||||
f 26/21 24/20 49/43
|
||||
f 24/20 22/18 49/43
|
||||
f 22/18 20/16 49/43
|
||||
f 20/16 18/14 49/43
|
||||
f 18/14 16/13 49/43
|
||||
f 16/13 14/11 49/43
|
||||
f 14/11 12/9 49/43
|
||||
f 12/9 10/8 49/43
|
||||
f 10/8 8/6 49/43
|
||||
f 8/6 6/4 49/43
|
||||
f 6/4 4/3 49/43
|
||||
f 34/42 33/1 4/3
|
||||
f 34/42 4/3 35/5
|
||||
f 36/7 35/5 8/6
|
||||
f 37/41 36/7 10/8
|
||||
f 37/41 10/8 38/10
|
||||
f 38/10 12/9 39/12
|
||||
f 40/39 39/12 16/13
|
||||
f 40/39 16/13 41/15
|
||||
f 41/15 18/14 42/17
|
||||
f 42/17 20/16 43/19
|
||||
f 44/36 43/19 24/20
|
||||
f 44/36 24/20 45/22
|
||||
f 45/22 26/21 46/24
|
||||
f 47/27 46/24 30/25
|
||||
f 48/32 32/26 33/1
|
||||
f 48/32 47/27 32/26
|
||||
f 29/31 31/44 1/33
|
||||
f 23/45 27/34 29/31
|
||||
f 27/34 23/45 25/35
|
||||
f 15/30 19/38 21/37
|
||||
f 11/40 13/46 15/30
|
||||
f 15/30 17/47 19/38
|
||||
f 3/28 23/45 29/31
|
||||
f 29/31 1/33 3/28
|
||||
f 3/28 5/48 7/29
|
||||
f 7/29 9/49 11/40
|
||||
f 23/45 15/30 21/37
|
||||
f 15/30 23/45 3/28
|
||||
f 15/30 7/29 11/40
|
||||
f 31/44 29/31 48/32
|
||||
f 31/44 48/32 1/33
|
||||
f 29/31 27/34 47/27
|
||||
f 27/34 25/35 46/24
|
||||
f 23/45 44/36 25/35
|
||||
f 23/45 21/37 44/36
|
||||
f 19/38 42/17 21/37
|
||||
f 17/47 41/15 19/38
|
||||
f 17/47 15/30 41/15
|
||||
f 13/46 39/12 15/30
|
||||
f 13/46 11/40 39/12
|
||||
f 9/49 37/41 11/40
|
||||
f 9/49 7/29 37/41
|
||||
f 5/48 35/5 7/29
|
||||
f 5/48 3/28 35/5
|
||||
f 1/33 33/1 3/28
|
BIN
mods/gloopblocks/textures/gloopblocks_ashes.png
Normal file
After Width: | Height: | Size: 564 B |
|
@ -27,7 +27,7 @@ for _, c in ipairs(bookcolors) do
|
|||
local color, hue = unpack(c)
|
||||
|
||||
local function book_dig(pos, node, digger)
|
||||
if minetest.is_protected(pos, digger:get_player_name()) then return end
|
||||
if not digger or minetest.is_protected(pos, digger:get_player_name()) then return end
|
||||
local meta = minetest.get_meta(pos)
|
||||
local data = minetest.serialize({
|
||||
title = meta:get_string("title") or "",
|
||||
|
|
|
@ -162,7 +162,10 @@ if minetest.get_modpath("digilines") then
|
|||
if puncher:get_player_control().sneak then
|
||||
local name = puncher:get_player_name()
|
||||
player_last_clicked[name] = pos
|
||||
local form = "field[channel;Channel;]"
|
||||
local form = "formspec_version[4]"..
|
||||
"size[8,4]"..
|
||||
"button_exit[3,2.5;2,0.5;proceed;Proceed]"..
|
||||
"field[1.75,1.5;4.5,0.5;channel;Channel;]"
|
||||
minetest.show_formspec(name, "homedecor:lamp_set_channel", form)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -141,7 +141,10 @@ if minetest.get_modpath("digilines") then
|
|||
local name = puncher:get_player_name()
|
||||
player_last_clicked[name] = pos
|
||||
local meta = minetest.get_meta(pos)
|
||||
local form = "field[channel;Channel;]"
|
||||
local form = "formspec_version[4]"..
|
||||
"size[8,4]"..
|
||||
"button_exit[3,2.5;2,0.5;proceed;Proceed]"..
|
||||
"field[1.75,1.5;4.5,0.5;channel;Channel;]"
|
||||
minetest.show_formspec(name, "ilights:set_channel", form)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -201,7 +201,8 @@ function intllib.get_strings(modname, langcode)
|
|||
local modpath = minetest.get_modpath(modname)
|
||||
msgstr = { }
|
||||
for _, l in ipairs(get_locales(langcode)) do
|
||||
local t = intllib.load_strings(modpath.."/locale/"..l..".txt") or { }
|
||||
local t = intllib.load_strings(modpath.."/locale/"..modname.."."..l..".tr")
|
||||
or intllib.load_strings(modpath.."/locale/"..l..".txt") or { }
|
||||
for k, v in pairs(t) do
|
||||
msgstr[k] = msgstr[k] or v
|
||||
end
|
||||
|
|
|
@ -81,7 +81,7 @@ stepheight = 0.6,
|
|||
return
|
||||
end
|
||||
|
||||
local pos = self.object:get_pos()
|
||||
local pos = self.object:get_pos() ; if not pos then return end
|
||||
|
||||
minetest.add_item(pos, "mobs:egg")
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ local use_cmi = minetest.global_exists("cmi")
|
|||
|
||||
mobs = {
|
||||
mod = "redo",
|
||||
version = "20210114",
|
||||
version = "20210206",
|
||||
intllib = S,
|
||||
invis = minetest.global_exists("invisibility") and invisibility or {}
|
||||
}
|
||||
|
@ -539,12 +539,10 @@ local ray_line_of_sight = function(self, pos1, pos2)
|
|||
return true
|
||||
end
|
||||
|
||||
-- detect if using minetest 5.0 by searching for permafrost node
|
||||
local is_50 = minetest.registered_nodes["default:permafrost"]
|
||||
|
||||
function mob_class:line_of_sight(pos1, pos2, stepsize)
|
||||
|
||||
if is_50 then -- only use if minetest 5.0 is detected
|
||||
if minetest.raycast then -- only use if minetest 5.0 is detected
|
||||
return ray_line_of_sight(self, pos1, pos2)
|
||||
end
|
||||
|
||||
|
@ -1354,11 +1352,15 @@ function mob_class:breed()
|
|||
self.on_grown(self)
|
||||
else
|
||||
-- jump when fully grown so as not to fall into ground
|
||||
self.object:set_velocity({
|
||||
x = 0,
|
||||
y = self.jump_height,
|
||||
z = 0
|
||||
})
|
||||
-- self.object:set_velocity({
|
||||
-- x = 0,
|
||||
-- y = self.jump_height,
|
||||
-- z = 0
|
||||
-- })
|
||||
local pos = self.object:get_pos() ; if not pos then return end
|
||||
local ent = self.object:get_luaentity()
|
||||
pos.y = pos.y + (ent.collisionbox[2] * -1) - 0.4
|
||||
self.object:set_pos(pos)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -1458,6 +1460,8 @@ function mob_class:breed()
|
|||
effect(pos, 15, "tnt_smoke.png", 1, 2, 2, 15, 5)
|
||||
end
|
||||
|
||||
pos.y = pos.y + 0.5 -- spawn child a little higher
|
||||
|
||||
local mob = minetest.add_entity(pos, self.name)
|
||||
local ent2 = mob:get_luaentity()
|
||||
local textures = self.base_texture
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
name = mobs_sky
|
||||
name = mobs_bat
|
||||
depends = default, mobs
|
||||
optional_depends =
|
||||
description = Adds bats into your world.
|
||||
|
|
|
@ -89,8 +89,9 @@ circular_saw.names = {
|
|||
}
|
||||
|
||||
function circular_saw:get_cost(inv, stackname)
|
||||
local name = minetest.registered_aliases[stackname] or stackname
|
||||
for i, item in pairs(inv:get_list("output")) do
|
||||
if item:get_name() == stackname then
|
||||
if item:get_name() == name then
|
||||
return circular_saw.cost_in_microblocks[i]
|
||||
end
|
||||
end
|
||||
|
|
|
@ -19,6 +19,11 @@ minetest.register_node("woodsoils:dirt_with_leaves_1", {
|
|||
sounds = default.node_sound_dirt_defaults({
|
||||
footstep = {name="default_grass_footstep", gain=0.4},
|
||||
}),
|
||||
soil = {
|
||||
base = "woodsoils:dirt_with_leaves_1",
|
||||
dry = "farming:soil",
|
||||
wet = "farming:soil_wet"
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_node("woodsoils:dirt_with_leaves_2", {
|
||||
|
@ -37,6 +42,11 @@ minetest.register_node("woodsoils:dirt_with_leaves_2", {
|
|||
sounds = default.node_sound_dirt_defaults({
|
||||
footstep = {name="default_grass_footstep", gain=0.4},
|
||||
}),
|
||||
soil = {
|
||||
base = "woodsoils:dirt_with_leaves_2",
|
||||
dry = "farming:soil",
|
||||
wet = "farming:soil_wet"
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_node("woodsoils:grass_with_leaves_1", {
|
||||
|
@ -55,6 +65,11 @@ minetest.register_node("woodsoils:grass_with_leaves_1", {
|
|||
sounds = default.node_sound_dirt_defaults({
|
||||
footstep = {name="default_grass_footstep", gain=0.4},
|
||||
}),
|
||||
soil = {
|
||||
base = "woodsoils:grass_with_leaves_1",
|
||||
dry = "farming:soil",
|
||||
wet = "farming:soil_wet"
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_node("woodsoils:grass_with_leaves_2", {
|
||||
|
@ -73,6 +88,11 @@ minetest.register_node("woodsoils:grass_with_leaves_2", {
|
|||
sounds = default.node_sound_dirt_defaults({
|
||||
footstep = {name="default_grass_footstep", gain=0.4},
|
||||
}),
|
||||
soil = {
|
||||
base = "woodsoils:grass_with_leaves_2",
|
||||
dry = "farming:soil",
|
||||
wet = "farming:soil_wet"
|
||||
}
|
||||
})
|
||||
|
||||
-- For compatibility with older stuff
|
||||
|
|
|
@ -245,7 +245,7 @@ minetest.register_privilege("no_knockback", {
|
|||
give_to_singleplayer = false})
|
||||
|
||||
-- is player knock-back effect enabled?
|
||||
if minetest.settings:get_bool("player_knockback") ~= false then
|
||||
if minetest.settings:get_bool("player_knockback") == true then
|
||||
|
||||
minetest.register_entity("playerplus:temp", {
|
||||
physical = true,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# If enabled players are knocked back when hit by mobs or other players
|
||||
player_knockback (Player knockback) bool true
|
||||
player_knockback (Player knockback) bool false
|
||||
# If enabled the players use the old sneak glitch and movements
|
||||
old_sneak (Use old sneak glitch) bool false
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
|
||||
local S = protector.intllib
|
||||
local radius = (tonumber(minetest.setting_get("protector_radius")) or 5)
|
||||
local radius = (tonumber(minetest.settings:get("protector_radius")) or 5)
|
||||
|
||||
-- radius limiter (minetest cannot handle node volume of more than 4096000)
|
||||
if radius > 22 then radius = 22 end
|
||||
|
||||
local hud = {}
|
||||
local hud_timer = 0
|
||||
local hud_interval = (tonumber(minetest.setting_get("protector_hud_interval")) or 5)
|
||||
local hud_interval = (tonumber(minetest.settings:get("protector_hud_interval")) or 5)
|
||||
|
||||
if hud_interval > 0 then
|
||||
minetest.register_globalstep(function(dtime)
|
||||
|
|
|
@ -155,6 +155,7 @@ signs_lib.flip_walldir = {
|
|||
|
||||
-- Initialize character texture cache
|
||||
local ctexcache = {}
|
||||
local ctexcache_wide = {}
|
||||
|
||||
-- entity handling
|
||||
|
||||
|
@ -251,7 +252,10 @@ function signs_lib.set_obj_text(pos, text)
|
|||
local text_ansi = Utf8ToAnsi(text)
|
||||
local n = minetest.registered_nodes[minetest.get_node(pos).name]
|
||||
signs_lib.delete_objects(pos)
|
||||
signs_lib.spawn_entity(pos, signs_lib.make_sign_texture(split(text_ansi), pos) )
|
||||
-- only create sign entity for actual text
|
||||
if text_ansi and text_ansi ~= "" then
|
||||
signs_lib.spawn_entity(pos, signs_lib.make_sign_texture(split(text_ansi), pos) )
|
||||
end
|
||||
end
|
||||
|
||||
-- rotation
|
||||
|
@ -330,8 +334,10 @@ end
|
|||
local TP = signs_lib.path .. "/textures"
|
||||
-- Font file formatter
|
||||
local CHAR_FILE = "%s_%02x.png"
|
||||
local CHAR_FILE_WIDE = "%s_%s.png"
|
||||
-- Fonts path
|
||||
local CHAR_PATH = TP .. "/" .. CHAR_FILE
|
||||
local CHAR_PATH_WIDE = TP .. "/" .. CHAR_FILE_WIDE
|
||||
|
||||
-- Lots of overkill here. KISS advocates, go away, shoo! ;) -- kaeza
|
||||
|
||||
|
@ -391,6 +397,7 @@ end
|
|||
local function build_char_db(font_size)
|
||||
|
||||
local cw = {}
|
||||
local cw_wide = {}
|
||||
|
||||
-- To calculate average char width.
|
||||
local total_width = 0
|
||||
|
@ -406,20 +413,32 @@ local function build_char_db(font_size)
|
|||
end
|
||||
end
|
||||
|
||||
for i = 1, #signs_lib.wide_character_codes do
|
||||
local ch = signs_lib.wide_character_codes[i]
|
||||
local w, h = signs_lib.read_image_size(CHAR_PATH_WIDE:format("signs_lib_font_"..font_size.."px", ch))
|
||||
if w and h then
|
||||
cw_wide[ch] = w
|
||||
total_width = total_width + w
|
||||
char_count = char_count + 1
|
||||
end
|
||||
end
|
||||
|
||||
local cbw, cbh = signs_lib.read_image_size(TP.."/signs_lib_color_"..font_size.."px_n.png")
|
||||
assert(cbw and cbh, "error reading bg dimensions")
|
||||
return cw, cbw, cbh, (total_width / char_count)
|
||||
return cw, cbw, cbh, (total_width / char_count), cw_wide
|
||||
end
|
||||
|
||||
signs_lib.charwidth15,
|
||||
signs_lib.colorbgw15,
|
||||
signs_lib.lineheight15,
|
||||
signs_lib.avgwidth15 = build_char_db(15)
|
||||
signs_lib.avgwidth15,
|
||||
signs_lib.charwidth_wide15 = build_char_db(15)
|
||||
|
||||
signs_lib.charwidth31,
|
||||
signs_lib.colorbgw31,
|
||||
signs_lib.lineheight31,
|
||||
signs_lib.avgwidth31 = build_char_db(31)
|
||||
signs_lib.avgwidth31,
|
||||
signs_lib.charwidth_wide31 = build_char_db(31)
|
||||
|
||||
local sign_groups = {choppy=2, dig_immediate=2}
|
||||
local fences_with_sign = { }
|
||||
|
@ -455,7 +474,22 @@ local function char_tex(font_name, ch)
|
|||
end
|
||||
end
|
||||
|
||||
local function make_line_texture(line, lineno, pos, line_width, line_height, cwidth_tab, font_size, colorbgw)
|
||||
local function char_tex_wide(font_name, ch)
|
||||
if ctexcache_wide[font_name..ch] then
|
||||
return ctexcache_wide[font_name..ch], true
|
||||
else
|
||||
local exists, tex = file_exists(CHAR_PATH_WIDE:format(font_name, ch))
|
||||
if exists then
|
||||
tex = CHAR_FILE_WIDE:format(font_name, ch)
|
||||
else
|
||||
tex = CHAR_FILE:format(font_name, 0x5f)
|
||||
end
|
||||
ctexcache_wide[font_name..ch] = tex
|
||||
return tex, exists
|
||||
end
|
||||
end
|
||||
|
||||
local function make_line_texture(line, lineno, pos, line_width, line_height, cwidth_tab, font_size, colorbgw, cwidth_tab_wide)
|
||||
local width = 0
|
||||
local maxw = 0
|
||||
local font_name = "signs_lib_font_"..font_size.."px"
|
||||
|
@ -492,6 +526,27 @@ local function make_line_texture(line, lineno, pos, line_width, line_height, cwi
|
|||
local word_l = #word
|
||||
local i = 1
|
||||
while i <= word_l do
|
||||
local wide_c
|
||||
if "&#x" == word:sub(i, i + 2) then
|
||||
local j = i + 3
|
||||
local collected = ""
|
||||
while j <= word_l do
|
||||
local c = word:sub(j, j)
|
||||
if c == ";" then
|
||||
wide_c = collected
|
||||
break
|
||||
elseif c < "0" then
|
||||
break
|
||||
elseif "f" < c then
|
||||
break
|
||||
elseif ("9" < c) and (c < "a") then
|
||||
break
|
||||
else
|
||||
collected = collected .. c
|
||||
j = j + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
local c = word:sub(i, i)
|
||||
if c == "#" then
|
||||
local cc = tonumber(word:sub(i+1, i+1), 16)
|
||||
|
@ -499,6 +554,25 @@ local function make_line_texture(line, lineno, pos, line_width, line_height, cwi
|
|||
i = i + 1
|
||||
cur_color = cc
|
||||
end
|
||||
elseif wide_c then
|
||||
local w = cwidth_tab_wide[wide_c]
|
||||
if w then
|
||||
width = width + w + 1
|
||||
if width >= (line_width - cwidth_tab[" "]) then
|
||||
width = 0
|
||||
else
|
||||
maxw = math_max(width, maxw)
|
||||
end
|
||||
if #chars < MAX_INPUT_CHARS then
|
||||
table.insert(chars, {
|
||||
off = ch_offs,
|
||||
tex = char_tex_wide(font_name, wide_c),
|
||||
col = ("%X"):format(cur_color),
|
||||
})
|
||||
end
|
||||
ch_offs = ch_offs + w
|
||||
end
|
||||
i = i + #wide_c + 3
|
||||
else
|
||||
local w = cwidth_tab[c]
|
||||
if w then
|
||||
|
@ -578,6 +652,7 @@ function signs_lib.make_sign_texture(lines, pos)
|
|||
local line_width
|
||||
local line_height
|
||||
local char_width
|
||||
local char_width_wide
|
||||
local colorbgw
|
||||
local widemult = 1
|
||||
|
||||
|
@ -590,12 +665,14 @@ function signs_lib.make_sign_texture(lines, pos)
|
|||
line_width = math.floor(signs_lib.avgwidth31 * def.chars_per_line) * (def.horiz_scaling * widemult)
|
||||
line_height = signs_lib.lineheight31
|
||||
char_width = signs_lib.charwidth31
|
||||
char_width_wide = signs_lib.charwidth_wide31
|
||||
colorbgw = signs_lib.colorbgw31
|
||||
else
|
||||
font_size = 15
|
||||
line_width = math.floor(signs_lib.avgwidth15 * def.chars_per_line) * (def.horiz_scaling * widemult)
|
||||
line_height = signs_lib.lineheight15
|
||||
char_width = signs_lib.charwidth15
|
||||
char_width_wide = signs_lib.charwidth_wide15
|
||||
colorbgw = signs_lib.colorbgw15
|
||||
end
|
||||
|
||||
|
@ -604,7 +681,7 @@ function signs_lib.make_sign_texture(lines, pos)
|
|||
local lineno = 0
|
||||
for i = 1, #lines do
|
||||
if lineno >= def.number_of_lines then break end
|
||||
local linetex, ln = make_line_texture(lines[i], lineno, pos, line_width, line_height, char_width, font_size, colorbgw)
|
||||
local linetex, ln = make_line_texture(lines[i], lineno, pos, line_width, line_height, char_width, font_size, colorbgw, char_width_wide)
|
||||
table.insert(texture, linetex)
|
||||
lineno = ln + 1
|
||||
end
|
||||
|
|
|
@ -203,6 +203,32 @@ local utf8_decode = {
|
|||
[210] = {[144] = "\165", [145] = "\180"}
|
||||
}
|
||||
|
||||
local wide_character_codes = {
|
||||
}
|
||||
|
||||
signs_lib.unicode_install = function(
|
||||
numbers
|
||||
)
|
||||
local scope = utf8_decode
|
||||
for i = 1,#numbers-2 do
|
||||
if not scope[numbers[i]] then
|
||||
scope[numbers[i]] = {}
|
||||
end
|
||||
scope = scope[numbers[i]]
|
||||
end
|
||||
scope[numbers[#numbers-1]] = "&#x" .. numbers[#numbers] .. ";"
|
||||
table.insert(
|
||||
wide_character_codes,
|
||||
numbers[#numbers]
|
||||
)
|
||||
end
|
||||
|
||||
signs_lib.unicode_install({38,"26"})
|
||||
|
||||
dofile(signs_lib.path.."/nonascii-de.lua")
|
||||
dofile(signs_lib.path.."/nonascii-fr.lua")
|
||||
dofile(signs_lib.path.."/nonascii-pl.lua")
|
||||
|
||||
local nmdc = {
|
||||
[36] = "$",
|
||||
[124] = "|"
|
||||
|
@ -230,36 +256,33 @@ function AnsiToUtf8(s)
|
|||
end
|
||||
|
||||
function Utf8ToAnsi(s)
|
||||
local a, j, r, b = 0, 0, ""
|
||||
local a, j, r, b, scope = 0, 0, ""
|
||||
for i = 1, s and s:len() or 0 do
|
||||
b = s:byte(i)
|
||||
if b < 128 then
|
||||
if b == 0x26 then
|
||||
r = r .. "&"
|
||||
elseif b < 128 then
|
||||
if nmdc[b] then
|
||||
r = r .. nmdc[b]
|
||||
else
|
||||
r = r .. string.char(b)
|
||||
end
|
||||
elseif a == 2 then
|
||||
a, j = a - 1, b
|
||||
elseif a == 1 then
|
||||
--if j == nil or b == nil then return r end
|
||||
--print(j)
|
||||
--print(b)
|
||||
--local ansi = utf8_decode[j]
|
||||
--if ansi == nil then return r end
|
||||
--if ansi[b] == nil then return r end
|
||||
if utf8_decode[j] then
|
||||
if utf8_decode[j][b] then
|
||||
a, r = a - 1, r .. utf8_decode[j][b]
|
||||
elseif scope then
|
||||
if scope[b] then
|
||||
scope = scope[b]
|
||||
if "string" == type(scope) then
|
||||
r, scope = r .. scope
|
||||
end
|
||||
else
|
||||
r, scope = r .. "_"
|
||||
end
|
||||
elseif b == 226 then
|
||||
a = 2
|
||||
elseif b == 194 or b == 208 or b == 209 or b == 210 then
|
||||
j, a = b, 1
|
||||
elseif utf8_decode[b] then
|
||||
scope = utf8_decode[b]
|
||||
else
|
||||
r = r .. "_"
|
||||
end
|
||||
end
|
||||
return r
|
||||
end
|
||||
|
||||
signs_lib.wide_character_codes = wide_character_codes
|
||||
|
|
|
@ -10,7 +10,7 @@ signs_lib.path = minetest.get_modpath(minetest.get_current_modname())
|
|||
local S, NS = dofile(signs_lib.path .. "/intllib.lua")
|
||||
signs_lib.gettext = S
|
||||
|
||||
dofile(signs_lib.path.."/api.lua")
|
||||
dofile(signs_lib.path.."/encoding.lua")
|
||||
dofile(signs_lib.path.."/api.lua")
|
||||
dofile(signs_lib.path.."/standard_signs.lua")
|
||||
dofile(signs_lib.path.."/compat.lua")
|
||||
|
|
7
mods/signs_lib/nonascii-de.lua
Normal file
|
@ -0,0 +1,7 @@
|
|||
signs_lib.unicode_install({195,132,"00c4"})
|
||||
signs_lib.unicode_install({195,150,"00d6"})
|
||||
signs_lib.unicode_install({195,156,"00dc"})
|
||||
signs_lib.unicode_install({195,159,"00df"})
|
||||
signs_lib.unicode_install({195,164,"00e4"})
|
||||
signs_lib.unicode_install({195,182,"00f6"})
|
||||
signs_lib.unicode_install({195,188,"00fc"})
|
16
mods/signs_lib/nonascii-fr.lua
Normal file
|
@ -0,0 +1,16 @@
|
|||
signs_lib.unicode_install({195,128,"00c0"})
|
||||
signs_lib.unicode_install({195,134,"00c6"})
|
||||
signs_lib.unicode_install({195,135,"00c7"})
|
||||
signs_lib.unicode_install({195,136,"00c8"})
|
||||
signs_lib.unicode_install({195,137,"00c9"})
|
||||
signs_lib.unicode_install({195,138,"00ca"})
|
||||
signs_lib.unicode_install({195,148,"00d4"})
|
||||
signs_lib.unicode_install({195,153,"00d9"})
|
||||
signs_lib.unicode_install({195,160,"00e0"})
|
||||
signs_lib.unicode_install({195,166,"00e6"})
|
||||
signs_lib.unicode_install({195,167,"00e7"})
|
||||
signs_lib.unicode_install({195,168,"00e8"})
|
||||
signs_lib.unicode_install({195,169,"00e9"})
|
||||
signs_lib.unicode_install({195,170,"00ea"})
|
||||
signs_lib.unicode_install({195,180,"00f4"})
|
||||
signs_lib.unicode_install({195,185,"00f9"})
|
16
mods/signs_lib/nonascii-pl.lua
Normal file
|
@ -0,0 +1,16 @@
|
|||
signs_lib.unicode_install({195,147,"00d3"})
|
||||
signs_lib.unicode_install({195,179,"00f3"})
|
||||
signs_lib.unicode_install({196,132,"0104"})
|
||||
signs_lib.unicode_install({196,133,"0105"})
|
||||
signs_lib.unicode_install({196,134,"0106"})
|
||||
signs_lib.unicode_install({196,135,"0107"})
|
||||
signs_lib.unicode_install({196,152,"0118"})
|
||||
signs_lib.unicode_install({196,153,"0119"})
|
||||
signs_lib.unicode_install({197,129,"0141"})
|
||||
signs_lib.unicode_install({197,130,"0142"})
|
||||
signs_lib.unicode_install({197,154,"015a"})
|
||||
signs_lib.unicode_install({197,155,"015b"})
|
||||
signs_lib.unicode_install({197,185,"0179"})
|
||||
signs_lib.unicode_install({197,186,"017a"})
|
||||
signs_lib.unicode_install({197,187,"017b"})
|
||||
signs_lib.unicode_install({197,188,"017c"})
|
BIN
mods/signs_lib/textures/signs_lib_font_15px_00c0.png
Normal file
After Width: | Height: | Size: 337 B |
BIN
mods/signs_lib/textures/signs_lib_font_15px_00c4.png
Normal file
After Width: | Height: | Size: 337 B |
BIN
mods/signs_lib/textures/signs_lib_font_15px_00c6.png
Normal file
After Width: | Height: | Size: 345 B |
BIN
mods/signs_lib/textures/signs_lib_font_15px_00c7.png
Normal file
After Width: | Height: | Size: 334 B |
BIN
mods/signs_lib/textures/signs_lib_font_15px_00c8.png
Normal file
After Width: | Height: | Size: 323 B |
BIN
mods/signs_lib/textures/signs_lib_font_15px_00c9.png
Normal file
After Width: | Height: | Size: 324 B |
BIN
mods/signs_lib/textures/signs_lib_font_15px_00ca.png
Normal file
After Width: | Height: | Size: 325 B |
BIN
mods/signs_lib/textures/signs_lib_font_15px_00d3.png
Normal file
After Width: | Height: | Size: 331 B |
BIN
mods/signs_lib/textures/signs_lib_font_15px_00d4.png
Normal file
After Width: | Height: | Size: 341 B |
BIN
mods/signs_lib/textures/signs_lib_font_15px_00d6.png
Normal file
After Width: | Height: | Size: 341 B |
BIN
mods/signs_lib/textures/signs_lib_font_15px_00d9.png
Normal file
After Width: | Height: | Size: 324 B |
BIN
mods/signs_lib/textures/signs_lib_font_15px_00dc.png
Normal file
After Width: | Height: | Size: 326 B |
BIN
mods/signs_lib/textures/signs_lib_font_15px_00df.png
Normal file
After Width: | Height: | Size: 332 B |
BIN
mods/signs_lib/textures/signs_lib_font_15px_00e0.png
Normal file
After Width: | Height: | Size: 332 B |