-- 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 wagon_prototypes
-- 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
-- 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.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")
--
function advtrains.create_wagon(wtype, owner)
local new_id=advtrains.random_id()
while advtrains.wagons[new_id] do new_id=advtrains.random_id() end
local wgn = {}
wgn.type = wtype
wgn.seatp = {}
wgn.owner = owner
wgn.id = new_id
---wgn.train_id = train_id --- will get this via update_trainpart_properties
advtrains.wagons[new_id] = wgn
--atdebug("Created new wagon:",wgn)
return new_id
end
local function make_inv_name(uid)
return "detached:advtrains_wgn_"..uid
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=1, y=1},
textures = {"black.png"},
is_wagon=true,
wagon_span=1,--how many index units of space does this wagon consume
wagon_width=3, -- Wagon width in meters
has_inventory=false,
static_save=false,
}
function wagon:train()
local data = advtrains.wagons[self.id]
return advtrains.trains[data.train_id]
end
function wagon:on_activate(sd_uid, dtime_s)
if sd_uid~="" then
--destroy when loaded from static block.
self.object:remove()
return
end
self.object:set_armor_groups({immortal=1})
end
local function invcallback(id, pname, rtallow, rtfail)
local data = advtrains.wagons[id]
if data and advtrains.check_driving_couple_protection(pname, data.owner, data.whitelist) then
return rtallow
end
return rtfail
end
function wagon:set_id(wid)
self.id = wid
self.initialized = true
local data = advtrains.wagons[self.id]
advtrains.wagon_objects[self.id] = self.object
--atdebug("Created wagon entity:",self.name," w_id",wid," t_id",data.train_id)
if self.has_inventory then
--to be used later
local inv=minetest.get_inventory({type="detached", name="advtrains_wgn_"..self.id})
-- create inventory, if not yet created
if not inv then
inv=minetest.create_detached_inventory("advtrains_wgn_"..self.id, {
allow_move = function(inv, from_list, from_index, to_list, to_index, count, player)
return invcallback(wid, player:get_player_name(), count, 0)
end,
allow_put = function(inv, listname, index, stack, player)
return invcallback(wid, player:get_player_name(), stack:get_count(), 0)
end,
allow_take = function(inv, listname, index, stack, player)
return invcallback(wid, player:get_player_name(), stack:get_count(), 0)
end
})
if data.ser_inv then
advtrains.deserialize_inventory(data.ser_inv, inv)
end
if self.inventory_list_sizes then
for lst, siz in pairs(self.inventory_list_sizes) do
inv:set_size(lst, siz)
end
end
end
end
self.door_anim_timer=0
self.door_state=0
minetest.after(0.2, function() self:reattach_all() end)
if self.set_textures then
self:set_textures(data)
end
if self.custom_on_activate then
self:custom_on_activate()
end
end
function wagon:get_staticdata()
return "STATIC"
end
function wagon:ensure_init()
-- Note: A wagon entity won't exist when there's no train, because the train is
-- the thing that actually creates the entity
-- Train not being set just means that this will happen as soon as the train calls update_trainpart_properties.
if self.initialized and self.id then
local data = advtrains.wagons[self.id]
if data and data.train_id and self:train() then
if self.noninitticks then self.noninitticks=nil end
return true
end
end
if not self.noninitticks then
atwarn("wagon",self.id,"uninitialized init=",self.initialized)
self.noninitticks=0
end
self.noninitticks=self.noninitticks+1
if self.noninitticks>20 then
atwarn("wagon",self.id,"uninitialized, removing")
self:destroy()
else
self.object:set_velocity({x=0,y=0,z=0})
end
return false
end
function wagon:train()
local data = advtrains.wagons[self.id]
return advtrains.trains[data.train_id]
end
-- Remove the wagon
function wagon:on_punch(puncher, time_from_last_punch, tool_capabilities, direction)
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 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.custom_may_destroy then
if not self.custom_may_destroy(self, puncher, time_from_last_punch, tool_capabilities, direction) then
return
end
end
local itemstack = puncher:get_wielded_item()
-- WARNING: This part of the API is guaranteed to change! DO NOT USE!
if self.set_livery and itemstack:get_name() == "bike:painter" then
self:set_livery(puncher, itemstack, data)
return
end
-- check whether wagon has an inventory. Is is empty?
if self.has_inventory then
local inv=minetest.get_inventory({type="detached", name="advtrains_wgn_"..self.id})
if not inv then -- inventory is not initialized when wagon was never loaded - should never happen
atwarn("Destroying wagon with inventory, but inventory is not found? Shouldn't happen!")
return
end
for listname, _ in pairs(inv:get_lists()) do
if not inv:is_empty(listname) then
minetest.chat_send_player(puncher:get_player_name(), attrans("The wagon's inventory is not empty!"));
return
end
end
end
if #(self:train().trainparts)>1 then
minetest.chat_send_player(puncher:get_player_name(), attrans("Wagon needs to be decoupled from other wagons in order to destroy it."));
return
end
local pc=puncher:get_player_control()
if not pc.sneak then
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 not self:destroy() then return end
local inv = puncher:get_inventory()
for _,item in ipairs(self.drops or {self.name}) do
inv:add_item("main", item)
end
end
function wagon:destroy()
--some rules:
-- you get only some items back
-- single left-click shows warning
-- shift leftclick destroys
-- not when a driver is inside
|