Adapt wagons to new path system

Still outstanding: trains
This commit is contained in:
orwell96 2018-04-19 11:38:00 +02:00
parent 3420a1a912
commit 064a454117
4 changed files with 277 additions and 294 deletions

View File

@ -235,6 +235,16 @@ function advtrains.is_protected(pos, name)
return minetest.is_protected(pos, name)
end
function advtrains.is_creative(name)
if not name then
error("advtrains.is_creative() called without name parameter!")
end
if minetest.check_player_privs(name, {creative=true}) then
return true
end
return minetest.settings:get_bool("creative_mode")
end
function advtrains.ms_to_kmh(speed)
return speed * 3.6
end

View File

@ -188,13 +188,14 @@ function advtrains.path_get(train, index)
end
-- interpolated position to fractional index given, and angle based on path_dir
-- returns: pos, angle(yaw)
-- returns: pos, angle(yaw), p_floor, p_ceil
function advtrains.path_get_interpolated(train, index)
local i_floor = atfloor(index)
local i_ceil = i_floor + 1
local frac = index - i_floor
local p_floor, = advtrains.path_get(train, i_floor)
local p_ceil = advtrains.path_get(train, i_ceil)
-- Note: minimal code duplication to path_get_adjacent, for performance
local d_floor = train.path_dir[i_floor]
local d_ceil = train.path_dir[i_ceil]
@ -203,10 +204,20 @@ function advtrains.path_get_interpolated(train, index)
local ang = advtrains.minAngleDiffRad(a_floor, a_ceil)
return vector.add(p_floor, vector.multiply(vector.subtract(p_ceil, p_floor), frac), (a_floor + frac * ang)%(2*math.pi) -- TODO does this behave correctly?
return vector.add(p_floor, vector.multiply(vector.subtract(npos2, npos), frac), (a_floor + frac * ang)%(2*math.pi), p_floor, p_ceil -- TODO does this behave correctly?
end
-- returns the 2 path positions directly adjacent to index and the fraction on how to interpolate between them
-- returns: pos_floor, pos_ceil, fraction
function advtrains.path_get_adjacent(train, index)
local i_floor = atfloor(index)
local i_ceil = i_floor + 1
local frac = index - i_floor
local p_floor, = advtrains.path_get(train, i_floor)
local p_ceil = advtrains.path_get(train, i_ceil)
return p_floor, p_ceil, frac
end
function advtrains.path_get_by_offset(train, index, offset)
function advtrains.path_get_index_by_offset(train, index, offset)
local pos_in_train_left=pit
local index=train.index
if pos_in_train_left>(index-math.floor(index))*(train.path_dist[math.floor(index)] or 1) then

View File

@ -624,19 +624,17 @@ function advtrains.get_train_end_index(train)
return advtrains.get_real_path_index(train, train.trainlen or 2)--this function can be found inside wagons.lua since it's more related to wagons. we just set trainlen as pos_in_train
end
function advtrains.add_wagon_to_train(wagon, train_id, index)
function advtrains.add_wagon_to_train(wagon_id, train_id, index)
local train=advtrains.trains[train_id]
if index then
table.insert(train.trainparts, index, wagon.unique_id)
table.insert(train.trainparts, index, wagon_id)
else
table.insert(train.trainparts, wagon.unique_id)
table.insert(train.trainparts, wagon_id)
end
--this is not the usual case!!!
--we may set initialized because the wagon has no chance to step()
wagon.initialized=true
--TODO is this art or can we throw it away?
advtrains.update_trainpart_properties(train_id)
end
-- this function sets wagon's pos_in_train(parts) properties and train's max_speed and drives_on (and more)
function advtrains.update_trainpart_properties(train_id, invert_flipstate)
local train=advtrains.trains[train_id]
train.drives_on=advtrains.merge_tables(advtrains.all_tracktypes)
@ -646,43 +644,29 @@ function advtrains.update_trainpart_properties(train_id, invert_flipstate)
local rel_pos=0
local count_l=0
local shift_dcpl_lock=false
for i, w_id in ipairs(train.trainparts) do
local wagon=nil
local shift_dcpl_lock=false
for aoid,iwagon in pairs(minetest.luaentities) do
if iwagon.is_wagon and iwagon.unique_id==w_id then
if wagon then
--duplicate
atprint("update_trainpart_properties: Removing duplicate wagon with id="..aoid)
iwagon.object:remove()
else
wagon=iwagon
end
local data = advtrains.wagons[w_id]
-- 1st: update wagon data (pos_in_train a.s.o)
if data then
local wagon = minetest.registered_luaentites[data.type]
if not wagon then
atwarn("Wagon '",data.type,"' couldn't be found. Please check that all required modules are loaded!")
wagon = minetest.registered_luaentites["advtrains:wagon_placeholder"]
end
end
if not wagon then
if advtrains.wagon_save[w_id] then
--spawn a new and initialize it with the properties from wagon_save
wagon=minetest.add_entity(train.last_pos, advtrains.wagon_save[w_id].entity_name):get_luaentity()
if not wagon then
minetest.chat_send_all("[advtrains] Warning: Wagon "..advtrains.wagon_save[w_id].entity_name.." does not exist. Make sure all required modules are loaded!")
else
wagon:init_from_wagon_save(w_id)
end
end
end
if wagon then
rel_pos=rel_pos+wagon.wagon_span
wagon.train_id=train_id
wagon.pos_in_train=rel_pos
wagon.pos_in_trainparts=i
wagon.old_velocity_vector=nil
data.train_id=train_id
data.pos_in_train=rel_pos
data.pos_in_trainparts=i
if wagon.is_locomotive then
count_l=count_l+1
end
if invert_flipstate then
wagon.wagon_flipped = not wagon.wagon_flipped
shift_dcpl_lock, wagon.dcpl_lock = wagon.dcpl_lock, shift_dcpl_lock
data.wagon_flipped = not data.wagon_flipped
shift_dcpl_lock, data.dcpl_lock = data.dcpl_lock, shift_dcpl_lock
end
rel_pos=rel_pos+wagon.wagon_span
@ -695,17 +679,39 @@ function advtrains.update_trainpart_properties(train_id, invert_flipstate)
end
train.max_speed=math.min(train.max_speed, wagon.max_speed)
train.extent_h = math.max(train.extent_h, wagon.extent_h or 1);
else
atwarn("Did not find save data for wagon",w_id,". The wagon will be deleted.")
--what the hell...
table.remove(train.trainparts, pit)
end
end
train.trainlen=rel_pos
train.locomotives_in_train=count_l
end
-- This function checks whether entities need to be spawned for certain wagons, and spawns them.
function advtrains.spawn_wagons(train_id)
local train=advtrains.trains[train_id]
for i, w_id in ipairs(train.trainparts) do
local data = advtrains.wagons[w_id]
if data then
if not data.object or not data.object:getyaw() 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))
if minetest.get_node_or_nil(pos) then
local wt = data.type
if not minetest.registered_luaentities[wt] then
atprint("Unable to load",w_id,"of type",wt,", using placeholder")
wt="advtrains:wagon_placeholder"
end
wagon=minetest.add_entity(pos, wt):get_luaentity()
wagon:set_id(w_id)
end
end
end
end
end
function advtrains.split_train_at_wagon(wagon)
--get train
local train=advtrains.trains[wagon.train_id]

