]> git.lizzy.rs Git - Crafter.git/blob - mods/train/init.lua
0e278884ab2ecb72125618087be327521efd75bc
[Crafter.git] / mods / train / init.lua
1 local pool = {}
2
3 local player_pool = {}
4
5
6 local dirs = {
7         {x= 1,y= 0,z= 0},
8         {x=-1,y= 0,z= 0},
9
10         {x= 1,y= 1,z= 0}, 
11         {x=-1,y= 1,z= 0},
12
13         {x= 1,y=-1,z= 0},
14         {x=-1,y=-1,z= 0},
15
16         {x= 0,y= 0,z= 1},
17         {x= 0,y= 0,z=-1},
18
19         {x= 0,y= 1,z= 1},
20         {x= 0,y= 1,z=-1},
21
22         {x= 0,y=-1,z= 1},
23         {x= 0,y=-1,z=-1},
24 }
25
26 local function coupling_particles(pos,truth)
27         local color = "red"
28         if truth then
29                 color = "green"
30         end
31
32         minetest.add_particlespawner({
33                 amount = 15,
34                 time = 0.001,
35                 minpos = pos,
36                 maxpos = pos,
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},
41                 minexptime = 1.1,
42                 maxexptime = 1.5,
43                 minsize = 1,
44                 maxsize = 2,
45                 collisiondetection = false,
46                 collision_removal = false,
47                 vertical = false,
48                 texture = "couple_particle.png^[colorize:"..color..":200",
49                 glow = 14,
50         })
51 end
52
53 local function data_injection(pos,data)
54         if data then
55                 pool[minetest.hash_node_position(pos)] = true
56         else
57                 pool[minetest.hash_node_position(pos)] = nil
58         end
59 end
60
61 local function speed_limiter(self,speed)
62         local test = self.object:get_velocity()--vector.multiply(self.velocity,new_vel)
63
64         if test.x > speed then
65                 test.x = speed
66         elseif test.x < -speed then
67                 test.x = -speed
68         end
69         if test.z > speed then
70                 test.z = speed
71         elseif test.z < -speed then
72                 test.z = -speed         
73         end
74         self.object:set_velocity(test)
75 end
76
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)
83                 end
84         end
85         return(possible_dirs)
86 end
87
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
95
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)
100                                 self.dir = dir
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)
106                                 self.dir = dir
107                         end
108                         return
109                 end
110         end
111 end
112
113 local function direction_snap(self)
114         local dir = self.dir
115         local pitch = 0
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))
120 end
121
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))
128                         self.dir = dir2
129                         self.axis_lock = "z"
130                         self.object:set_pos(pos)
131                         direction_snap(self)
132                         return(true)
133                 end
134         end
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))
140                         self.dir = dir2
141                         self.axis_lock = "x"
142                         self.object:set_pos(pos)
143                         direction_snap(self)
144                         return(true)
145                 end
146         end
147         return(false)
148 end
149
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))
156                         self.dir = dir2
157                         self.axis_lock = "x"
158                         self.object:set_pos(pos)
159                         direction_snap(self)
160                         return(true)
161                 end
162         end
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))
168                         self.dir = dir2
169                         self.axis_lock = "z"
170                         self.object:set_pos(pos)
171                         direction_snap(self)
172                         return(true)
173                 end
174         end
175         return(false)
176 end
177
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)
184                         self.axis_lock = "x"
185                         self.object:set_pos(pos)
186                         direction_snap(self)
187                         return(true)
188                 end
189         end
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)
195                         self.axis_lock = "z"
196                         self.object:set_pos(pos)
197                         direction_snap(self)
198                         return(true)
199                 end
200         end
201         return(false)
202 end
203
204
205 local function coupling_logic(self)
206         
207         if not self.axis_lock then return end
208
209         if not self.coupler1 then return end
210
211         if self.dir.y ~= 0 then return end
212
213         local pos = self.object:get_pos()
214         
215         local pos2 = self.coupler1:get_pos()
216
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))
224                         self.dir = dir
225                         new_vel = vector.multiply(dir,velocity)
226                 else
227                         new_vel = vector.multiply(velocity_real,-1)
228                 end
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))
237                         self.dir = dir
238                         new_vel = vector.multiply(dir,velocity)
239                 else
240                         new_vel = vector.multiply(velocity_real,-1)
241                 end
242                 self.object:add_velocity(new_vel)
243         end
244
245         return
246 end
247
248
249 local function rail_brain(self,pos)
250
251         if not self.dir then self.dir = vector.new(0,0,0) end
252
253         local pos2 = self.object:get_pos()
254
255         local dir = self.dir
256
257         speed_limiter(self,6)
258
259         if not pool[minetest.hash_node_position(vector.add(pos,dir))] then
260
261                 if straight_snap(pos,self,dir) then
262                         return
263                 end
264
265                 local possible_dirs = create_axis(pos)
266
267                 if table.getn(possible_dirs) == 0 then
268                         --stop slow down become physical
269                 else
270                         for _,dir2 in pairs(possible_dirs) do
271                                 if turn_snap(pos,self,dir,dir2) then
272                                         return
273                                 end
274                                 if climb_snap(pos,self,dir,dir2) then
275                                         return
276                                 end
277                         end
278                 end
279         else
280                 coupling_logic(self)
281         end
282
283 end
284
285
286 --[[
287  █████╗ ██████╗ ██╗    ██████╗ ███████╗ ██████╗ ██╗███╗   ██╗
288 ██╔══██╗██╔══██╗██║    ██╔══██╗██╔════╝██╔════╝ ██║████╗  ██║
289 ███████║██████╔╝██║    ██████╔╝█████╗  ██║  ███╗██║██╔██╗ ██║
290 ██╔══██║██╔═══╝ ██║    ██╔══██╗██╔══╝  ██║   ██║██║██║╚██╗██║
291 ██║  ██║██║     ██║    ██████╔╝███████╗╚██████╔╝██║██║ ╚████║
292 ╚═╝  ╚═╝╚═╝     ╚═╝    ╚═════╝ ╚══════╝ ╚═════╝ ╚═╝╚═╝  ╚═══╝
293 ]]--
294
295
296 function register_train(name,data)
297 local train = {}
298
299 train.power            = data.power
300 train.coupler_distance = data.coupler_distance
301 train.is_car           = data.is_car
302 train.is_engine        = data.is_engine
303 train.speed_max        = data.speed_max
304
305 train.on_step = function(self,dtime)
306         if dtime > 0.1 then
307                 self.object:set_pos(self.old_pos)
308         end
309         local pos = vector.round(self.object:get_pos())
310         if not self.axis_lock then
311                 local possible_dirs = create_axis(pos)
312                 for _,dir in pairs(possible_dirs) do
313                         if dir.x ~=0 then
314                                 self.axis_lock = "x"
315                                 self.dir = dir
316                                 direction_snap(self)
317                                 break
318                         elseif dir.z ~= 0 then
319                                 self.axis_lock = "z"
320                                 self.dir = dir
321                                 direction_snap(self)
322                                 break
323                         end
324                 end
325         else
326                 rail_brain(self,pos)
327                 --collision_detect(self)
328         end
329         self.old_pos = self.object:get_pos()
330 end
331
332
333
334
335 train.on_punch = function(self, puncher)
336         if not puncher:get_wielded_item():get_name() == "train:wrench" then
337                 return
338         end
339
340         if self.is_engine and puncher:get_player_control().sneak then
341                 if vector.equals(self.object:get_velocity(),vector.new(0,0,0)) then
342                         print("reverse direction")
343                 end
344                 return
345         end
346
347         if self.coupler1 then
348                 self.coupler1:get_luaentity().coupler2 = nil
349                 self.coupler1 = nil
350         end
351
352         if self.coupler2 then
353                 self.coupler2:get_luaentity().coupler1 = nil
354                 self.coupler2 = nil
355         end
356
357 end
358
359
360 train.on_rightclick = function(self,clicker)
361         local pos = self.object:get_pos()
362         if clicker:get_wielded_item():get_name() == "utility:furnace" then
363                 local obj = minetest.add_entity(pos, "train:furnace")
364                 obj:set_attach(self.object,"",vector.new(0,0,0),vector.new(0,0,0))
365                 minetest.sound_play("wrench",{
366                         object = self.object,
367                         gain = 1.0,
368                         max_hear_distance = 64,
369                 })
370                 coupling_particles(pos,true)
371                 self.furnace = true
372                 return
373         end
374
375         if not clicker:get_wielded_item():get_name() == "train:wrench" then
376                 return
377         end
378
379         local name = clicker:get_player_name()
380         if not pool[name] then
381                 if not self.coupler2 then
382                         pool[name] = self.object
383                         minetest.sound_play("wrench",{
384                                 object = self.object,
385                                 gain = 1.0,
386                                 max_hear_distance = 64,
387                         })
388                         coupling_particles(pos,true)
389                 else
390                         minetest.sound_play("wrench",{
391                                 object = self.object,
392                                 gain = 1.0,
393                                 max_hear_distance = 64,
394                                 pitch = 0.7,
395                         })
396                         coupling_particles(pos,false)
397                 end
398         else
399                 if not self.is_engine and pool[name] ~= self.object and not (pool[name]:get_luaentity().coupler1 and pool[name]:get_luaentity().coupler1 == self.object or self.coupler2) then
400                         self.coupler1 = pool[name]
401                         pool[name]:get_luaentity().coupler2 = self.object
402                         minetest.sound_play("wrench",{
403                                 object = self.object,
404                                 gain = 1.0,
405                                 max_hear_distance = 64,
406                         })
407                         coupling_particles(pos,true)
408                 else
409                         minetest.sound_play("wrench",{
410                                 object = self.object,
411                                 gain = 1.0,
412                                 max_hear_distance = 64,
413                                 pitch = 0.7,
414                         })
415                         coupling_particles(pos,false)
416                 end
417                 pool[name] = nil
418         end
419 end
420
421 --get old data
422 train.on_activate = function(self,staticdata, dtime_s)
423         self.object:set_armor_groups({immortal=1})
424         if string.sub(staticdata, 1, string.len("return")) ~= "return" then
425                 return
426         end
427         local data = minetest.deserialize(staticdata)
428         if type(data) ~= "table" then
429                 return
430         end
431         self.old_pos = self.object:get_pos()
432         self.velocity = vector.new(0,0,0)
433 end
434
435 train.get_staticdata = function(self)
436         return minetest.serialize({
437         })
438 end
439
440
441
442 train.initial_properties = {
443         physical = false, -- otherwise going uphill breaks
444         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},
445         visual = "mesh",
446         mesh = "steam_train.b3d",
447         visual_size = {x=1, y=1},
448         textures = {"steam_train.png"},
449 }
450
451         
452
453 minetest.register_entity(name, train)
454
455 end
456
457
458
459
460
461
462
463
464
465
466 minetest.register_craftitem("train:minecart", {
467         description = "train",
468         inventory_image = "minecartitem.png",
469         wield_image = "minecartitem.png",
470         on_place = function(itemstack, placer, pointed_thing)
471                 if not pointed_thing.type == "node" then
472                         return
473                 end
474                 
475                 local sneak = placer:get_player_control().sneak
476                 local noddef = minetest.registered_nodes[minetest.get_node(pointed_thing.under).name]
477                 if not sneak and noddef.on_rightclick then
478                         minetest.item_place(itemstack, placer, pointed_thing)
479                         return
480                 end
481                 
482                 if minetest.get_item_group(minetest.get_node(pointed_thing.under).name, "rail")>0 then
483                         minetest.add_entity(pointed_thing.under, "train:train")
484                 else
485                         return
486                 end
487
488                 itemstack:take_item()
489
490                 return itemstack
491         end,
492 })
493
494 minetest.register_craft({
495         output = "train:train",
496         recipe = {
497                 {"main:iron", "", "main:iron"},
498                 {"main:iron", "main:iron", "main:iron"},
499         },
500 })
501
502
503
504
505
506 minetest.register_node("train:rail",{
507         description = "Rail",
508         wield_image = "rail.png",
509         tiles = {
510                 "rail.png", "railcurve.png",
511                 "railt.png", "railcross.png"
512         },
513         drawtype = "raillike",
514         paramtype = "light",
515         sunlight_propagates = true,
516         is_ground_content = false,
517         walkable = false,
518         node_placement_prediction = "",
519         selection_box = {
520                 type = "fixed",
521                 fixed = {-1/2, -1/2, -1/2, 1/2, -1/2+1/16, 1/2},
522         },
523         sounds = main.stoneSound(),
524         after_place_node = function(pos)
525                 data_injection(pos,true)
526         end,
527         after_destruct = function(pos)
528                 data_injection(pos)
529         end,
530         groups={stone=1,wood=1,rail=1,attached_node=1},
531 })
532
533
534 minetest.register_lbm({
535         name = "train:rail",
536         nodenames = {"train:rail"},
537         run_at_every_load = true,
538         action = function(pos)
539                 data_injection(pos,true)
540         end,
541 })
542
543 minetest.register_craft({
544         output = "train:rail 32",
545         recipe = {
546                 {"main:iron","","main:iron"},
547                 {"main:iron","main:stick","main:iron"},
548                 {"main:iron","","main:iron"}
549         }
550 })
551
552
553 minetest.register_food("train:wrench",{
554         description = "Train Wrench",
555         texture = "wrench.png",
556 })
557
558 minetest.register_craft({
559         output = "train:wrench",
560         recipe = {
561                 {"main:iron", "", "main:iron"},
562                 {"main:iron", "main:lapis", "main:iron"},
563                 {"", "main:lapis", ""}
564         }
565 })
566
567
568
569 minetest.register_entity("train:furnace", {
570         initial_properties = {
571                 visual = "wielditem",
572                 visual_size = {x = 0.6, y = 0.6},
573                 textures = {},
574                 physical = true,
575                 is_visible = false,
576                 collide_with_objects = false,
577                 pointable=false,
578                 collisionbox = {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5},
579         },
580         set_node = function(self)
581                 self.object:set_properties({
582                         is_visible = true,
583                         textures = {"utility:furnace"},
584                 })
585         end,
586
587
588         on_activate = function(self, staticdata)
589                 self.object:set_armor_groups({immortal = 1})
590
591                 self:set_node()
592         end,
593 })