1 local S
= minetest
.get_translator("flying_carpet")
7 local function get_sign(i
)
15 local function get_velocity(v
, yaw
, y
)
16 local x
= math
.cos(yaw
)*v
17 local z
= math
.sin(yaw
)*v
18 return {x
=x
, y
=y
, z
=z
}
21 local function get_v(v
)
22 return math
.sqrt(v
.x^
2+v
.z^
2)
25 local function get_v3(v
)
26 return math
.sqrt(v
.x^
2+v
.y^
2+v
.z^
2)
29 local function magic_particle(object
, texture
)
30 local src
= object
:get_pos()
31 local velo
= object
:get_velocity()
32 velo
= vector
.multiply(velo
, 0.1)
33 if texture
== nil then texture
= "flying_carpet_magic_smoke.png" end
34 return minetest
.add_particlespawner({
37 minpos
= {x
=src
.x
-1.5, y
=src
.y
-0.03, z
=src
.z
-1.5},
38 maxpos
= {x
=src
.x
+1.5, y
=src
.y
+0.03, z
=src
.z
+1.},
39 minvel
= {x
=velo
.x
-0.4, y
=velo
.y
-0.1, z
=velo
.z
-0.4},
40 maxvel
= {x
=velo
.x
+0.4, y
=velo
.y
+0.1, z
=velo
.z
+0.4},
49 local function death_particles(object
)
50 local src
= object
:get_pos()
51 local yaw
= object
:get_yaw()
52 minetest
.add_particlespawner({
55 minpos
= {x
=src
.x
-1, y
=src
.y
-0.02, z
=src
.z
-1},
56 maxpos
= {x
=src
.x
+1, y
=src
.y
+0.02, z
=src
.z
+1},
57 minvel
= {x
=-0.01, y
=0, z
=0.01},
58 maxvel
= {x
=0.01, y
=0, z
=0.01},
63 texture
= "flying_carpet_death.png",
71 local mana_regen_cost
= 1.5
73 local mod_model
= minetest
.get_modpath("player_api") ~= nil
74 local mod_mana
= minetest
.get_modpath("mana") ~= nil
75 local mod_craft
= minetest
.get_modpath("default") ~= nil and minetest
.get_modpath("wool")
76 local mod_craft_mcl2
= minetest
.get_modpath("mcl_core") and minetest
.get_modpath("mcl_wool") and minetest
.get_modpath("mesecons_torch")
77 local mod_doc_items
= minetest
.get_modpath("doc_items") ~= nil
78 local mod_doc_identifier
= minetest
.get_modpath("doc_identifier") ~= nil
84 local init_velocities
= function()
87 table.insert(ret
,{x
=0,y
=0,z
=0})
92 local update_velocities
= function(velocities
, new_value
)
93 table.insert(velocities
, 1, new_value
)
94 table.remove(velocities
, 51)
100 collide_with_objects
= false,
101 collisionbox
= {-1,-0.02,-1, 1,0.02,1},
103 mesh
= "flying_carpet_model.obj",
104 textures
= {"flying_carpet_surface.png"},
106 driver
= nil, -- Attached player object
107 v
= 0, -- Velocity on the horizontal plane
108 h
= 0, -- Velocity on the vertical plane (height speed)
109 falling
= false, -- Fall mode: Carpet falls down and can't be controlled
110 flying
= false, -- If it is in fly mode
111 prefly
= true, -- Prefly mode: Carpet has been placed and waits to be boarded
112 starttimer
= 0, -- Time in seconds since the flight has been started
113 weartimer
= 0, -- Timer to apply wear for flying
114 viscosity
= 0, -- Viscosity of node in which the carpet is in (no liquid = 0)
115 wear
= 0, -- Damage / wear of carpet, corresponds to tool wear
116 sound_slide
= nil, -- Sound handle for sliding loop
117 sound_flight
= nil, -- Sound handle for flight loop
118 sound_death_warning
=nil, -- Sound handle for flight loop with high damage
119 slidepos
= nil, -- Position of nearby node which has been checked for sliding
120 slidenode
= nil, -- Node definition of nearby node which has been checked for sliding
121 liquidpos
= nil, -- Position of nearby node which has been checked for being a liquid
122 liquidnode
= nil, -- Node definition of nearby node which has been checked for being a liquid
123 past_velocities
= init_velocities(), -- List of recent velocities
124 past_diff
= 0, -- Difference of the past two velocities, used to deal collision damage to player
125 last_damage
= nil, -- How much damage has been dealt to the player in the last step
126 last_particle
= 1, -- Particle spawning timer
129 -- Rightclick allows to attach and detach to/from carpet
130 function carpet
:on_rightclick(clicker
)
131 if not clicker
or not clicker
:is_player() then
134 local name
= clicker
:get_player_name()
135 -- Detach attached player
136 if self
.driver
and clicker
== self
.driver
then
140 player_api
.player_attached
[name
] = false
141 player_api
.set_animation(clicker
, "stand", 30)
143 if mod_mana
and self
.flying
then
144 mana
.setregen(clicker
:get_player_name(), mana
.getregen(clicker
:get_player_name())+mana_regen_cost
)
145 clicker
:get_meta():set_int("flying_carpet:mana_cost", 0)
147 -- Attach player (must be unoccupied and slow)
148 elseif not self
.driver
and clicker
:get_attach() == nil and ((self
.v
< 0.5 and math
.abs(self
.h
) < 1) or self
.prefly
) then
149 self
.driver
= clicker
150 clicker
:set_look_horizontal(self
.object
:get_yaw()-math
.pi
/2)
151 clicker
:set_attach(self
.object
, "", {x
=-4,y
=0.3,z
=0}, {x
=0,y
=90,z
=0})
153 player_api
.player_attached
[name
] = true
154 minetest
.after(0.2, function(player
)
155 if player
and player
:is_player() then
156 player_api
.set_animation(player
, "sit", 30)
160 if mod_mana
and (self
.flying
or self
.prefly
) then
161 mana
.setregen(clicker
:get_player_name(), mana
.getregen(clicker
:get_player_name())-mana_regen_cost
)
162 clicker
:get_meta():set_int("flying_carpet:mana_cost", 1)
168 self
.object
:set_velocity(get_velocity(4, self
.object
:get_yaw(), 0))
173 function carpet
:on_activate(staticdata
, dtime_s
)
174 self
.object
:set_armor_groups({fabric
=100})
175 if staticdata
~= nil and staticdata
~= "" then
176 local data
= minetest
.deserialize(staticdata
)
180 self
.falling
= data
.falling
181 self
.flying
= data
.flying
182 self
.prefly
= data
.prefly
183 self
.viscosity
= data
.viscosity
184 self
.wear
= data
.wear
186 minetest
.log("error", "[flying_carpet] carpet:on_activate: deserialized data was nil!")
192 function carpet
:get_staticdata()
193 if self
.sound_flight
then
194 minetest
.sound_stop(self
.sound_flight
)
195 self
.sound_flight
= nil
197 if self
.sound_slide
then
198 minetest
.sound_stop(self
.sound_slide
)
199 self
.sound_slide
= nil
201 if self
.sound_death_warning
then
202 minetest
.sound_stop(self
.sound_death_warning
)
203 self
.sound_death_warning
= nil
208 data
.falling
= self
.falling
209 data
.flying
= self
.flying
210 data
.prefly
= self
.prefly
211 data
.viscosity
= self
.viscosity
212 data
.wear
= self
.wear
213 return minetest
.serialize(data
)
216 -- Stop flying or collect carpet on punching it
217 function carpet
:on_punch(puncher
, time_from_last_punch
, tool_capabilities
, direction
)
218 if puncher
and puncher
:is_player() then
219 -- Owner of flying carpet punches
220 if (self
.driver
== puncher
and self
.flying
) then
221 -- Go into falling mode immediately
222 minetest
.sound_play("flying_carpet_out_of_energy", {pos
= self
.object
:get_pos(), gain
=0.7}, true)
223 magic_particle(self
.object
)
225 mana
.setregen(puncher
:get_player_name(), mana
.getregen(puncher
:get_player_name())+mana_regen_cost
)
226 puncher
:get_meta():set_int("flying_carpet:mana_cost", 0)
230 if self
.sound_flight
then
231 minetest
.sound_stop(self
.sound_flight
)
232 self
.sound_flight
= nil
234 if self
.sound_death_warning
then
235 minetest
.sound_stop(self
.sound_death_warning
)
236 self
.sound_death_warning
= nil
238 -- Someone punches slow non-flying carpet
239 elseif (self
.driver
== nil or self
.driver
== puncher
) and (self
.v
< 1 and math
.abs(self
.h
) < 1) and not self
.flying
then
241 local carpetitem
= ItemStack("flying_carpet:carpet")
242 carpetitem
:set_wear(self
.wear
)
243 local inv
= puncher
:get_inventory()
244 if not minetest
.settings
:get_bool("creative_mode") or not inv
:contains_item("main", "flying_carpet:carpet") then
245 local leftover
= inv
:add_item("main", carpetitem
)
246 -- Put item into world if no space in inventory
247 if not leftover
:is_empty() then
248 minetest
.add_item(self
.object
:get_pos(), leftover
)
251 minetest
.sound_play("flying_carpet_take", {pos
=self
.object
:get_pos(), gain
=0.3}, true)
254 player_api
.player_attached
[puncher
:get_player_name()] = false
256 if self
.sound_flight
then
257 minetest
.sound_stop(self
.sound_flight
)
258 self
.sound_flight
= nil
260 if self
.sound_death_warning
then
261 minetest
.sound_stop(self
.sound_death_warning
)
262 self
.sound_death_warning
= nil
264 if self
.sound_slide
then
265 minetest
.sound_stop(self
.sound_slide
)
266 self
.sound_slide
= nil
273 function carpet
:on_step(dtime
)
274 if not self
.prefly
then
276 self
.v
= get_v(self
.object
:get_velocity())*get_sign(self
.v
)
277 self
.h
= self
.object
:get_velocity().y
279 local spawn_particles
280 self
.last_particle
= self
.last_particle
+ 0.1
281 if self
.last_particle
> 0.1 then
282 spawn_particles
= true
283 self
.last_particle
= 0
285 spawn_particles
= false
288 -- check for big recent changes in speed, (likely caused by collisions)
289 self
.past_velocities
= update_velocities(self
.past_velocities
, self
.object
:get_velocity())
290 local avg
= {x
=0,y
=0,z
=0}
291 for i
=1,#self
.past_velocities
do
292 avg
.x
= avg
.x
+ self
.past_velocities
[i
].x
293 avg
.y
= avg
.y
+ self
.past_velocities
[i
].y
294 avg
.z
= avg
.z
+ self
.past_velocities
[i
].z
296 avg
.x
= avg
.x
/#self
.past_velocities
297 avg
.y
= avg
.y
/#self
.past_velocities
298 avg
.z
= avg
.z
/#self
.past_velocities
300 local v3_avg
= get_v3(avg
)
301 local v3
= get_v3(self
.object
:get_velocity())
303 local diff
= math
.abs(v3_avg
- v3
)
305 if not minetest
.settings
:get_bool("creative_mode") then
306 if self
.last_damage
~= nil then
307 self
.last_damage
= self
.last_damage
+ dtime
310 if self
.last_damage
== nil or self
.last_damage
> 1 then
311 -- hurt the driver if there was a large recent speed change
312 if self
.driver
and diff
>= 9 and diff
> self
.past_diff
then
313 -- ... but don't hurt the driver too many times in a row accidentally
314 self
.driver
:set_hp(self
.driver
:get_hp() - math
.floor(diff
- 9))
317 -- damage the carpet if there was a large recent speed change
318 if diff
>= 6 and diff
> self
.past_diff
then
319 -- ... but don't damage the carpet too many times in a row accidentally
320 self
.wear
= self
.wear
+ math
.floor((diff
- 6)*700)
326 self
.past_diff
= diff
329 if not minetest
.settings
:get_bool("creative_mode") then
330 -- Add wear for flying
331 self
.weartimer
= self
.weartimer
+ dtime
332 -- This allows a flight of roughly 12 hours until the maximum wear of 65535 is reached
333 local add_wear_at
= 0.32959
334 if(self
.weartimer
> add_wear_at
) then
335 self
.wear
= self
.wear
+ math
.floor((self
.weartimer
/ add_wear_at
))
336 self
.weartimer
= self
.weartimer
% add_wear_at
341 local ctrl
= self
.driver
:get_player_control()
344 local vpenalty
= 2 + self
.viscosity
345 self
.v
= self
.v
+ 0.1 / vpenalty
351 self
.object
:set_yaw(self
.object
:get_yaw()+math
.pi
/600+dtime
*math
.pi
/600)
354 self
.object
:set_yaw(self
.object
:get_yaw()-math
.pi
/600-dtime
*math
.pi
/600)
368 if mana
.getregen(self
.driver
:get_player_name()) < 0 and mana
.get(self
.driver
:get_player_name()) == 0 then
369 minetest
.sound_play("flying_carpet_out_of_energy", {pos
= self
.object
:get_pos(), gain
=0.7}, true)
370 magic_particle(self
.object
)
373 mana
.setregen(self
.driver
:get_player_name(), mana
.getregen(self
.driver
:get_player_name())+mana_regen_cost
)
374 self
.driver
:get_meta():set_int("flying_carpet:mana_cost", 0)
375 if self
.sound_flight
then
376 minetest
.sound_stop(self
.sound_flight
)
377 self
.sound_flight
= nil
379 if self
.sound_death_warning
then
380 minetest
.sound_stop(self
.sound_death_warning
)
381 self
.sound_death_warning
= nil
386 -- Harsh speed penalty for being in a liquid with viscosity > 0
387 local vpenalty
= 0.09 * self
.viscosity
390 self
.v
= self
.v
- 0.02 - vpenalty
391 elseif self
.v
> 12 then
392 self
.v
= self
.v
- 0.02 - vpenalty
393 elseif self
.v
< 6 then
394 self
.v
= self
.v
+ 0.06 - vpenalty
395 elseif self
.v
< 9 then
396 self
.v
= self
.v
+ 0.04 - vpenalty
397 elseif self
.v
< 12 then
398 self
.v
= self
.v
+ 0.02 - vpenalty
402 -- Harsh speed penalty for being in a liquid with viscosity > 0
403 local vpenalty
= 0.09 * self
.viscosity
404 -- Slow down flying carpet if no player is attached
405 self
.v
= self
.v
- 0.025 - vpenalty
407 if self
.v
> 3 and self
.flying
then
408 if spawn_particles
then
411 star
= "flying_carpet_star.png"
413 star
= "flying_carpet_star_warning.png"
415 local src
= self
.object
:get_pos()
416 local velo
= self
.object
:get_velocity()
417 -- Speed indicator particles
418 minetest
.add_particlespawner({
419 amount
= math
.ceil(math
.min(3,math
.max(0,self
.v
+3))),
421 minpos
= {x
=src
.x
-0.6, y
=src
.y
-0.02, z
=src
.z
-0.6},
422 maxpos
= {x
=src
.x
+0.6, y
=src
.y
-0.02, z
=src
.z
+0.6},
423 minvel
= {x
=velo
.x
*0.01,y
=velo
.y
*0.01,z
=velo
.z
*0.01},
424 maxvel
= {x
=velo
.x
*0.01,y
=velo
.y
*0.01,z
=velo
.z
*0.01},
431 -- Death warning particles
432 if not minetest
.settings
:get_bool("creative_mode") and self
.wear
> 58499 then
433 minetest
.add_particlespawner({
434 --[[ More speed and more wear = more particles.
435 Carefully adjusted to roughly match the number of speed indicator particles
436 At near full wear, the number of death warning particles should be equal
437 to the number of speed indicator particles ]]
438 amount
= math
.ceil(math
.min(3,math
.max(0,(self
.v
+3)/(18-(self
.wear
-58499)/391)))),
440 minpos
= {x
=src
.x
-0.6, y
=src
.y
-0.06, z
=src
.z
-0.6},
441 maxpos
= {x
=src
.x
+0.6, y
=src
.y
-0.06, z
=src
.z
+0.6},
442 minvel
= {x
=velo
.x
*0.005,y
=velo
.y
*0.005,z
=velo
.z
*0.005},
443 maxvel
= {x
=velo
.x
*0.005,y
=velo
.y
*0.005,z
=velo
.z
*0.005},
448 texture
= "flying_carpet_star_death_warning.png"
452 if not self
.sound_flight
then
453 self
.sound_flight
= minetest
.sound_play("flying_carpet_flight", {object
= self
.object
, gain
= 0.6, max_hear_distance
= 16, loop
= true })
455 if not minetest
.settings
:get_bool("creative_mode") and self
.wear
> 64624 and not self
.sound_death_warning
then
456 self
.sound_death_warning
= minetest
.sound_play("flying_carpet_death_warning", {object
= self
.object
, gain
= 0.2, max_hear_distance
= 24, loop
= true })
459 if self
.sound_flight
then
460 minetest
.sound_stop(self
.sound_flight
)
461 self
.sound_flight
= nil
463 if self
.sound_death_warning
then
464 minetest
.sound_stop(self
.sound_death_warning
)
465 self
.sound_death_warning
= nil
470 local sh
= get_sign(self
.h
)
472 local op
= self
.object
:get_pos()
473 local ps
= table.copy(op
)
476 if self
.slidepos
~= nil and vector
.equals(vector
.round(ps
), self
.slidepos
) then
479 n
= minetest
.get_node(ps
)
480 self
.slidepos
= vector
.round(ps
)
483 if n
.name
~= "ignore" and n
.name
~= "air" and n
.name
~= nil then
484 local comma
= ps
.y
- math
.floor(ps
.y
)
485 if minetest
.registered_nodes
[n
.name
].walkable
and self
.h
< 0.001 and comma
> 0.519 and comma
< 0.53 then
486 self
.v
= self
.v
- 0.2
487 if minetest
.settings
:get_bool("creative_mode") and self
.v
> 6 then
488 -- Apply a small wear for sliding
489 self
.wear
= self
.wear
+ math
.floor(self
.v
)
492 if spawn_particles
then
493 local src
= self
.object
:get_pos()
494 local velo
= self
.object
:get_velocity()
495 minetest
.add_particlespawner({
496 amount
= math
.ceil(math
.min(10,math
.max(0,self
.v
))),
498 minpos
= {x
=src
.x
-0.6, y
=src
.y
-0.02, z
=src
.z
-0.6},
499 maxpos
= {x
=src
.x
+0.6, y
=src
.y
-0.02, z
=src
.z
+0.6},
500 minvel
= {x
=-velo
.x
*0.01-0.1, y
=velo
.y
*0.05+0.01, z
=-velo
.z
*0.01-0.1},
501 maxvel
= {x
=-velo
.x
*0.01+0.1, y
=velo
.y
*0.05+0.05, z
=-velo
.z
*0.01+0.1},
506 texture
= "flying_carpet_smoke.png",
509 if self
.v
> 1 and not self
.sound_slide
then
510 self
.sound_slide
= minetest
.sound_play("flying_carpet_slide", {object
= self
.object
, gain
= 0.5, max_hear_distance
= 24, loop
= true })
511 elseif self
.v
<= 1 and self
.sound_slide
then
512 minetest
.sound_stop(self
.sound_slide
)
513 self
.sound_slide
= nil
515 elseif self
.sound_slide
then
516 minetest
.sound_stop(self
.sound_slide
)
517 self
.sound_slide
= nil
519 elseif self
.sound_slide
then
520 minetest
.sound_stop(self
.sound_slide
)
521 self
.sound_slide
= nil
524 local pl
= table.copy(op
)
526 if self
.liquidpos
~= nil and vector
.equals(vector
.round(pl
), self
.liquidpos
) then
529 n
= minetest
.get_node(pl
)
530 self
.liquidpos
= vector
.round(pl
)
534 local ndef
= minetest
.registered_nodes
[n
.name
]
535 if ndef
.liquidtype
~= "none" then
537 self
.h
= self
.h
+ 0.05
538 self
.viscosity
= ndef
.liquid_viscosity
544 self
.starttimer
= self
.starttimer
+ dtime
546 if (self
.v
< 6 and self
.starttimer
>= 5) or (self
.v
< 0.5 and self
.starttimer
>= 0.5) then
547 if self
.falling
== false then
548 minetest
.sound_play("flying_carpet_out_of_energy", {pos
= self
.object
:get_pos(), gain
=0.7}, true)
551 if mod_mana
and self
.driver
~= nil then
552 mana
.setregen(self
.driver
:get_player_name(), mana
.getregen(self
.driver
:get_player_name())+mana_regen_cost
)
553 self
.driver
:get_meta():set_int("flying_carpet:mana_cost", 0)
555 magic_particle(self
.object
)
556 if self
.sound_flight
then
557 self
.sound_flight
= minetest
.sound_stop(self
.sound_flight
)
558 self
.sound_flight
= nil
560 if self
.sound_death_warning
then
561 self
.sound_death_warning
= minetest
.sound_stop(self
.sound_death_warning
)
562 self
.sound_death_warning
= nil
564 self
.v
= self
.v
- 0.04
567 if self
.h
> 0.01 then
568 self
.h
= self
.h
- 0.005
569 elseif self
.h
< 0.01 then
570 self
.h
= self
.h
+ 0.005
572 if math
.abs(self
.h
) > 1 then
575 if math
.abs(self
.h
) < 0.01 then
587 y
= self
.object
:get_velocity().y
588 if self
.viscosity
> 0 then
589 local vpenalty
= 0.09 * self
.viscosity
590 self
.v
= self
.v
- 0.06 - vpenalty
592 self
.object
:set_acceleration({x
=0,y
=0,z
=0})
593 local v_ref
= -0.4 / self
.viscosity
595 if(math
.abs(y
-v_ref
) < (dampen
+0.01)*self
.viscosity
) then
599 y
= y
- dampen
*self
.viscosity
601 y
= y
+ dampen
*self
.viscosity
605 self
.object
:set_acceleration({x
=0,y
=-tonumber(minetest
.settings
:get("movement_gravity")),z
=0})
609 self
.object
:set_acceleration({x
=0,y
=0,z
=0})
611 self
.object
:set_velocity(get_velocity(self
.v
, self
.object
:get_yaw(), y
))
616 if minetest
.settings
:get_bool("creative_mode") and self
.wear
>= 65535 then
617 -- Destroy carpet if it took to much damage
618 death_particles(self
.object
)
619 if self
.sound_flight
then
620 minetest
.sound_stop(self
.sound_flight
)
621 self
.sound_flight
= nil
623 if self
.sound_death_warning
then
624 minetest
.sound_stop(self
.sound_death_warning
)
625 self
.sound_death_warning
= nil
627 if self
.sound_slide
then
628 minetest
.sound_stop(self
.sound_slide
)
629 self
.sound_slide
= nil
631 -- Detach driver and refund mana
632 if self
.driver
~= nil then
633 self
.driver
:set_detach()
635 player_api
.player_attached
[self
.driver
:get_player_name()] = false
636 player_api
.set_animation(self
.driver
, "stand", 30)
639 mana
.setregen(self
.driver
:get_player_name(), mana
.getregen(self
.driver
:get_player_name())+mana_regen_cost
)
640 self
.driver
:get_meta():set_int("flying_carpet:mana_cost", 0)
643 minetest
.sound_play("flying_carpet_out_of_energy", {pos
= self
.object
:get_pos(), gain
=0.7}, true)
649 minetest
.register_entity("flying_carpet:carpet", carpet
)
651 local longdesc
, usagehelp
, durability
652 if mod_doc_items
then
653 longdesc
= S("Quickly explore the vast terrain with the magical flying carpet. But only the skilled users can master its speed, the fools will crash and hurt themselves. The carpet flies fast horizontally, but is only slowly able to change its rotation or height.")
655 if minetest
.settings
:get_bool("creative_mode") then
656 longdesc
= longdesc
.. S(" It constantly reduces your mana resources and will wear out over time.")
658 longdesc
= longdesc
.. S(" It constantly reduces your mana resources.")
660 elseif not minetest
.settings
:get_bool("creative_mode") then
661 longdesc
= longdesc
.. S(" It will wear out over time.")
664 usagehelp
= usagehelp
.. S("Look to the desired initial flight direction and place the carpet on any flat open surface. Make sure it has enough space (3×2×3), then place it. Right-click the carpet to sit on it and depart. You can only enter the flying carpet if it is not moving itself and you're not in another vehicle.") .. "\n\n"
666 usagehelp
= usagehelp
.. S("Flight is controlled with the movement keys: Up to speed up, down to slow down, left and right to rotate slowly. Jump to rise, sneak to sink.") .. "\n\n"
668 usagehelp
= usagehelp
.. S("The flying carpet has a basic movement speed which it is aiming to reach; thus, it flies without your intervention. Speed changes apply only for as long as you hold down the keys. As soon as you release the keys, the carpet will go back to its basic speed again. When the carpet is under a certain critical speed, it loses its magic and just drops on the ground. There's a short “grace period” at the beginning of the flight where this minimum speed limit does not apply. Watch the particles emitted by the carpet carefully: If they are yellow, everything is okay, but if they become red this means the carpet is going dangerously slow and you should speed up.") .. "\n\n"
670 usagehelp
= usagehelp
.. S("The flying carpet also fails immediately if you crash into the landscape, which may even hurt you. You can also also take fall damage, but it is greatly reduced.")
673 usagehelp
= usagehelp
.. S(" Your carpet will also stop working if your mana resources are depleted.")
675 usagehelp
= usagehelp
.. S(" When your carpet stopped working, you have to collect the carpet (punch it) and place it again.") .. "\n\n"
677 usagehelp
= usagehelp
.. S("If you fly directly down onto a flat solid ground, your flying carpet will come to a halt quickly due to friction. Your carpet can not fly into liquids from above, but it can fly into them from the side or even below. But the speed in liquids is greatly reduced and your carpet will likely fail.") .. "\n\n"
679 if not minetest
.settings
:get_bool("creative_mode") then
680 usagehelp
= usagehelp
.. S("Your carpet is not indestructible! Long flights, scratching and crashes will wear out the carpet over time and it might get destroyed eventually. Crashes deal major damage, sliding deals a very minor wear and flying deals a tiny amount of wear.") .. "\n\n"
681 usagehelp
= usagehelp
.. S("On high wear levels, the carpet will emit black particles which will increase in number with its wear. At this point, you should get a replacement soon. On a critical wear level, the carpet will emit a very annoying loud noise while flying. If you hear this, you will have roughly five minutes worth of flight until the carpet finally disintegrates under your feet! Additionally, a flying carpet will disintegrate and is lost forever if it stands still and has no user for 1 minute.")
683 durability
= S("Ca. 12 hours worth of flight time, if you fly without accidents.")
687 minetest
.register_tool("flying_carpet:carpet", {
688 description
= S("Flying carpet"),
689 _doc_items_longdesc
= longdesc
,
690 _doc_items_usagehelp
= usagehelp
,
691 _doc_items_durability
= durability
,
692 inventory_image
= "flying_carpet_inventory.png",
693 wield_image
= "flying_carpet_wield.png",
694 wield_scale
= {x
=1.374, y
=2, z
=0.2},
696 on_place
= function(itemstack
, placer
, pointed_thing
)
697 if pointed_thing
.type ~= "node" then
700 local place_pos
= pointed_thing
.under
701 place_pos
.y
= place_pos
.y
+1.5
702 -- check if there is enough free space for the carpet
703 local check_pos
= vector
.round(place_pos
)
707 local node
= minetest
.get_node({x
=check_pos
.x
+x
, y
=check_pos
.y
+y
, z
=check_pos
.z
+z
})
708 local nodedef
= minetest
.registered_nodes
[node
.name
]
709 if not (not nodedef
.walkable
and nodedef
.liquidtype
== "none") then
716 -- check if carpet wouldn't collide with any nearby carpet
717 local conflict_objects
= minetest
.get_objects_inside_radius(place_pos
, math
.sqrt(2)*1.5)
718 for i
=1, #conflict_objects
do
719 local le
= conflict_objects
[i
]:get_luaentity()
720 if le
~= nil and le
.name
== "flying_carpet:carpet" then
725 local ent
= minetest
.add_entity(place_pos
, "flying_carpet:carpet")
726 ent
:set_yaw(placer
:get_look_horizontal()+math
.pi
/2)
727 ent
:get_luaentity().wear
= itemstack
:get_wear()
729 if not minetest
.settings
:get_bool("creative_mode") then
730 itemstack
:take_item()
732 minetest
.sound_play("flying_carpet_place", {pos
= place_pos
, gain
= 1}, true)
738 -- Restore mana regeneration when players leave
739 minetest
.register_on_joinplayer(function(player
)
740 local name
= player
:get_player_name()
741 local meta
= player
:get_meta()
742 local mana_cost
= meta
:get_int("flying_carpet:mana_cost") == 1
744 mana
.setregen(name
, mana
.getregen(name
)+mana_regen_cost
)
745 minetest
.log("info", "[flying_carpet] Restoring mana regen for "..name
..", who was previously attached to a carpet")
751 minetest
.register_craft({
752 output
= "flying_carpet:carpet",
753 recipe
= { { "wool:red", "wool:yellow", "wool:red" },
754 { "wool:yellow", "wool:red", "wool:yellow" },
755 { "default:mese_crystal", "default:goldblock", "default:mese_crystal" } }
758 if mod_craft_mcl2
then
759 minetest
.register_craft({
760 output
= "flying_carpet:carpet",
761 recipe
= { { "mcl_wool:red", "mcl_wool:yellow", "mcl_wool:red" },
762 { "mcl_wool:yellow", "mcl_wool:red", "mcl_wool:yellow" },
763 { "mesecons_torch:redstoneblock", "mcl_core:goldblock", "mesecons_torch:redstoneblock" } }
767 if mod_doc_identifier
then
768 doc
.sub
.identifier
.register_object("flying_carpet:carpet", "tools", "flying_carpet:carpet")