View File

@ -1,11 +1,35 @@
--atan2 counts angles clockwise, minetest does counterclockwise
-- wagon.lua
-- Holds all logic related to wagons
-- From now on, wagons are, just like trains, just entries in a table
-- All data that is static is stored in the entity prototype (self).
-- A copy of the entity prototype is always available inside minetest.registered_luaentities
-- All dynamic data is stored in the (new) wagons table
-- An entity is ONLY spawned by update_trainpart_properties when it finds it useful.
-- Only data that are only important to the entity itself are stored in the luaentity
advtrains.wagons = {}
--
function advtrains.create_wagon(type, train_id, owner)
local new_id=advtrains.random_id()
while advtrains.wagons[new_id] do new_id=advtrains.random_id() end
local wgn = {}
wgn.type = type
wgn.seatp = {}
wgn.owner = owner
wgn.id = new_id
wgn.train_id = train_id
advtrains.wagons[new_id] = wgn
return new_id
end
local wagon={
collisionbox = {-0.5,-0.5,-0.5, 0.5,0.5,0.5},
--physical = true,
visual = "mesh",
mesh = "wagon.b3d",
visual_size = {x=3, y=3},
visual_size = {x=1, y=1},
textures = {"black.png"},
is_wagon=true,
wagon_span=1,--how many index units of space does this wagon consume
@ -18,14 +42,7 @@ function wagon:train()
return advtrains.trains[self.train_id]
end
--[[about 'initalized':
when initialized is false, the entity hasn't got any data yet and should wait for these to be set before doing anything
when loading an existing object (with staticdata), it will be set
when instanciating a new object via add_entity, it is not set at the time on_activate is called.
then, wagon:initialize() will be called
wagon will save only uid in staticdata, no serialized table
]]
function wagon:on_activate(sd_uid, dtime_s)
if sd_uid~="" then
--destroy when loaded from static block.
@ -33,68 +50,17 @@ function wagon:on_activate(sd_uid, dtime_s)
return
end
self.object:set_armor_groups({immortal=1})
self.entity_name=self.name
end
function wagon:get_staticdata()
return advtrains.pcall(function()
if not self:ensure_init() then return end
atprint("[wagon "..((self.unique_id and self.unique_id~="" and self.unique_id) or "no-id").."]: saving to wagon_save")
--serialize inventory, if it has one
if self.has_inventory then
local inv=minetest.get_inventory({type="detached", name="advtrains_wgn_"..self.unique_id})
self.ser_inv=advtrains.serialize_inventory(inv)
end
--save to table before being unloaded
advtrains.wagon_save[self.unique_id]=advtrains.save_keys(self, {
"seatp", "owner", "ser_inv", "wagon_flipped", "train_id",
"dcpl_lock", "seat_access",
})
advtrains.wagon_save[self.unique_id].entity_name=self.name
return self.unique_id
end)
end
--returns: uid of wagon
function wagon:init_new_instance(train_id, properties)
local new_id=advtrains.random_id()
while advtrains.wagon_save[new_id] do new_id=advtrains.random_id() end--ensure uniqueness
self.unique_id=new_id
self.train_id=train_id
for k,v in pairs(properties) do
if k~="name" and k~="object" then
self[k]=v
end
end
self:init_shared()
self.initialized=true
atprint("init_new_instance "..self.unique_id.." ("..self.train_id..")")
return self.unique_id
end
function wagon:init_from_wagon_save(uid)
if not advtrains.wagon_save[uid] then
self.object:remove()
return
end
self.unique_id=uid
for k,v in pairs(advtrains.wagon_save[uid]) do
if k~="name" and k~="object" then
self[k]=v
end
end
if not self.train_id or not self:train() then
self.object:remove()
return
end
self:init_shared()
self.initialized=true
minetest.after(0.2, function() self:reattach_all() end)
atprint("init_from_wagon_save "..self.unique_id.." ("..self.train_id..")")
end
function wagon:init_shared()
function wagon:set_id(wid)
self.id = wid
self.initialized = true
local data = advtrains.wagons[self.id]
if self.has_inventory then
local uid_noptr=self.unique_id..""
--to be used later
local inv=minetest.create_detached_inventory("advtrains_wgn_"..self.unique_id, {
local inv=minetest.create_detached_inventory("advtrains_wgn_"..self.id, {
allow_move = function(inv, from_list, from_index, to_list, to_index, count, player)
return count
end,
@ -106,7 +72,7 @@ function wagon:init_shared()
end
})
if self.ser_inv then
advtrains.deserialize_inventory(self.ser_inv, inv)
advtrains.deserialize_inventory(data.ser_inv, inv)
end
if self.inventory_list_sizes then
for lst, siz in pairs(self.inventory_list_sizes) do
@ -114,17 +80,19 @@ function wagon:init_shared()
end
end
end
if self.doors then
self.door_anim_timer=0
self.door_state=0
end
self.door_anim_timer=0
self.door_state=0
minetest.after(0.2, function() self:reattach_all() end)
if self.custom_on_activate then
self:custom_on_activate(dtime_s)
end
-- reset line and infotext cache to update object properties on first call
self.line_cache=nil
self.infotext_cache=nil
function wagon:get_staticdata()
return "STATIC"
end
function wagon:ensure_init()
if self.initialized then
if self.noninitticks then self.noninitticks=nil end
@ -140,15 +108,22 @@ function wagon:ensure_init()
return false
end
function wagon:train()
return advtrains.trains[self.train_id]
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]
if not puncher or not puncher:is_player() then
return
end
if self.owner and puncher:get_player_name()~=self.owner and (not minetest.check_player_privs(puncher, {train_admin = true })) then
minetest.chat_send_player(puncher:get_player_name(), attrans("This wagon is owned by @1, you can't destroy it.", self.owner));
if data.owner and puncher:get_player_name()~=data.owner and (not minetest.check_player_privs(puncher, {train_admin = true })) then
minetest.chat_send_player(puncher:get_player_name(), attrans("This wagon is owned by @1, you can't destroy it.", data.owner));
return
end
if #(self:train().trainparts)>1 then
@ -161,6 +136,12 @@ function wagon:on_punch(puncher, time_from_last_punch, tool_capabilities, direct
minetest.chat_send_player(puncher:get_player_name(), attrans("Warning: If you destroy this wagon, you only get some steel back! If you are sure, hold Sneak and left-click the wagon."))
return
end
if self.custom_may_destroy then
if not self.custom_may_destroy(self, puncher, time_from_last_punch, tool_capabilities, direction) then
return
end
end
if not self:destroy() then return end
@ -176,27 +157,24 @@ function wagon:destroy()
-- single left-click shows warning
-- shift leftclick destroys
-- not when a driver is inside
local data = advtrains.wagons[self.id]
for _,_ in pairs(self.seatp) do
return
end
if self.custom_may_destroy then
if not self.custom_may_destroy(self, puncher, time_from_last_punch, tool_capabilities, direction) then
return
end
end
if self.custom_on_destroy then
self.custom_on_destroy(self, puncher, time_from_last_punch, tool_capabilities, direction)
self.custom_on_destroy(self)
end
atprint("[wagon "..((self.unique_id and self.unique_id~="" and self.unique_id) or "no-id").."]: destroying")
for seat,_ in pairs(data.seatp) do
self:get_off(seat)
end
atprint("[wagon ", self.id, "]: destroying")
self.object:remove()
table.remove(self:train().trainparts, self.pos_in_trainparts)
if self.train_id and self:train() then
table.remove(self:train().trainparts, data.pos_in_trainparts)
advtrains.update_trainpart_properties(self.train_id)
advtrains.wagon_save[self.unique_id]=nil
advtrains.wagons[self.id]=nil
if self.discouple then self.discouple.object:remove() end--will have no effect on unloaded objects
return true
end
@ -208,22 +186,21 @@ function wagon:on_step(dtime)
local t=os.clock()
local pos = self.object:getpos()
local data = advtrains.wagons[self.id]
if not pos then
atprint("["..self.unique_id.."][fatal] missing position (object:getpos() returned nil)")
return
end
self.entity_name=self.name
--is my train still here
if not self.train_id or not self:train() then
atprint("[wagon "..self.unique_id.."] missing train_id, destroying")
self.object:remove()
self:destroy()
return
end
if not self.seatp then
self.seatp={}
if not data.seatp then
data.seatp={}
end
if not self.seatpc then
self.seatpc={}
@ -236,16 +213,16 @@ function wagon:on_step(dtime)
--driver control
for seatno, seat in ipairs(self.seats) do
local pname=self.seatp[seatno]
local pname=data.seatp[seatno]
local driver=pname and minetest.get_player_by_name(pname)
local has_driverstand = pname and advtrains.check_driving_couple_protection(pname, self.owner, self.whitelist)
local has_driverstand = pname and advtrains.check_driving_couple_protection(pname, data.owner, data.whitelist)
if self.seat_groups then
has_driverstand = has_driverstand and (seat.driving_ctrl_access or self.seat_groups[seat.group].driving_ctrl_access)
else
has_driverstand = has_driverstand and (seat.driving_ctrl_access)
end
if has_driverstand and driver then
advtrains.update_driver_hud(driver:get_player_name(), self:train(), self.wagon_flipped)
advtrains.update_driver_hud(driver:get_player_name(), self:train(), data.wagon_flipped)
elseif driver then
--only show the inside text
local inside=self:train().text_inside or ""
@ -257,7 +234,7 @@ function wagon:on_step(dtime)
if has_driverstand then
--regular driver stand controls
advtrains.on_control_change(pc, self:train(), self.wagon_flipped)
advtrains.on_control_change(pc, self:train(), data.wagon_flipped)
--bordcom
if pc.sneak and pc.jump then
self:show_bordcom(self.seatp[seatno])
@ -276,7 +253,7 @@ function wagon:on_step(dtime)
end
else
-- If on a passenger seat and doors are open, get off when W or D pressed.
local pass = self.seatp[seatno] and minetest.get_player_by_name(self.seatp[seatno])
local pass = data.seatp[seatno] and minetest.get_player_by_name(data.seatp[seatno])
if pass and self:train().door_open~=0 then
local pc=pass:get_player_control()
if pc.up or pc.down then
@ -293,11 +270,9 @@ function wagon:on_step(dtime)
--check infotext
local outside=self:train().text_outside or ""
local gp=self:train()
local train=self:train()
--show off-track information in outside text instead of notifying the whole server about this
local front_off_track=gp.max_index_on_track and gp.index and gp.index>gp.max_index_on_track
local back_off_track=gp.min_index_on_track and gp.end_index and gp.end_index<gp.min_index_on_track
if front_off_track or back_off_track then
if train.index < train.path_trk_b or train.index > train.path_trk_f then
outside = outside .."\n!!! Train off track !!!"
end
@ -306,14 +281,14 @@ function wagon:on_step(dtime)
self.infotext_cache=outside
end
local fct=self.wagon_flipped and -1 or 1
local fct=data.wagon_flipped and -1 or 1
--set line number
if self.name == "advtrains:subway_wagon" and gp.line and gp.line~=self.line_cache then
if self.name == "advtrains:subway_wagon" and train.line and train.line~=self.line_cache then
local new_line_tex="advtrains_subway_wagon.png^advtrains_subway_wagon_line"..gp.line..".png"
self.object:set_properties({
textures={new_line_tex},
})
self.line_cache=gp.line
self.line_cache=train.line
elseif self.line_cache~=nil and gp.line==nil then
self.object:set_properties({
textures=self.textures,
@ -323,7 +298,7 @@ function wagon:on_step(dtime)
--door animation
if self.doors then
if (self.door_anim_timer or 0)<=0 then
local dstate = (gp.door_open or 0) * fct
local dstate = (train.door_open or 0) * fct
if dstate ~= self.door_state then
local at
--meaning of the train.door_open field:
@ -352,12 +327,12 @@ function wagon:on_step(dtime)
end
--DisCouple
if self.pos_in_trainparts and self.pos_in_trainparts>1 then
if gp.velocity==0 and not self.dcpl_lock then
if data.pos_in_trainparts and data.pos_in_trainparts>1 then
if train.velocity==0 and not data.dcpl_lock then
if not self.discouple or not self.discouple.object:getyaw() then
atprint(self.unique_id,"trying to spawn discouple")
local yaw = self.object:getyaw()
local flipsign=self.wagon_flipped and -1 or 1
local flipsign=data.wagon_flipped and -1 or 1
local dcpl_pos = vector.add(pos, {y=0, x=-math.sin(yaw)*self.wagon_span*flipsign, z=math.cos(yaw)*self.wagon_span*flipsign})
local object=minetest.add_entity(dcpl_pos, "advtrains:discouple")
if object then
@ -374,23 +349,23 @@ function wagon:on_step(dtime)
else
if self.discouple and self.discouple.object:getyaw() then
self.discouple.object:remove()
atprint(self.unique_id," removing discouple")
atprint(self.id," removing discouple")
end
end
end
--for path to be available. if not, skip step
if not gp.path then
if not train.path then
self.object:setvelocity({x=0, y=0, z=0})
return
end
if not self.pos_in_train then
--why ever. but better continue next step...
advtrains.update_trainpart_properties(self.train_id)
if not data.pos_in_train then
return
end
local index=advtrains.get_real_path_index(self:train(), self.pos_in_train)
--atprint("trainindex "..gp.index.." wagonindex "..index)
-- Calculate new position, yaw and direction vector
local index = advtrains.path_get_index_by_offset(train, train.index, -data.pos_in_train)
local pos, yaw, npos, npos2 = advtrains.path_get_interpolated(train, index)
local vdir = vector.normalize(vector.subtract(npos2, npos))
--automatic get_on
--needs to know index and path
@ -398,9 +373,8 @@ function wagon:on_step(dtime)
--using the mapping created by the trainlogic globalstep
for i, ino in ipairs(self.door_entry) do
--fct is the flipstate flag from door animation above
local aci = index + ino*fct
local ix1=gp.path[math.floor(aci)]
local ix2=gp.path[math.floor(aci+1)]
local aci = advtrains.path_get_index_by_offset(train, index, ino*fct)
local ix1, ix2 = advtrains.path_get_adjacent(train, aci)
-- the two wanted positions are ix1 and ix2 + (2nd-1st rotated by 90deg)
-- (x z) rotated by 90deg is (-z x) (http://stackoverflow.com/a/4780141)
local add = { x = (ix2.z-ix1.z)*gp.door_open, y = 0, z = (ix1.x-ix2.x)*gp.door_open }
@ -423,15 +397,6 @@ function wagon:on_step(dtime)
end
end
--position recalculation
local first_pos=gp.path[math.floor(index)]
local second_pos=gp.path[math.floor(index)+1]
if not first_pos or not second_pos then
--atprint(" object "..self.unique_id.." path end reached!")
self.object:setvelocity({x=0,y=0,z=0})
return
end
--checking for environment collisions(a 3x3 cube around the center)
if not gp.recently_collided_with_env then
local collides=false
@ -440,7 +405,7 @@ function wagon:on_step(dtime)
for x=-exh,exh do
for y=0,exv do
for z=-exh,exh do
local node=minetest.get_node_or_nil(vector.add(first_pos, {x=x, y=y, z=z}))
local node=minetest.get_node_or_nil(vector.add(npos, {x=x, y=y, z=z}))
if (advtrains.train_collides(node)) then
collides=true
end
@ -466,24 +431,12 @@ function wagon:on_step(dtime)
end
--FIX: use index of the wagon, not of the train.
local velocity=(gp.velocity*gp.movedir)/(gp.path_dist[math.floor(index)] or 1)
local acceleration=(gp.last_accel or 0)/(gp.path_dist[math.floor(index)] or 1)
local factor=index-math.floor(index)
local actual_pos={x=first_pos.x-(first_pos.x-second_pos.x)*factor, y=first_pos.y-(first_pos.y-second_pos.y)*factor, z=first_pos.z-(first_pos.z-second_pos.z)*factor,}
local velocityvec={x=(first_pos.x-second_pos.x)*velocity*-1, z=(first_pos.z-second_pos.z)*velocity*-1, y=(first_pos.y-second_pos.y)*velocity*-1}
local accelerationvec={x=(first_pos.x-second_pos.x)*acceleration*-1, z=(first_pos.z-second_pos.z)*acceleration*-1, y=(first_pos.y-second_pos.y)*acceleration*-1}
local velocity = (gp.velocity*gp.movedir)
local acceleration = (gp.last_accel or 0)
local velocityvec = vector.multiply(vdir, velocity)
local accelerationvec = vector.multiply(vdir, acceleration)
--some additional positions to determine orientation
local aposfwd=gp.path[math.floor(index+2)]
local aposbwd=gp.path[math.floor(index-1)]
local yaw
if aposfwd and aposbwd then
yaw=advtrains.get_wagon_yaw(aposfwd, second_pos, first_pos, aposbwd, factor)+math.pi--TODO remove when cleaning up
else
yaw=math.atan2((first_pos.x-second_pos.x), (second_pos.z-first_pos.z))
end
if self.wagon_flipped then
if data.wagon_flipped then
yaw=yaw+math.pi
end
@ -494,7 +447,7 @@ function wagon:on_step(dtime)
or not vector.equals(accelerationvec, self.old_acceleration_vector)
or self.old_yaw~=yaw
or self.updatepct_timer<=0 then--only send update packet if something changed
self.object:setpos(actual_pos)
self.object:setpos(pos)
self.object:setvelocity(velocityvec)
self.object:setacceleration(accelerationvec)
@ -505,7 +458,7 @@ function wagon:on_step(dtime)
if not self.old_yaw then
self.old_yaw=yaw
end
for _,name in pairs(self.seatp) do
for _,name in pairs(data.seatp) do
local p = minetest.get_player_by_name(name)
if p then
if not self.turning then
@ -541,29 +494,15 @@ function wagon:on_step(dtime)
end)
end
function advtrains.get_real_path_index(train, pit)
local pos_in_train_left=pit
local index=train.index
if pos_in_train_left>(index-math.floor(index))*(train.path_dist[math.floor(index)] or 1) then
pos_in_train_left=pos_in_train_left - (index-math.floor(index))*(train.path_dist[math.floor(index)] or 1)
index=math.floor(index)
while pos_in_train_left>(train.path_dist[index-1] or 1) do
pos_in_train_left=pos_in_train_left - (train.path_dist[index-1] or 1)
index=index-1
end
index=index-(pos_in_train_left/(train.path_dist[index-1] or 1))
else
index=index-(pos_in_train_left/(train.path_dist[math.floor(index-1)] or 1))
end
return index
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
end
local data = advtrains.wagons[self.id]
local pname=clicker:get_player_name()
local no=self:get_seatno(pname)
if no then
@ -578,10 +517,10 @@ function wagon:on_rightclick(clicker)
if self.has_inventory and self.get_inventory_formspec then
poss[#poss+1]={name=attrans("Show Inventory"), key="inv"}
end
if self.seat_groups[sgr].driving_ctrl_access and advtrains.check_driving_couple_protection(pname, self.owner, self.whitelist) then
if self.seat_groups[sgr].driving_ctrl_access and advtrains.check_driving_couple_protection(pname, data.owner, data.whitelist) then
poss[#poss+1]={name=attrans("Bord Computer"), key="bordcom"}
end
if self.owner==pname then
if data.owner==pname then
poss[#poss+1]={name=attrans("Wagon properties"), key="prop"}
end
if not self.seat_groups[sgr].require_doors_open or self:train().door_open~=0 then
@ -626,7 +565,7 @@ function wagon:on_rightclick(clicker)
for seatid, seatdef in ipairs(self.seats) do
if seatdef.group==sgr then
if (not self.seat_groups[sgr].require_doors_open or doors_open) then
if not self.seatp[seatid] then
if not data.seatp[seatid] then
self:get_on(clicker, seatid)
return
else
@ -648,7 +587,10 @@ function wagon:on_rightclick(clicker)
end
function wagon:get_on(clicker, seatno)
if not self.seatp then self.seatp={}end
local data = advtrains.wagons[self.id]
if not data.seatp then data.seatp={}end
if not self.seatpc then self.seatpc={}end--player controls in driver stands
if not self.seats[seatno] then return end
@ -657,14 +599,14 @@ function wagon:get_on(clicker, seatno)
atprint("get_on: clearing oldno",seatno)
advtrains.player_to_train_mapping[clicker:get_player_name()]=nil
advtrains.clear_driver_hud(clicker:get_player_name())
self.seatp[oldno]=nil
data.seatp[oldno]=nil
end
if self.seatp[seatno] and self.seatp[seatno]~=clicker:get_player_name() then
atprint("get_on: throwing off",self.seatp[seatno],"from seat",seatno)
if data.seatp[seatno] and data.seatp[seatno]~=clicker:get_player_name() then
atprint("get_on: throwing off",data.seatp[seatno],"from seat",seatno)
self:get_off(seatno)
end
atprint("get_on: attaching",clicker:get_player_name())
self.seatp[seatno] = clicker:get_player_name()
data.seatp[seatno] = clicker:get_player_name()
self.seatpc[seatno] = clicker:get_player_control_bits()
advtrains.player_to_train_mapping[clicker:get_player_name()]=self.train_id
clicker:set_attach(self.object, "", self.seats[seatno].attach_offset, {x=0,y=0,z=0})
@ -677,7 +619,10 @@ function wagon:get_off_plr(pname)
end
end
function wagon:get_seatno(pname)
for no, cont in pairs(self.seatp) do
local data = advtrains.wagons[self.id]
for no, cont in pairs(data.seatp) do
if cont==pname then
return no
end
@ -685,30 +630,31 @@ function wagon:get_seatno(pname)
return nil
end
function wagon:get_off(seatno)
if not self.seatp[seatno] then return end
local pname = self.seatp[seatno]
local data = advtrains.wagons[self.id]
if not data.seatp[seatno] then return end
local pname = data.seatp[seatno]
local clicker = minetest.get_player_by_name(pname)
advtrains.player_to_train_mapping[pname]=nil
advtrains.clear_driver_hud(pname)
self.seatp[seatno]=nil
data.seatp[seatno]=nil
self.seatpc[seatno]=nil
if clicker then
atprint("get_off: detaching",clicker:get_player_name())
clicker:set_detach()
clicker:set_eye_offset({x=0,y=0,z=0}, {x=0,y=0,z=0})
local gp=self:train()
local train=self:train()
--code as in step - automatic get on
if self.door_entry and gp.door_open and gp.door_open~=0 and gp.velocity==0 and gp.index and gp.path then
local index=advtrains.get_real_path_index(gp, self.pos_in_train)
--using the mapping created by the trainlogic globalstep
if self.door_entry and train.door_open and train.door_open~=0 and train.velocity==0 and train.index and train.path then
local index = advtrains.path_get_index_by_offset(train, train.index, -data.pos_in_train)
for i, ino in ipairs(self.door_entry) do
local aci = index + ino*(self.wagon_flipped and -1 or 1)
local ix1=gp.path[math.floor(aci)]
local ix2=gp.path[math.floor(aci+1)]
--fct is the flipstate flag from door animation above
local aci = advtrains.path_get_index_by_offset(train, index, ino*fct)
local ix1, ix2 = advtrains.path_get_adjacent(train, aci)
-- the two wanted positions are ix1 and ix2 + (2nd-1st rotated by 90deg)
-- (x z) rotated by 90deg is (-z x) (http://stackoverflow.com/a/4780141)
-- multiplied by 2 here, to place off on platform, y of add is 1.
local add = { x = (ix2.z-ix1.z)*gp.door_open, y = 0, z = (ix1.x-ix2.x)*gp.door_open}
local add = { x = (ix2.z-ix1.z)*gp.door_open, y = 0, z = (ix1.x-ix2.x)*gp.door_open }
local oadd = { x = (ix2.z-ix1.z)*gp.door_open*2, y = 1, z = (ix1.x-ix2.x)*gp.door_open*2}
local platpos=vector.round(vector.add(ix1, add))
local offpos=vector.round(vector.add(ix1, oadd))
@ -736,6 +682,8 @@ function wagon:get_off(seatno)
end
function wagon:show_get_on_form(pname)
if not self.initialized then return end
local data = advtrains.wagons[self.id]
if #self.seats==0 then
if self.has_inventory and self.get_inventory_formspec then
minetest.show_formspec(pname, "advtrains_inv_"..self.unique_id, self:get_inventory_formspec(pname))
@ -745,7 +693,7 @@ function wagon:show_get_on_form(pname)
local form, comma="size[5,8]label[0.5,0.5;"..attrans("Select seat:").."]textlist[0.5,1;4,6;seat;", ""
for seatno, seattbl in ipairs(self.seats) do
local addtext, colorcode="", ""
if self.seatp and self.seatp[seatno] then
if data.seatp and data.seatp[seatno] then
colorcode="#FF0000"
addtext=" ("..self.seatp[seatno]..")"
end
@ -764,11 +712,12 @@ function wagon:show_wagon_properties(pname)
field: driving/couple whitelist
button: save
]]
local data = advtrains.wagons[self.id]
local form="size[5,5]"
form = form .. "field[0.5,1;4,1;whitelist;Allow these players to drive your wagon:;"..(self.whitelist or "").."]"
form = form .. "field[0.5,1;4,1;whitelist;Allow these players to drive your wagon:;"..(data.whitelist or "").."]"
--seat groups access lists were here
form=form.."button_exit[0.5,3;4,1;save;"..attrans("Save wagon properties").."]"
minetest.show_formspec(pname, "advtrains_prop_"..self.unique_id, form)
minetest.show_formspec(pname, "advtrains_prop_"..self.id, form)
end
--BordCom
@ -793,6 +742,7 @@ end
function wagon:show_bordcom(pname)
if not self:train() then return end
local train = self:train()
local data = advtrains.wagons[self.id]
local form = "size[11,9]label[0.5,0;AdvTrains Boardcom v0.1]"
form=form.."textarea[0.5,1.5;7,1;text_outside;"..attrans("Text displayed outside on train")..";"..(train.text_outside or "").."]"
@ -817,7 +767,7 @@ function wagon:show_bordcom(pname)
form = form .. "image_button["..(i-0.5)..","..(linhei+2)..";1,1;advtrains_cpl_lock.png;dcpl_ulck_"..i..";]"
end
end
if i == self.pos_in_trainparts then
if i == data.pos_in_trainparts then
form = form .. "box["..(i-0.1)..","..(linhei-0.1)..";1,1;green]"
end
pre_own = ent.owner
@ -861,6 +811,8 @@ function wagon:show_bordcom(pname)
minetest.show_formspec(pname, "advtrains_bordcom_"..self.unique_id, form)
end
function wagon:handle_bordcom_fields(pname, formname, fields)
local data = advtrains.wagons[self.id]
local seatno=self:get_seatno(pname)
if not seatno or not self.seat_groups[self.seats[seatno].group].driving_ctrl_access or not advtrains.check_driving_couple_protection(pname, self.owner, self.whitelist) then
return
@ -885,35 +837,25 @@ function wagon:handle_bordcom_fields(pname, formname, fields)
if fields["dcpl_"..i] then
for _,wagon in pairs(minetest.luaentities) do
if wagon.is_wagon and wagon.initialized and wagon.unique_id==tpid then
wagon:safe_decouple(pname)
wagon:safe_decouple(pname) -- TODO: Move this into external function (don't search entity?)
end
end
end
if i>1 and fields["dcpl_lck_"..i] then
local ent = advtrains.wagon_save[tpid]
local pent = advtrains.wagon_save[train.trainparts[i-1]]
local ent = advtrains.wagons[tpid]
local pent = advtrains.wagons[train.trainparts[i-1]]
if ent and pent then
if checklock(pname, ent.owner, pent.owner, ent.whitelist, pent.whitelist) then
for _,wagon in pairs(minetest.luaentities) do
if wagon.is_wagon and wagon.initialized and wagon.unique_id==tpid then
wagon.dcpl_lock=true
wagon:get_staticdata()
end
end
ent.dcpl_lock = true
end
end
end
if i>1 and fields["dcpl_ulck_"..i] then
local ent = advtrains.wagon_save[tpid]
local pent = advtrains.wagon_save[train.trainparts[i-1]]
local ent = advtrains.wagons[tpid]
local pent = advtrains.wagons[train.trainparts[i-1]]
if ent and pent then
if checklock(pname, ent.owner, pent.owner, ent.whitelist, pent.whitelist) then
for _,wagon in pairs(minetest.luaentities) do
if wagon.is_wagon and wagon.initialized and wagon.unique_id==tpid then
wagon.dcpl_lock=false
wagon:get_staticdata()
end
end
ent.dcpl_lock = false
end
end
end
@ -932,7 +874,7 @@ function wagon:handle_bordcom_fields(pname, formname, fields)
local function chkownsany()
local owns_any = minetest.check_player_privs(pname, "train_admin")
for i, tpid in ipairs(train.trainparts) do
local ent = advtrains.wagon_save[tpid]
local ent = advtrains.wagons[tpid]
if ent then
owns_any = owns_any or advtrains.check_driving_couple_protection(pname, ent.owner, ent.whitelist)
end
@ -963,14 +905,15 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
local uid=string.match(formname, "^advtrains_geton_(.+)$")
if uid then
for _,wagon in pairs(minetest.luaentities) do
if wagon.is_wagon and wagon.initialized and wagon.unique_id==uid then
if wagon.is_wagon and wagon.initialized and wagon.id==uid then
local data = advtrains.wagons[wagon.id]
if fields.inv then
if wagon.has_inventory and wagon.get_inventory_formspec then
minetest.show_formspec(player:get_player_name(), "advtrains_inv_"..uid, wagon:get_inventory_formspec(player:get_player_name()))
end
elseif fields.seat then
local val=minetest.explode_textlist_event(fields.seat)
if val and val.type~="INV" and not wagon.seatp[player:get_player_name()] then
if val and val.type~="INV" and not data.seatp[player:get_player_name()] then
--get on
wagon:get_on(player, val.index)
--will work with the new close_formspec functionality. close exactly this formspec.
@ -983,7 +926,7 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
uid=string.match(formname, "^advtrains_seating_(.+)$")
if uid then
for _,wagon in pairs(minetest.luaentities) do
if wagon.is_wagon and wagon.initialized and wagon.unique_id==uid then
if wagon.is_wagon and wagon.initialized and wagon.id==uid then
local pname=player:get_player_name()
local no=wagon:get_seatno(pname)
if no then
@ -996,19 +939,14 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
end
uid=string.match(formname, "^advtrains_prop_(.+)$")
if uid then
for _,wagon in pairs(minetest.luaentities) do
if wagon.is_wagon and wagon.initialized and wagon.unique_id==uid then
local pname=player:get_player_name()
if pname~=wagon.owner and not minetest.check_player_privs(pname, {train_admin = true}) then
return true
end
if fields.save or not fields.quit then
for sgr,sgrdef in pairs(wagon.seat_groups) do
if fields.whitelist then
wagon.whitelist = fields.whitelist
end
end
end
local pname=player:get_player_name()
local data = advtrains.wagons[uid]
if pname~=data.owner and not minetest.check_player_privs(pname, {train_admin = true}) then
return true
end
if fields.save or not fields.quit then
if fields.whitelist then
data.whitelist = fields.whitelist
end
end
end
@ -1023,11 +961,12 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
end)
end)
function wagon:seating_from_key_helper(pname, fields, no)
local data = advtrains.wagons[wagon.id]
local sgr=self.seats[no].group
for _,access in ipairs(self.seat_groups[sgr].access_to) do
if fields["sgr_"..access] and self:check_seat_group_access(pname, access) then
for seatid, seatdef in ipairs(self.seats) do
if seatdef.group==access and not self.seatp[seatid] then
if seatdef.group==access and not data.seatp[seatid] then
self:get_on(minetest.get_player_by_name(pname), seatid)
return
end
@ -1035,7 +974,7 @@ function wagon:seating_from_key_helper(pname, fields, no)
end
end
if fields.inv and self.has_inventory and self.get_inventory_formspec then
minetest.show_formspec(player:get_player_name(), "advtrains_inv_"..self.unique_id, wagon:get_inventory_formspec(player:get_player_name()))
minetest.show_formspec(player:get_player_name(), "advtrains_inv_"..self.id, self:get_inventory_formspec(player:get_player_name()))
end
if fields.prop and self.owner==pname then
self:show_wagon_properties(pname)
@ -1060,8 +999,9 @@ function wagon:check_seat_group_access(pname, sgr)
return true
end
function wagon:reattach_all()
if not self.seatp then self.seatp={} end
for seatno, pname in pairs(self.seatp) do
local data = advtrains.wagons[wagon.id]
if not data.seatp then data.seatp={} end
for seatno, pname in pairs(data.seatp) do
local p=minetest.get_player_by_name(pname)
if p then
self:get_on(p ,seatno)
@ -1074,7 +1014,8 @@ function wagon:safe_decouple(pname)
minetest.chat_send_player(pname, "Missing train_operator privilege")
return false
end
if self.dcpl_lock then
local data = advtrains.wagons[wagon.id]
if data.dcpl_lock then
minetest.chat_send_player(pname, "Couple is locked (ask owner or admin to unlock it)")
return false
end
@ -1085,7 +1026,7 @@ function wagon:safe_decouple(pname)
end
function advtrains.register_wagon(sysname_p, prototype, desc, inv_img)
function advtrains.register_wagon(sysname_p, prototype, desc, inv_img, nincreative)
local sysname = sysname_p
if not string.match(sysname, ":") then
sysname = "advtrains:"..sysname_p
@ -1099,12 +1040,14 @@ function advtrains.register_wagon(sysname_p, prototype, desc, inv_img)
wield_image = inv_img,
stack_max = 1,
groups = { not_in_creative_inventory = nincreative and 0 or 1}
on_place = function(itemstack, placer, pointed_thing)
return advtrains.pcall(function()
if not pointed_thing.type == "node" 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
@ -1114,7 +1057,7 @@ function advtrains.register_wagon(sysname_p, prototype, desc, inv_img)
return itemstack
end
if not minetest.check_player_privs(placer, {train_operator = true }) then
minetest.chat_send_player(placer:get_player_name(), "You don't have the train_operator privilege.")
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
@ -1125,21 +1068,17 @@ function advtrains.register_wagon(sysname_p, prototype, desc, inv_img)
local plconnid = advtrains.yawToClosestConn(yaw, tconns)
local prevpos = advtrains.get_adjacent_rail(pointed_thing.under, tconns, plconnid, prototype.drives_on)
if not prevpos then return end
local id=advtrains.create_new_train_at(pointed_thing.under, prevpos)
local ob=minetest.add_entity(pointed_thing.under, sysname)
if not ob then
atprint("couldn't add_entity, aborting")
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 le=ob:get_luaentity()
local id=advtrains.create_new_train_at(pointed_thing.under, plconnid)
le.owner=placer:get_player_name()
local wid = advtrains.create_wagon(type, id, pname)
local wagon_uid=le:init_new_instance(id, {})
advtrains.add_wagon_to_train(wid, id)
advtrains.add_wagon_to_train(le, id)
if not minetest.settings:get_bool("creative_mode") then
if not advtrains.is_creative(pname) then
itemstack:take_item()
end
return itemstack
@ -1149,4 +1088,21 @@ function advtrains.register_wagon(sysname_p, prototype, desc, inv_img)
})
end
-- Placeholder wagon. Will be spawned whenever a mod is missing
advtrains.register_wagon("advtrains:wagon_placeholder", {
visual="sprite",
textures = {"advtrains_wheel.png"},
collisionbox = {-0.3,-0.3,-0.3, 0.3,0.3,0.3},
visual_size = {x=0.7, y=0.7},
initial_sprite_basepos = {x=0, y=0},
drives_on = advtrains.all_tracktypes,
max_speed = 5,
seats = {
},
seat_groups = {
},
assign_to_seat_group = {},
wagon_span=1,
drops={},
}, "Wagon placeholder", "advtrains_subway_wagon_inv.png", true)