26 local function coupling_particles(pos,truth)
32 minetest.add_particlespawner({
37 minvel = vector.new(-10,-10,-10),
38 maxvel = vector.new(10,10,10),
39 minacc = {x=0, y=0, z=0},
40 maxacc = {x=0, y=0, z=0},
45 collisiondetection = false,
46 collision_removal = false,
48 texture = "couple_particle.png^[colorize:"..color..":200",
53 local function data_injection(pos,data)
55 pool[minetest.hash_node_position(pos)] = true
57 pool[minetest.hash_node_position(pos)] = nil
61 local function speed_limiter(self,speed)
62 local test = self.object:get_velocity()--vector.multiply(self.velocity,new_vel)
64 if test.x > speed then
66 elseif test.x < -speed then
69 if test.z > speed then
71 elseif test.z < -speed then
74 self.object:set_velocity(test)
77 local function create_axis(pos)
78 local possible_dirs = {}
79 for _,dir in pairs(dirs) do
80 local pos2 = vector.add(pos,dir)
81 if pool[minetest.hash_node_position(pos2)] then
82 table.insert(possible_dirs,dir)
88 local function collision_detect(self)
89 if not self.axis_lock then return end
90 local pos = self.object:get_pos()
91 for _,object in ipairs(minetest.get_objects_inside_radius(pos, 1)) do
92 if object:is_player() then
93 local pos2 = object:get_pos()
94 if self.axis_lock == "x" then
96 local velocity = (1-vector.distance(vector.new(pos.x,0,0),vector.new(pos2.x,0,0)))*5
97 local dir = vector.direction(vector.new(pos2.x,0,0),vector.new(pos.x,0,0))
98 local new_vel = vector.multiply(dir,velocity)
99 self.object:add_velocity(new_vel)
101 elseif self.axis_lock == "z" then
102 local velocity = (1-vector.distance(vector.new(0,0,pos.z),vector.new(0,0,pos2.z)))*5
103 local dir = vector.direction(vector.new(0,0,pos2.z),vector.new(0,0,pos.z))
104 local new_vel = vector.multiply(dir,velocity)
105 self.object:add_velocity(new_vel)
113 local function direction_snap(self)
116 if dir.y == 1 then pitch = math.pi/4 end
117 if dir.y == -1 then pitch = -math.pi/4 end
118 local yaw = minetest.dir_to_yaw(dir)
119 self.object:set_rotation(vector.new(pitch,yaw,0))
122 local function turn_snap(pos,self,dir,dir2)
123 if self.axis_lock == "x" then
124 if dir.x ~= 0 and dir2.z ~= 0 then
125 local velocity = self.object:get_velocity()
126 local inertia = math.abs(velocity.x)
127 self.object:set_velocity(vector.multiply(dir2,inertia))
130 self.object:set_pos(pos)
135 if self.axis_lock == "z" then
136 if dir.z ~= 0 and dir2.x ~= 0 then
137 local velocity = self.object:get_velocity()
138 local inertia = math.abs(velocity.z)
139 self.object:set_velocity(vector.multiply(dir2,inertia))
142 self.object:set_pos(pos)
150 local function climb_snap(pos,self,dir,dir2)
151 if self.axis_lock == "x" then
152 if dir.x == dir2.x and dir2.y ~= 0 then
153 local velocity = self.object:get_velocity()
154 local inertia = math.abs(velocity.x)
155 self.object:set_velocity(vector.multiply(dir2,inertia))
158 self.object:set_pos(pos)
163 if self.axis_lock == "z" then
164 if dir.z == dir2.z and dir2.y ~= 0 then
165 local velocity = self.object:get_velocity()
166 local inertia = math.abs(velocity.z)
167 self.object:set_velocity(vector.multiply(dir2,inertia))
170 self.object:set_pos(pos)
178 local function straight_snap(pos,self,dir)
179 if self.axis_lock == "x" then
180 if dir.x ~= 0 and pool[minetest.hash_node_position(vector.add(pos,vector.new(dir.x,0,0)))] then
181 local velocity = self.object:get_velocity()
182 self.object:set_velocity(vector.new(velocity.x,0,0))
183 self.dir = vector.new(dir.x,0,0)
185 self.object:set_pos(pos)
190 if self.axis_lock == "z" then
191 if dir.z ~= 0 and pool[minetest.hash_node_position(vector.add(pos,vector.new(0,0,dir.z)))] then
192 local velocity = self.object:get_velocity()
193 self.object:set_velocity(vector.new(0,0,velocity.z))
194 self.dir = vector.new(0,0,dir.z)
196 self.object:set_pos(pos)
205 local function coupling_logic(self)
207 if not self.axis_lock then return end
209 if not self.coupler1 then return end
211 if self.dir.y ~= 0 then return end
213 local pos = self.object:get_pos()
215 local pos2 = self.coupler1:get_pos()
217 if self.axis_lock == "x" then
218 local velocity_real = self.object:get_velocity()
219 local distance = vector.distance(pos,pos2)
220 local new_vel = vector.new(0,0,0)
221 if distance > 1.5 then
222 local velocity = (distance-1)
223 local dir = vector.direction(vector.new(pos.x,0,0),vector.new(pos2.x,0,0))
225 new_vel = vector.multiply(dir,velocity)
227 new_vel = vector.multiply(velocity_real,-1)
229 self.object:add_velocity(new_vel)
230 elseif self.axis_lock == "z" then
231 local velocity_real = self.object:get_velocity()
232 local distance = vector.distance(pos,pos2)
233 local new_vel = vector.new(0,0,0)
234 if distance > 1.5 then
235 local velocity = (distance-1)
236 local dir = vector.direction(vector.new(0,0,pos.z),vector.new(0,0,pos2.z))
238 new_vel = vector.multiply(dir,velocity)
240 new_vel = vector.multiply(velocity_real,-1)
242 self.object:add_velocity(new_vel)
249 local function rail_brain(self,pos)
252 if not self.dir then self.dir = vector.new(0,0,0) end
254 local pos2 = self.object:get_pos()
258 speed_limiter(self,6)
260 if not pool[minetest.hash_node_position(vector.add(pos,dir))] then
262 if straight_snap(pos,self,dir) then
266 local possible_dirs = create_axis(pos)
268 if table.getn(possible_dirs) == 0 then
269 --stop slow down become physical
271 for _,dir2 in pairs(possible_dirs) do
272 if turn_snap(pos,self,dir,dir2) then
275 if climb_snap(pos,self,dir,dir2) then
288 minecart.on_step = function(self,dtime)
290 self.object:set_pos(self.old_pos)
292 local pos = vector.round(self.object:get_pos())
293 if not self.axis_lock then
294 local possible_dirs = create_axis(pos)
295 for _,dir in pairs(possible_dirs) do
301 elseif dir.z ~= 0 then
310 --collision_detect(self)
312 self.old_pos = self.object:get_pos()
316 minecart.on_punch = function(self, puncher)
317 if not puncher:get_wielded_item():get_name() == "minecart:wrench" then
321 self.object:set_velocity(vector.multiply(self.dir,6))
322 minetest.add_particlespawner({
325 minpos = vector.new(0,0.5,0),
326 maxpos = vector.new(0,0.5,0),
327 minvel = vector.new(0,0,0),
328 maxvel = vector.new(0,0,0),
329 minacc = {x=0, y=3, z=0},
330 maxacc = {x=0, y=5, z=0},
335 collisiondetection = false,
336 collision_removal = false,
338 texture = "smoke.png",
339 attached = self.object
346 minecart.on_rightclick = function(self,clicker)
347 local pos = self.object:get_pos()
348 if clicker:get_wielded_item():get_name() == "utility:furnace" then
349 local obj = minetest.add_entity(pos, "minecart:furnace")
350 obj:set_attach(self.object,"",vector.new(0,0,0),vector.new(0,0,0))
351 minetest.sound_play("wrench",{
352 object = self.object,
354 max_hear_distance = 64,
356 coupling_particles(pos,true)
361 if not clicker:get_wielded_item():get_name() == "minecart:wrench" then
365 local name = clicker:get_player_name()
366 if not pool[name] then
367 if not self.coupler2 then
368 pool[name] = self.object
369 minetest.sound_play("wrench",{
370 object = self.object,
372 max_hear_distance = 64,
374 coupling_particles(pos,true)
376 minetest.sound_play("wrench",{
377 object = self.object,
379 max_hear_distance = 64,
382 coupling_particles(pos,false)
385 if pool[name] ~= self.object and not (pool[name]:get_luaentity().coupler1 and pool[name]:get_luaentity().coupler1 == self.object or self.coupler2) then
386 self.coupler1 = pool[name]
387 pool[name]:get_luaentity().coupler2 = self.object
388 minetest.sound_play("wrench",{
389 object = self.object,
391 max_hear_distance = 64,
393 coupling_particles(pos,true)
395 minetest.sound_play("wrench",{
396 object = self.object,
398 max_hear_distance = 64,
401 coupling_particles(pos,false)
408 minecart.on_activate = function(self,staticdata, dtime_s)
409 self.object:set_armor_groups({immortal=1})
410 if string.sub(staticdata, 1, string.len("return")) ~= "return" then
413 local data = minetest.deserialize(staticdata)
414 if type(data) ~= "table" then
417 self.old_pos = self.object:get_pos()
418 self.velocity = vector.new(0,0,0)
421 minecart.get_staticdata = function(self)
422 return minetest.serialize({
428 minecart.initial_properties = {
429 physical = false, -- otherwise going uphill breaks
430 collisionbox = {-0.4, -0.5, -0.4, 0.4, 0.45, 0.4},--{-0.5, -0.4, -0.5, 0.5, 0.25, 0.5},
433 visual_size = {x=1, y=1},
434 textures = {"minecart.png"},
439 minetest.register_entity("minecart:minecart", minecart)
452 minetest.register_craftitem("minecart:minecart", {
453 description = "Minecart",
454 inventory_image = "minecartitem.png",
455 wield_image = "minecartitem.png",
456 on_place = function(itemstack, placer, pointed_thing)
457 if not pointed_thing.type == "node" then
461 local sneak = placer:get_player_control().sneak
462 local noddef = minetest.registered_nodes[minetest.get_node(pointed_thing.under).name]
463 if not sneak and noddef.on_rightclick then
464 minetest.item_place(itemstack, placer, pointed_thing)
468 if minetest.get_item_group(minetest.get_node(pointed_thing.under).name, "rail")>0 then
469 minetest.add_entity(pointed_thing.under, "minecart:minecart")
474 itemstack:take_item()
480 minetest.register_craft({
481 output = "minecart:minecart",
483 {"main:iron", "", "main:iron"},
484 {"main:iron", "main:iron", "main:iron"},
492 minetest.register_node("minecart:rail",{
493 description = "Rail",
494 wield_image = "rail.png",
496 "rail.png", "railcurve.png",
497 "railt.png", "railcross.png"
499 drawtype = "raillike",
501 sunlight_propagates = true,
502 is_ground_content = false,
504 node_placement_prediction = "",
507 fixed = {-1/2, -1/2, -1/2, 1/2, -1/2+1/16, 1/2},
509 sounds = main.stoneSound(),
510 after_place_node = function(pos)
511 data_injection(pos,true)
513 after_destruct = function(pos)
516 groups={stone=1,wood=1,rail=1,attached_node=1},
520 minetest.register_lbm({
521 name = "minecart:rail",
522 nodenames = {"minecart:rail"},
523 run_at_every_load = true,
524 action = function(pos)
525 data_injection(pos,true)
529 minetest.register_craft({
530 output = "minecart:rail 32",
532 {"main:iron","","main:iron"},
533 {"main:iron","main:stick","main:iron"},
534 {"main:iron","","main:iron"}
539 minetest.register_food("minecart:wrench",{
540 description = "Train Wrench",
541 texture = "wrench.png",
544 minetest.register_craft({
545 output = "minecart:wrench",
547 {"main:iron", "", "main:iron"},
548 {"main:iron", "main:lapis", "main:iron"},
549 {"", "main:lapis", ""}
555 minetest.register_entity("minecart:furnace", {
556 initial_properties = {
557 visual = "wielditem",
558 visual_size = {x = 0.6, y = 0.6},
562 collide_with_objects = false,
564 collisionbox = {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5},
566 set_node = function(self)
567 self.object:set_properties({
569 textures = {"utility:furnace"},
574 on_activate = function(self, staticdata)
575 self.object:set_armor_groups({immortal = 1})