4 -- Snowballs and other throwable items
7 local GRAVITY
= tonumber(minetest
.settings
:get("movement_gravity"))
9 local entity_mapping
= {
10 ["mcl_throwing:snowball"] = "mcl_throwing:snowball_entity",
11 ["mcl_throwing:egg"] = "mcl_throwing:egg_entity",
12 ["mcl_throwing:ender_pearl"] = "mcl_throwing:ender_pearl_entity",
16 ["mcl_throwing:snowball_entity"] = 22,
17 ["mcl_throwing:egg_entity"] = 22,
18 ["mcl_throwing:ender_pearl_entity"] = 22,
21 mcl_throwing
.throw
= function(throw_item
, pos
, dir
, velocity
)
22 if velocity
== nil then
23 velocity
= velocities
[throw_item
]
25 if velocity
== nil then
29 local itemstring
= ItemStack(throw_item
):get_name()
30 local obj
= minetest
.add_entity(pos
, entity_mapping
[itemstring
])
31 obj
:setvelocity({x
=dir
.x
*velocity
, y
=dir
.y
*velocity
, z
=dir
.z
*velocity
})
32 obj
:setacceleration({x
=dir
.x
*-3, y
=-GRAVITY
, z
=dir
.z
*-3})
37 local throw_function
= function(entity_name
, velocity
)
38 local func
= function(item
, player
, pointed_thing
)
39 local playerpos
= player
:get_pos()
40 local dir
= player
:get_look_dir()
41 local obj
= mcl_throwing
.throw(item
, {x
=playerpos
.x
, y
=playerpos
.y
+1.5, z
=playerpos
.z
}, dir
, velocity
)
42 obj
:get_luaentity()._thrower
= player
:get_player_name()
43 if not minetest
.settings
:get_bool("creative_mode") then
51 local dispense_function
= function(stack
, dispenserpos
, droppos
, dropnode
, dropdir
)
52 -- Launch throwable item
53 local shootpos
= vector
.add(dispenserpos
, vector
.multiply(dropdir
, 0.51))
54 mcl_throwing
.throw(stack
:get_name(), shootpos
, dropdir
)
57 -- Staticdata handling because objects may want to be reloaded
58 local get_staticdata
= function(self
)
60 _lastpos
= self
._lastpos
,
61 _thrower
= self
._thrower
,
63 return minetest
.serialize(data
)
66 local on_activate
= function(self
, staticdata
, dtime_s
)
67 local data
= minetest
.deserialize(staticdata
)
69 self
._lastpos
= data
._lastpos
70 self
._thrower
= data
._thrower
74 -- The snowball entity
75 local snowball_ENTITY
={
78 textures
= {"mcl_throwing_snowball.png"},
79 visual_size
= {x
=0.5, y
=0.5},
80 collisionbox
= {0,0,0,0,0,0},
82 get_staticdata
= get_staticdata
,
83 on_activate
= on_activate
,
90 textures
= {"mcl_throwing_egg.png"},
91 visual_size
= {x
=0.45, y
=0.45},
92 collisionbox
= {0,0,0,0,0,0},
94 get_staticdata
= get_staticdata
,
95 on_activate
= on_activate
,
103 textures
= {"mcl_throwing_ender_pearl.png"},
104 visual_size
= {x
=0.9, y
=0.9},
105 collisionbox
= {0,0,0,0,0,0},
107 get_staticdata
= get_staticdata
,
108 on_activate
= on_activate
,
111 _thrower
= nil, -- Player ObjectRef of the player who threw the ender pearl
114 -- Snowball on_step()--> called when snowball is moving.
115 local snowball_on_step
= function(self
, dtime
)
116 self
.timer
=self
.timer
+dtime
117 local pos
= self
.object
:get_pos()
118 local node
= minetest
.get_node(pos
)
119 local def
= minetest
.registered_nodes
[node
.name
]
121 -- Destroy when hitting a solid node
122 if self
._lastpos
.x
~=nil then
123 if (def
and def
.walkable
) or not def
then
128 self
._lastpos
={x
=pos
.x
, y
=pos
.y
, z
=pos
.z
} -- Set _lastpos-->Node will be added at last pos outside the node
131 -- Movement function of egg
132 local egg_on_step
= function(self
, dtime
)
133 self
.timer
=self
.timer
+dtime
134 local pos
= self
.object
:get_pos()
135 local node
= minetest
.get_node(pos
)
136 local def
= minetest
.registered_nodes
[node
.name
]
138 -- Destroy when hitting a solid node
139 if self
._lastpos
.x
~=nil then
140 if (def
and def
.walkable
) or not def
then
141 -- 1/8 chance to spawn a chick
142 -- FIXME: Chicks have a quite good chance to spawn in walls
143 local r
= math
.random(1,8)
145 -- Turn given object into a child
146 local make_child
= function(object
)
147 local ent
= object
:get_luaentity()
148 object
:set_properties({
149 visual_size
= { x
= ent
.base_size
.x
/2, y
= ent
.base_size
.y
/2 },
151 ent
.base_colbox
[1]/2,
152 ent
.base_colbox
[2]/2,
153 ent
.base_colbox
[3]/2,
154 ent
.base_colbox
[4]/2,
155 ent
.base_colbox
[5]/2,
156 ent
.base_colbox
[6]/2,
162 make_child(minetest
.add_entity(self
._lastpos
, "mobs_mc:chicken"))
164 -- BONUS ROUND: 1/32 chance to spawn 3 additional chicks
165 local r
= math
.random(1,32)
169 { x
=-0.7, y
=0, z
=-0.7 },
170 { x
=-0.7, y
=0, z
=0.7 },
173 local pos
= vector
.add(self
._lastpos
, offsets
[o
])
174 make_child(minetest
.add_entity(pos
, "mobs_mc:chicken"))
182 self
._lastpos
={x
=pos
.x
, y
=pos
.y
, z
=pos
.z
} -- Set lastpos-->Node will be added at last pos outside the node
185 -- Movement function of ender pearl
186 local pearl_on_step
= function(self
, dtime
)
187 self
.timer
=self
.timer
+dtime
188 local pos
= self
.object
:get_pos()
189 pos
.y
= math
.floor(pos
.y
)
190 local node
= minetest
.get_node(pos
)
192 local def
= minetest
.registered_nodes
[node
.name
]
194 -- Destroy when hitting a solid node
195 if self
._lastpos
.x
~=nil then
196 local walkable
= (def
and def
.walkable
)
198 -- No teleport for hitting ignore for now. Otherwise the player could get stuck.
199 -- FIXME: This also means the player loses an ender pearl for throwing into unloaded areas
200 if node
.name
== "ignore" then
202 -- Activate when hitting a solid node or a plant
203 elseif walkable
or nn
== "mcl_core:vine" or nn
== "mcl_core:deadbush" or minetest
.get_item_group(nn
, "flower") ~= 0 or minetest
.get_item_group(nn
, "sapling") ~= 0 or minetest
.get_item_group(nn
, "plant") ~= 0 or minetest
.get_item_group(nn
, "mushroom") ~= 0 or not def
then
204 local player
= minetest
.get_player_by_name(self
._thrower
)
206 -- Teleport and hurt player
208 -- First determine good teleport position
209 local dir
= {x
=0, y
=0, z
=0}
211 local v
= self
.object
:getvelocity()
213 local vc
= table.copy(v
) -- vector for calculating
214 -- Node is walkable, we have to find a place somewhere outside of that node
215 vc
= vector
.normalize(vc
)
217 -- Zero-out the two axes with a lower absolute value than
218 -- the axis with the strongest force
220 lv
, ld
= math
.abs(vc
.y
), "y"
221 if math
.abs(vc
.x
) > lv
then
222 lv
, ld
= math
.abs(vc
.x
), "x"
224 if math
.abs(vc
.z
) > lv
then
225 lv
, ld
= math
.abs(vc
.z
), "z"
227 if ld
~= "x" then vc
.x
= 0 end
228 if ld
~= "y" then vc
.y
= 0 end
229 if ld
~= "z" then vc
.z
= 0 end
231 -- Final tweaks to the teleporting pos, based on direction
232 -- Impact from the side
236 -- Special case: top or bottom of node
238 -- We need more space when impact is from below
245 -- If node was not walkable, no modification to pos is made.
247 -- Final teleportation position
248 local telepos
= vector
.add(pos
, dir
)
249 local telenode
= minetest
.get_node(telepos
)
251 --[[ It may be possible that telepos is walkable due to the algorithm.
252 Especially when the ender pearl is faster horizontally than vertical.
253 This applies final fixing, just to be sure we're not in a walkable node ]]
254 if not minetest
.registered_nodes
[telenode
.name
] or minetest
.registered_nodes
[telenode
.name
].walkable
then
256 telepos
.y
= telepos
.y
+ 0.5
258 telepos
.y
= telepos
.y
- 2.3
262 local oldpos
= player
:get_pos()
263 -- Teleport and hurt player
264 player
:setpos(telepos
)
265 player
:set_hp(player
:get_hp() - 5)
267 -- 5% chance to spawn endermite at the player's origin
268 local r
= math
.random(1,20)
270 minetest
.add_entity(oldpos
, "mobs_mc:endermite")
278 self
._lastpos
={x
=pos
.x
, y
=pos
.y
, z
=pos
.z
} -- Set lastpos-->Node will be added at last pos outside the node
281 snowball_ENTITY
.on_step
= snowball_on_step
282 egg_ENTITY
.on_step
= egg_on_step
283 pearl_ENTITY
.on_step
= pearl_on_step
285 minetest
.register_entity("mcl_throwing:snowball_entity", snowball_ENTITY
)
286 minetest
.register_entity("mcl_throwing:egg_entity", egg_ENTITY
)
287 minetest
.register_entity("mcl_throwing:ender_pearl_entity", pearl_ENTITY
)
289 local how_to_throw
= "Hold it in your and and leftclick to throw."
292 minetest
.register_craftitem("mcl_throwing:snowball", {
293 description
= "Snowball",
294 _doc_items_longdesc
= "Snowballs can be thrown or launched from a dispenser for fun. Hitting something with a snowball does nothing.",
295 _doc_items_usagehelp
= how_to_throw
,
296 inventory_image
= "mcl_throwing_snowball.png",
298 on_use
= throw_function("mcl_throwing:snowball_entity"),
299 _on_dispense
= dispense_function
,
303 minetest
.register_craftitem("mcl_throwing:egg", {
305 _doc_items_longdesc
= "Eggs can be thrown or launched from a dispenser and breaks on impact. There is a small chance that 1 or even 4 chickens will pop out of the egg when it hits the ground.",
306 _doc_items_usagehelp
= how_to_throw
,
307 inventory_image
= "mcl_throwing_egg.png",
309 on_use
= throw_function("mcl_throwing:egg_entity"),
310 _on_dispense
= dispense_function
,
311 groups
= { craftitem
= 1 },
315 minetest
.register_craftitem("mcl_throwing:ender_pearl", {
316 description
= "Ender Pearl",
317 _doc_items_longdesc
= "An ender pearl is an item which can be used for teleportation at the cost of health. It can be thrown and teleport the thrower to its impact location when it hits a solid block, a plant or vines. Each teleportation hurts the user by 5 hit points.",
318 _doc_items_usagehelp
= how_to_throw
,
319 wield_image
= "mcl_throwing_ender_pearl.png",
320 inventory_image
= "mcl_throwing_ender_pearl.png",
322 on_use
= throw_function("mcl_throwing:ender_pearl_entity"),