Replace getpos() with get_pos()
[MineClone/MineClone2.git] / mods / ITEMS / mcl_throwing / init.lua
blob731e9ce83f465d7314fe620816762919f88d0556
1 mcl_throwing = {}
3 --
4 -- Snowballs and other throwable items
5 --
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",
15 local velocities = {
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]
24 end
25 if velocity == nil then
26 velocity = 22
27 end
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})
33 return obj
34 end
36 -- Throw item
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
44 item:take_item()
45 end
46 return item
47 end
48 return func
49 end
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)
55 end
57 -- Staticdata handling because objects may want to be reloaded
58 local get_staticdata = function(self)
59 local data = {
60 _lastpos = self._lastpos,
61 _thrower = self._thrower,
63 return minetest.serialize(data)
64 end
66 local on_activate = function(self, staticdata, dtime_s)
67 local data = minetest.deserialize(staticdata)
68 if data then
69 self._lastpos = data._lastpos
70 self._thrower = data._thrower
71 end
72 end
74 -- The snowball entity
75 local snowball_ENTITY={
76 physical = false,
77 timer=0,
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,
85 _lastpos={},
87 local egg_ENTITY={
88 physical = false,
89 timer=0,
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,
97 _lastpos={},
99 -- Ender pearl entity
100 local pearl_ENTITY={
101 physical = false,
102 timer=0,
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,
110 _lastpos={},
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
124 self.object:remove()
125 return
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 },
150 collisionbox = {
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,
159 ent.child = true
161 if r == 1 then
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)
166 if r == 1 then
167 local offsets = {
168 { x=0.7, y=0, z=0 },
169 { x=-0.7, y=0, z=-0.7 },
170 { x=-0.7, y=0, z=0.7 },
172 for o=1, 3 do
173 local pos = vector.add(self._lastpos, offsets[o])
174 make_child(minetest.add_entity(pos, "mobs_mc:chicken"))
178 self.object:remove()
179 return
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)
191 local nn = node.name
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
201 self.object:remove()
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)
205 if player then
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()
212 if walkable then
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
219 local lv, ld
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
233 dir.x = vc.x * -1
234 dir.z = vc.z * -1
236 -- Special case: top or bottom of node
237 if vc.y > 0 then
238 -- We need more space when impact is from below
239 dir.y = -2.3
240 elseif vc.y < 0 then
241 -- Standing on top
242 dir.y = 0.5
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
255 if v.y < 0 then
256 telepos.y = telepos.y + 0.5
257 else
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)
269 if r == 1 then
270 minetest.add_entity(oldpos, "mobs_mc:endermite")
274 self.object:remove()
275 return
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."
291 -- Snowball
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",
297 stack_max = 16,
298 on_use = throw_function("mcl_throwing:snowball_entity"),
299 _on_dispense = dispense_function,
302 -- Egg
303 minetest.register_craftitem("mcl_throwing:egg", {
304 description = "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",
308 stack_max = 16,
309 on_use = throw_function("mcl_throwing:egg_entity"),
310 _on_dispense = dispense_function,
311 groups = { craftitem = 1 },
314 -- Ender Pearl
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",
321 stack_max = 16,
322 on_use = throw_function("mcl_throwing:ender_pearl_entity"),