Replace getpos() with get_pos()
[MineClone/MineClone2.git] / mods / ENTITIES / mobs_mc / enderman.lua
blob06998175c55427f707ea79c314caed959f39d3d0
1 --MCmobs v0.4
2 --maikerumine
3 --made for MC like Survival game
4 --License for code WTFPL and otherwise stated in readmes
6 -- ENDERMAN BEHAVIOUR:
7 -- In this game, endermen attack the player on sight, like other monsters do.
8 -- However, they have a reduced viewing range to make them less dangerous.
9 -- This differs from MC, in which endermen only become hostile when provoked,
10 -- and they are provoked by looking directly at them.
11 -- TODO: Implement MC behaviour.
13 -- intllib
14 local MP = minetest.get_modpath(minetest.get_current_modname())
15 local S, NS = dofile(MP.."/intllib.lua")
17 --dofile(minetest.get_modpath("mobs").."/api.lua")
20 --###################
21 --################### ENDERMAN
22 --###################
24 local pr = PseudoRandom(os.time()*(-334))
26 -- How freqeuntly to take and place blocks, in seconds
27 local take_frequency_min = 25
28 local take_frequency_max = 90
29 local place_frequency_min = 10
30 local place_frequency_max = 30
32 -- Create the textures table for the enderman, depending on which kind of block
33 -- the enderman holds (if any).
34 local create_enderman_textures = function(block_type, itemstring)
35 local base = "mobs_mc_enderman.png^mobs_mc_enderman_eyes.png"
37 --[[ Order of the textures in the texture table:
38 Flower, 90 degrees
39 Flower, 45 degrees
40 Held block, backside
41 Held block, bottom
42 Held block, front
43 Held block, left
44 Held block, right
45 Held block, top
46 Enderman texture (base)
48 -- Regular cube
49 if block_type == "cube" then
50 local tiles = minetest.registered_nodes[itemstring].tiles
51 local textures = {}
52 local last
53 if mobs_mc.enderman_block_texture_overrides[itemstring] then
54 -- Texture override available? Use these instead!
55 textures = mobs_mc.enderman_block_texture_overrides[itemstring]
56 else
57 -- Extract the texture names
58 for i = 1, 6 do
59 if type(tiles[i]) == "string" then
60 last = tiles[i]
61 elseif type(tiles[i]) == "table" then
62 if tiles[i].name then
63 last = tiles[i].name
64 end
65 end
66 table.insert(textures, last)
67 end
68 end
69 return {
70 "blank.png",
71 "blank.png",
72 textures[5],
73 textures[2],
74 textures[6],
75 textures[3],
76 textures[4],
77 textures[1],
78 base, -- Enderman texture
80 -- Node of plantlike drawtype, 45° (recommended)
81 elseif block_type == "plantlike45" then
82 local textures = minetest.registered_nodes[itemstring].tiles
83 return {
84 "blank.png",
85 textures[1],
86 "blank.png",
87 "blank.png",
88 "blank.png",
89 "blank.png",
90 "blank.png",
91 "blank.png",
92 base,
94 -- Node of plantlike drawtype, 90°
95 elseif block_type == "plantlike90" then
96 local textures = minetest.registered_nodes[itemstring].tiles
97 return {
98 textures[1],
99 "blank.png",
100 "blank.png",
101 "blank.png",
102 "blank.png",
103 "blank.png",
104 "blank.png",
105 "blank.png",
106 base,
108 elseif block_type == "unknown" then
109 return {
110 "blank.png",
111 "blank.png",
112 "unknown_node.png",
113 "unknown_node.png",
114 "unknown_node.png",
115 "unknown_node.png",
116 "unknown_node.png",
117 "unknown_node.png",
118 base, -- Enderman texture
120 -- No block held (for initial texture)
121 elseif block_type == "nothing" or block_type == nil then
122 return {
123 "blank.png",
124 "blank.png",
125 "blank.png",
126 "blank.png",
127 "blank.png",
128 "blank.png",
129 "blank.png",
130 "blank.png",
131 base, -- Enderman texture
136 -- Select a new animation definition.
137 local select_enderman_animation = function(animation_type)
138 -- Enderman holds a block
139 if animation_type == "block" then
140 return {
141 walk_speed = 25,
142 run_speed = 50,
143 stand_speed = 25,
144 stand_start = 200,
145 stand_end = 200,
146 walk_start = 161,
147 walk_end = 200,
148 run_start = 161,
149 run_end = 200,
150 punch_start = 121,
151 punch_end = 160,
153 -- Enderman doesn't hold a block
154 elseif animation_type == "normal" or animation_type == nil then
155 return {
156 walk_speed = 25,
157 run_speed = 50,
158 stand_speed = 25,
159 stand_start = 40,
160 stand_end = 80,
161 walk_start = 0,
162 walk_end = 40,
163 run_start = 0,
164 run_end = 40,
165 punch_start = 81,
166 punch_end = 120,
171 local mobs_griefing = minetest.settings:get_bool("mobs_griefing") ~= false
173 mobs:register_mob("mobs_mc:enderman", {
174 -- TODO: Endermen should be classified as passive
175 type = "monster",
176 passive = false,
177 pathfinding = 1,
178 stepheight = 1.2,
179 hp_min = 40,
180 hp_max = 40,
181 collisionbox = {-0.3, -0.01, -0.3, 0.3, 2.89, 0.3},
182 visual = "mesh",
183 mesh = "mobs_mc_enderman.b3d",
184 textures = create_enderman_textures(),
185 visual_size = {x=3, y=3},
186 makes_footstep_sound = true,
187 sounds = {
188 war_cry = "mobs_sandmonster",
189 death = "green_slime_death",
190 -- TODO: damage, random
191 distance = 16,
193 walk_velocity = 0.2,
194 run_velocity = 3.4,
195 damage = 7,
196 reach = 2,
197 drops = {
198 {name = mobs_mc.items.ender_pearl,
199 chance = 1,
200 min = 0,
201 max = 1,},
203 animation = select_enderman_animation("normal"),
204 _taken_node = "",
205 do_custom = function(self, dtime)
206 if not mobs_griefing then
207 return
209 -- Take and put nodes
210 if not self._take_place_timer or not self._next_take_place_time then
211 self._take_place_timer = 0
212 self._next_take_place_time = math.random(take_frequency_min, take_frequency_max)
213 return
215 self._take_place_timer = self._take_place_timer + dtime
216 if (self._taken_node == nil or self._taken_node == "") and self._take_place_timer >= self._next_take_place_time then
217 -- Take random node
218 self._take_place_timer = 0
219 self._next_take_place_time = math.random(place_frequency_min, place_frequency_max)
220 local pos = self.object:get_pos()
221 local takable_nodes = minetest.find_nodes_in_area({x=pos.x-2, y=pos.y-1, z=pos.z-2}, {x=pos.x+2, y=pos.y+1, z=pos.z+2}, mobs_mc.enderman_takable)
222 if #takable_nodes >= 1 then
223 local r = pr:next(1, #takable_nodes)
224 local take_pos = takable_nodes[r]
225 local node = minetest.get_node(take_pos)
226 local dug = minetest.dig_node(take_pos)
227 if dug then
228 if mobs_mc.enderman_replace_on_take[node.name] then
229 self._taken_node = mobs_mc.enderman_replace_on_take[node.name]
230 else
231 self._taken_node = node.name
233 local def = minetest.registered_nodes[self._taken_node]
234 -- Update animation and texture accordingly (adds visibly carried block)
235 local block_type
236 -- Cube-shaped
237 if def.drawtype == "normal" or
238 def.drawtype == "nodebox" or
239 def.drawtype == "liquid" or
240 def.drawtype == "flowingliquid" or
241 def.drawtype == "glasslike" or
242 def.drawtype == "glasslike_framed" or
243 def.drawtype == "glasslike_framed_optional" or
244 def.drawtype == "allfaces" or
245 def.drawtype == "allfaces_optional" or
246 def.drawtype == nil then
247 block_type = "cube"
248 elseif def.drawtype == "plantlike" then
249 -- Flowers and stuff
250 block_type = "plantlike45"
251 elseif def.drawtype == "airlike" then
252 -- Just air
253 block_type = nil
254 else
255 -- Fallback for complex drawtypes
256 block_type = "unknown"
258 self.base_texture = create_enderman_textures(block_type, self._taken_node)
259 self.object:set_properties({ textures = self.base_texture })
260 self.animation = select_enderman_animation("block")
261 mobs:set_animation(self, self.animation.current)
262 if def.sounds and def.sounds.dug then
263 minetest.sound_play(def.sounds.dug, {pos = take_pos, max_hear_distance = 16})
267 elseif self._taken_node ~= nil and self._taken_node ~= "" and self._take_place_timer >= self._next_take_place_time then
268 -- Place taken node
269 self._take_place_timer = 0
270 self._next_take_place_time = math.random(take_frequency_min, take_frequency_max)
271 local pos = self.object:get_pos()
272 local yaw = self.object:get_yaw()
273 -- Place node at looking direction
274 local place_pos = vector.subtract(pos, minetest.facedir_to_dir(minetest.dir_to_facedir(minetest.yaw_to_dir(yaw))))
275 if minetest.get_node(place_pos).name == "air" then
276 -- ... but only if there's a free space
277 local success = minetest.place_node(place_pos, {name = self._taken_node})
278 if success then
279 local def = minetest.registered_nodes[self._taken_node]
280 -- Update animation accordingly (removes visible block)
281 self.animation = select_enderman_animation("normal")
282 mobs:set_animation(self, self.animation.current)
283 if def.sounds and def.sounds.place then
284 minetest.sound_play(def.sounds.place, {pos = place_pos, max_hear_distance = 16})
286 self._taken_node = ""
290 end,
291 -- TODO: Teleport enderman on damage, etc.
292 _do_teleport = function(self)
293 -- Attempt to randomly teleport enderman
294 local pos = self.object:get_pos()
295 -- Find all solid nodes below air in a 65×65×65 cuboid centered on the enderman
296 local nodes = minetest.find_nodes_in_area_under_air(vector.subtract(pos, 32), vector.add(pos, 32), {"group:solid", "group:cracky", "group:crumbly"})
297 local telepos
298 if #nodes > 0 then
299 -- Up to 64 attempts to teleport
300 for n=1, math.min(64, #nodes) do
301 local r = pr:next(1, #nodes)
302 local nodepos = nodes[r]
303 local node_ok = true
304 -- Selected node needs to have 3 nodes of free space above
305 for u=1, 3 do
306 local node = minetest.get_node({x=nodepos.x, y=nodepos.y+u, z=nodepos.z})
307 if minetest.registered_nodes[node.name].walkable then
308 node_ok = false
309 break
312 if node_ok then
313 telepos = {x=nodepos.x, y=nodepos.y+1, z=nodepos.z}
316 if telepos then
317 self.object:setpos(telepos)
320 end,
321 on_die = function(self, pos)
322 -- Drop carried node on death
323 if self._taken_node ~= nil and self._taken_node ~= "" then
324 minetest.add_item(pos, self._taken_node)
326 end,
327 water_damage = 8,
328 lava_damage = 4,
329 light_damage = 0,
330 -- TODO: Increase view range when it detects being seen
331 -- Low view range to emulate that behaviour somehow
332 view_range = 4,
333 fear_height = 4,
334 attack_type = "dogfight",
335 blood_amount = 0,
339 -- End spawn
340 mobs:spawn_specific("mobs_mc:enderman", mobs_mc.spawn.solid, {"air"}, 0, minetest.LIGHT_MAX+1, 30, 3000, 12, mobs_mc.spawn_height.end_min, mobs_mc.spawn_height.end_max)
341 -- Overworld spawn
342 mobs:spawn_specific("mobs_mc:enderman", mobs_mc.spawn.solid, {"air"}, 0, 7, 30, 19000, 2, mobs_mc.spawn_height.overworld_min, mobs_mc.spawn_height.overworld_max)
343 -- Nether spawn (rare)
344 mobs:spawn_specific("mobs_mc:enderman", mobs_mc.spawn.solid, {"air"}, 0, 7, 30, 27500, 4, mobs_mc.spawn_height.nether_min, mobs_mc.spawn_height.nether_max)
346 -- spawn eggs
347 mobs:register_egg("mobs_mc:enderman", S("Enderman"), "mobs_mc_spawn_icon_enderman.png", 0)
349 if minetest.settings:get_bool("log_mods") then
351 minetest.log("action", "MC Enderman loaded")