Update global API.md wrt MRM
[MineClone/MineClone2.git] / mods / ENTITIES / mobs / mount.lua
blob09df15870af6e005c9933ff01eb0e476f0ead158
2 -- lib_mount by Blert2112 (edited by TenPlus1)
4 local enable_crash = false
5 local crash_threshold = 6.5 -- ignored if enable_crash=false
7 ------------------------------------------------------------------------------
9 --
10 -- Helper functions
13 local node_ok = function(pos, fallback)
15         fallback = fallback or mobs.fallback_node
17         local node = minetest.get_node_or_nil(pos)
19         if node and minetest.registered_nodes[node.name] then
20                 return node
21         end
23         return {name = fallback}
24 end
27 local function node_is(pos)
29         local node = node_ok(pos)
31         if node.name == "air" then
32                 return "air"
33         end
35         if minetest.get_item_group(node.name, "lava") ~= 0 then
36                 return "lava"
37         end
39         if minetest.get_item_group(node.name, "liquid") ~= 0 then
40                 return "liquid"
41         end
43         if minetest.registered_nodes[node.name].walkable == true then
44                 return "walkable"
45         end
47         return "other"
48 end
51 local function get_sign(i)
53         i = i or 0
55         if i == 0 then
56                 return 0
57         else
58                 return i / math.abs(i)
59         end
60 end
63 local function get_velocity(v, yaw, y)
65         local x = -math.sin(yaw) * v
66         local z =  math.cos(yaw) * v
68         return {x = x, y = y, z = z}
69 end
72 local function get_v(v)
73         return math.sqrt(v.x * v.x + v.z * v.z)
74 end
77 local function force_detach(player)
79         local attached_to = player:get_attach()
81         if not attached_to then
82                 return
83         end
85         local entity = attached_to:get_luaentity()
87         if entity.driver
88         and entity.driver == player then
90                 entity.driver = nil
91         end
93         player:set_detach()
94         mcl_player.player_attached[player:get_player_name()] = false
95         player:set_eye_offset({x = 0, y = 0, z = 0}, {x = 0, y = 0, z = 0})
96         mcl_player.player_set_animation(player, "stand" , 30)
97         player:set_properties({visual_size = {x = 1, y = 1} })
99 end
101 -------------------------------------------------------------------------------
104 minetest.register_on_leaveplayer(function(player)
105         force_detach(player)
106 end)
108 minetest.register_on_shutdown(function()
109         local players = minetest.get_connected_players()
110         for i = 1, #players do
111                 force_detach(players[i])
112         end
113 end)
115 minetest.register_on_dieplayer(function(player)
116         force_detach(player)
117         return true
118 end)
120 -------------------------------------------------------------------------------
122 function mobs.attach(entity, player)
124         local attach_at, eye_offset = {}, {}
126         entity.player_rotation = entity.player_rotation or {x = 0, y = 0, z = 0}
127         entity.driver_attach_at = entity.driver_attach_at or {x = 0, y = 0, z = 0}
128         entity.driver_eye_offset = entity.driver_eye_offset or {x = 0, y = 0, z = 0}
129         entity.driver_scale = entity.driver_scale or {x = 1, y = 1}
131         local rot_view = 0
133         if entity.player_rotation.y == 90 then
134                 rot_view = math.pi/2
135         end
137         attach_at = entity.driver_attach_at
138         eye_offset = entity.driver_eye_offset
139         entity.driver = player
141         force_detach(player)
143         player:set_attach(entity.object, "", attach_at, entity.player_rotation)
144         mcl_player.player_attached[player:get_player_name()] = true
145         player:set_eye_offset(eye_offset, {x = 0, y = 0, z = 0})
147         player:set_properties({
148                 visual_size = {
149                         x = entity.driver_scale.x,
150                         y = entity.driver_scale.y
151                 }
152         })
154         minetest.after(0.2, function()
155                 mcl_player.player_set_animation(player, "sit" , 30)
156         end)
158         --player:set_look_yaw(entity.object:get_yaw() - rot_view)
159         player:set_look_horizontal(entity.object:get_yaw() - rot_view)
163 function mobs.detach(player, offset)
165         force_detach(player)
167         mcl_player.player_set_animation(player, "stand" , 30)
169         local pos = player:get_pos()
171         pos = {x = pos.x + offset.x, y = pos.y + 0.2 + offset.y, z = pos.z + offset.z}
173         minetest.after(0.1, function()
174                 player:setpos(pos)
175         end)
179 function mobs.drive(entity, moving_anim, stand_anim, can_fly, dtime)
181         local rot_steer, rot_view = math.pi/2, 0
183         if entity.player_rotation.y == 90 then
184                 rot_steer, rot_view = 0, math.pi/2
185         end
187         local acce_y = 0
188         local velo = entity.object:getvelocity()
190         entity.v = get_v(velo) * get_sign(entity.v)
192         -- process controls
193         if entity.driver then
195 --print ("---velo", get_v(velo))
197                 local ctrl = entity.driver:get_player_control()
199                 -- move forwards
200                 if ctrl.up then
202                         entity.v = entity.v + entity.accel / 10
204                 -- move backwards
205                 elseif ctrl.down then
207                         if entity.max_speed_reverse == 0 and entity.v == 0 then
208                                 return
209                         end
211                         entity.v = entity.v - entity.accel / 10
212                 end
214                 -- fix mob rotation
215                 entity.object:setyaw(entity.driver:get_look_horizontal() - entity.rotate)
217                 if can_fly then
219                         -- fly up
220                         if ctrl.jump then
221                                 velo.y = velo.y + 1
222                                 if velo.y > entity.accel then velo.y = entity.accel end
224                         elseif velo.y > 0 then
225                                 velo.y = velo.y - 0.1
226                                 if velo.y < 0 then velo.y = 0 end
227                         end
229                         -- fly down
230                         if ctrl.sneak then
231                                 velo.y = velo.y - 1
232                                 if velo.y < -entity.accel then velo.y = -entity.accel end
234                         elseif velo.y < 0 then
235                                 velo.y = velo.y + 0.1
236                                 if velo.y > 0 then velo.y = 0 end
237                         end
239                 else
241                         -- jump
242                         if ctrl.jump then
244                                 if velo.y == 0 then
245                                         velo.y = velo.y + entity.jump_height
246                                         acce_y = acce_y + (acce_y * 3) + 1
247                                 end
248                         end
250                 end
251         end
253         -- if not moving then set animation and return
254         if entity.v == 0 and velo.x == 0 and velo.y == 0 and velo.z == 0 then
256                 if stand_anim then
257                         mobs:set_animation(entity, stand_anim)
258                 end
260                 return
261         end
262         
263         -- set moving animation
264         if moving_anim then
265                 mobs:set_animation(entity, moving_anim)
266         end
268         -- Stop!
269         local s = get_sign(entity.v)
271         entity.v = entity.v - 0.02 * s
273         if s ~= get_sign(entity.v) then
275                 entity.object:setvelocity({x = 0, y = 0, z = 0})
276                 entity.v = 0
277                 return
278         end
280         -- enforce speed limit forward and reverse
281         local max_spd = entity.max_speed_reverse
283         if get_sign(entity.v) >= 0 then
284                 max_spd = entity.max_speed_forward
285         end
287         if math.abs(entity.v) > max_spd then
288                 entity.v = entity.v - get_sign(entity.v)
289         end
291         -- Set position, velocity and acceleration
292         local p = entity.object:get_pos()
293         local new_velo = {x = 0, y = 0, z = 0}
294         local new_acce = {x = 0, y = -9.8, z = 0}
296         p.y = p.y - 0.5
298         local ni = node_is(p)
299         local v = entity.v
301         if ni == "air" then
303                 if can_fly == true then
304                         new_acce.y = 0
305                 end
307         elseif ni == "liquid" or ni == "lava" then
309                 if ni == "lava" and entity.lava_damage ~= 0 then
311                         entity.lava_counter = (entity.lava_counter or 0) + dtime
313                         if entity.lava_counter > 1 then
315                                 minetest.sound_play("default_punch", {
316                                         object = entity.object,
317                                         max_hear_distance = 5
318                                 })
320                                 entity.object:punch(entity.object, 1.0, {
321                                         full_punch_interval = 1.0,
322                                         damage_groups = {fleshy = entity.lava_damage}
323                                 }, nil)
325                                 entity.lava_counter = 0
326                         end
327                 end
329                 if entity.terrain_type == 2
330                 or entity.terrain_type == 3 then
332                         new_acce.y = 0
333                         p.y = p.y + 1
335                         if node_is(p) == "liquid" then
337                                 if velo.y >= 5 then
338                                         velo.y = 5
339                                 elseif velo.y < 0 then
340                                         new_acce.y = 20
341                                 else
342                                         new_acce.y = 5
343                                 end
344                         else
345                                 if math.abs(velo.y) < 1 then
346                                         local pos = entity.object:get_pos()
347                                         pos.y = math.floor(pos.y) + 0.5
348                                         entity.object:setpos(pos)
349                                         velo.y = 0
350                                 end
351                         end
352                 else
353                         v = v * 0.25
354                 end
355         end
357         new_velo = get_velocity(v, entity.object:get_yaw() - rot_view, velo.y)
358         new_acce.y = new_acce.y + acce_y
360         entity.object:setvelocity(new_velo)
361         entity.object:setacceleration(new_acce)
363         -- CRASH!
364         if enable_crash then
366                 local intensity = entity.v2 - v
368                 if intensity >= crash_threshold then
370 --print("----------- crash", intensity)
372                         entity.object:punch(entity.object, 1.0, {
373                                 full_punch_interval = 1.0,
374                                 damage_groups = {fleshy = intensity}
375                         }, nil)
377                 end
378         end
380         entity.v2 = v
384 -- directional flying routine by D00Med (edited by TenPlus1)
386 function mobs.fly(entity, dtime, speed, shoots, arrow, moving_anim, stand_anim)
388         local ctrl = entity.driver:get_player_control()
389         local velo = entity.object:getvelocity()
390         local dir = entity.driver:get_look_dir()
391         local yaw = entity.driver:get_look_horizontal() + 1.57 -- offset fix between old and new commands
392         local rot_steer, rot_view = math.pi / 2, 0
394         if entity.player_rotation.y == 90 then
395                 rot_steer, rot_view = 0, math.pi / 2
396         end
398         if ctrl.up then
399                 entity.object:setvelocity({
400                         x = dir.x * speed,
401                         y = dir.y * speed + 2,
402                         z = dir.z * speed
403                 })
405         elseif ctrl.down then
406                 entity.object:setvelocity({
407                         x = -dir.x * speed,
408                         y = dir.y * speed + 2,
409                         z = -dir.z * speed
410                 })
412         elseif not ctrl.down or ctrl.up or ctrl.jump then
413                 entity.object:setvelocity({x = 0, y = -2, z = 0})
414         end
416         entity.object:setyaw(yaw + math.pi + math.pi / 2 - entity.rotate)
418         -- firing arrows
419         if ctrl.LMB and ctrl.sneak and shoots then
421                 local pos = entity.object:get_pos()
422                 local obj = minetest.add_entity({
423                         x = pos.x + 0 + dir.x * 2.5,
424                         y = pos.y + 1.5 + dir.y,
425                         z = pos.z + 0 + dir.z * 2.5}, arrow)
427                 local ent = obj:get_luaentity()
428                 if ent then
429                         ent.switch = 1 -- for mob specific arrows
430                         ent.owner_id = tostring(entity.object) -- so arrows dont hurt entity you are riding
431                         local vec = {x = dir.x * 6, y = dir.y * 6, z = dir.z * 6}
432                         local yaw = entity.driver:get_look_horizontal()
433                         obj:setyaw(yaw + math.pi / 2)
434                         obj:setvelocity(vec)
435                 else
436                         obj:remove()
437                 end
438         end
440         -- change animation if stopped
441         if velo.x == 0 and velo.y == 0 and velo.z == 0 then
443                 mobs:set_animation(entity, stand_anim)
444         else
445                 -- moving animation
446                 mobs:set_animation(entity, moving_anim)
447         end