1 --this is where mobs are defined
6 local path = minetest.get_modpath(minetest.get_current_modname())
8 dofile(path.."/spawning.lua")
9 dofile(path.."/items.lua")
12 --these are helpers to create entities
14 mob.initial_properties = {
17 collide_with_objects = false,
18 collisionbox = {-0.37, -0.37, -0.37, 0.37, 0.865, 0.37},
20 visual_size = {x = 3, y = 3},
23 "body.png","leg.png","leg.png","leg.png","leg.png"
27 automatic_face_movement_dir = 0.0,
28 automatic_face_movement_max_rotation_per_sec = 600,
36 mob.get_staticdata = function(self)
37 return minetest.serialize({
43 mob.on_activate = function(self, staticdata, dtime_s)
44 self.object:set_armor_groups({immortal = 1})
45 --self.object:set_velocity({x = math.random(-5,5), y = 5, z = math.random(-5,5)})
46 self.object:set_acceleration({x = 0, y = -9.81, z = 0})
47 if string.sub(staticdata, 1, string.len("return")) == "return" then
48 local data = minetest.deserialize(staticdata)
49 if data and type(data) == "table" then
50 --self.range = data.range
54 self.object:set_animation({x=5,y=15}, 1, 0, true)
55 self.object:set_hp(self.hp)
56 self.direction = vector.new(math.random()*math.random(-1,1),0,math.random()*math.random(-1,1))
59 local head = minetest.add_entity(self.object:get_pos(), "mob:head")
62 self.child:get_luaentity().parent = self.object
63 self.child:set_attach(self.object, "", vector.new(2.4,1.2,0), vector.new(180,0,180))
64 self.head_rotation = vector.new(180,180,90)
65 self.child:set_animation({x=90,y=90}, 15, 0, true)
68 --self.object:set_yaw(math.pi*math.random(-1,1)*math.random())
70 mob.on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir)
71 local hurt = tool_capabilities.damage_groups.fleshy
75 local hp = self.object:get_hp()
76 self.object:set_hp(hp-hurt)
78 minetest.sound_play("hurt", {object=self.object, gain = 1.0, max_hear_distance = 60,pitch = math.random(80,100)/100})
82 mob.on_death = function(self, killer)
83 local pos = self.object:getpos()
85 minetest.sound_play("mob_die", {pos = pos, gain = 1.0})
86 minetest.add_particlespawner({
91 minvel = vector.new(-5,-5,-5),
92 maxvel = vector.new(5,5,5),
93 minacc = {x=0, y=0, z=0},
94 maxacc = {x=0, y=0, z=0},
99 collisiondetection = false,
101 texture = "smoke.png",
103 local obj = minetest.add_item(pos,"mob:raw_porkchop")
104 self.child:get_luaentity().parent = nil
107 mob.push = function(self)
108 local pos = self.object:getpos()
110 for _,object in ipairs(minetest.get_objects_inside_radius(pos, radius)) do
111 if object:is_player() or object:get_luaentity().mob == true then
112 local player_pos = object:getpos()
116 local currentvel = self.object:getvelocity()
117 local vel = vector.subtract(pos, player_pos)
118 vel = vector.normalize(vel)
119 local distance = vector.distance(pos,player_pos)
120 distance = (radius-distance)*10
121 vel = vector.multiply(vel,distance)
122 local acceleration = vector.new(vel.x-currentvel.x,0,vel.z-currentvel.z)
125 self.object:add_velocity(acceleration)
127 acceleration = vector.multiply(acceleration, 5)
128 object:add_player_velocity(acceleration)
132 --This makes the mob walk at a certain speed and jump
133 mob.move = function(self,dtime)
134 self.timer = self.timer - dtime
135 if self.timer <= 0 then
136 self.timer = math.random(1,3)
137 self.direction = vector.new(math.random()*math.random(-1,1),0,math.random()*math.random(-1,1))
138 --local yaw = self.object:get_yaw() + dtime
140 --self.object:set_yaw(yaw)
143 local pos1 = self.object:getpos()
144 pos1.y = pos1.y + 0.37
145 local currentvel = self.object:getvelocity()
146 local goal = vector.multiply(self.direction,1)
147 local acceleration = vector.new(goal.x-currentvel.x,0,goal.z-currentvel.z)
148 acceleration = vector.multiply(acceleration, 0.05)
149 self.object:add_velocity(acceleration)
152 if currentvel.y <= 0 then
153 local in_front = minetest.raycast(pos1, vector.add(pos1,vector.multiply(self.direction,3)), false, false):next()
154 local below = minetest.raycast(pos1, vector.add(pos1, vector.new(0,-0.02,0)), false, false):next()
156 in_front = minetest.registered_nodes[minetest.get_node(in_front.under).name].walkable
159 below = minetest.registered_nodes[minetest.get_node(below.under).name].walkable
162 if in_front and below then
163 self.object:add_velocity(vector.new(0,5,0))
168 mob.swim = function(self)
169 local pos = self.object:getpos()
171 local node = minetest.get_node(pos).name
172 local vel = self.object:getvelocity()
174 local acceleration = vector.new(0,goal-vel.y,0)
175 self.swimming = false
177 if node == "main:water" or node =="main:waterflow" then
179 self.object:add_velocity(acceleration)
182 --sets the mob animation and speed
183 mob.set_animation = function(self)
184 local distance = vector.distance(vector.new(0,0,0), self.object:getvelocity())
185 self.object:set_animation_frame_speed(distance*5)
188 --converts yaw to degrees
189 local degrees = function(yaw)
191 return(yaw*180.0/math.pi)
194 local degree_round = function(degree)
195 return(degree + 0.5 - (degree + 0.5) % 1)
198 local radians_to_degrees = function(radians)
199 return(radians*180.0/math.pi)
202 --a movement test to move the head
203 mob.move_head = function(self,pos2)
205 --print(self.head_rotation.y)
206 --if passed a direction to look
208 local pos = self.object:get_pos()
209 local body_yaw = self.object:get_yaw() - (math.pi/2)
210 local dir = vector.multiply(minetest.yaw_to_dir(body_yaw),0.72)
211 local real_dir = minetest.yaw_to_dir(body_yaw)
212 local body_yaw = degree_round(degrees(minetest.dir_to_yaw(dir)))
214 --pos is where the head actually is
215 pos = vector.add(pos,dir)
219 local head_yaw = degree_round(degrees(minetest.dir_to_yaw(vector.direction(pos,pos2))))
221 local new_yaw = (body_yaw-head_yaw)
226 --print(self.head_rotation.y)
227 if math.abs(new_yaw) <= 90 or math.abs(new_yaw) >= 270 then
228 --do other calculations on pitch and roll
230 local triangle = vector.new(vector.distance(pos,pos2),0,pos2.y-pos.y)
232 local tri_yaw = minetest.dir_to_yaw(triangle)+(math.pi/2)
234 pitch = radians_to_degrees(tri_yaw)
236 pitch = math.floor(pitch+90 + 0.5)
239 local goal_yaw = 180-new_yaw
242 goal_yaw = goal_yaw + 360
245 if goal_yaw > 360 then
246 goal_yaw = goal_yaw - 360
249 local current_yaw = self.head_rotation.y
251 if goal_yaw > current_yaw then
252 current_yaw = current_yaw + 4
253 elseif goal_yaw < current_yaw then
254 current_yaw = current_yaw - 4
260 if math.abs(math.abs(goal_yaw) - math.abs(current_yaw)) <= 4 then
262 --print(math.abs(goal_yaw) - math.abs(current_yaw))
263 current_yaw = goal_yaw
265 --print(" NOT SKIPPING")
266 --print(math.abs(goal_yaw) - math.abs(current_yaw))
270 local goal_pitch = pitch
272 local current_pitch = self.head_rotation.z
274 if goal_pitch > current_pitch then
275 current_pitch = current_pitch + 1
276 elseif goal_pitch < current_pitch then
277 current_pitch = current_pitch - 1
280 self.child:set_attach(self.object, "", vector.new(2.4,1.2,0), vector.new(180, current_yaw, 180))
281 self.child:set_animation({x=current_pitch,y=current_pitch}, 15, 0, true)
282 self.head_rotation = vector.new(180, current_yaw, current_pitch)
285 self.return_head_to_origin(self)
289 --if nothing to look at
291 --print("not looking")
292 self.return_head_to_origin(self)
296 --this sets the mob to move it's head back to pointing forwards
297 mob.return_head_to_origin = function(self)
298 --print("setting back to origin")
299 local rotation = self.head_rotation
301 --make the head yaw move back twice as fast
302 if rotation.y > 180 then
303 if rotation.y > 360 then
304 rotation.y = rotation.y - 360
306 rotation.y = rotation.y - 2
307 elseif rotation.y < 180 then
308 if rotation.y < 0 then
309 rotation.y = rotation.y + 360
311 rotation.y = rotation.y + 2
314 if math.abs(rotation.y)+1 == 180 then
317 --move up down (pitch) back to center
318 if rotation.z > 90 then
319 rotation.z = rotation.z - 1
320 elseif rotation.z < 90 then
321 rotation.z = rotation.z + 1
325 rotation.z = math.floor(rotation.z + 0.5)
326 rotation.y = math.floor(rotation.y + 0.5)
328 self.child:set_attach(self.object, "", vector.new(2.4,1.2,0), vector.new(180, rotation.y, 180))
329 self.child:set_animation({x=rotation.z,y=rotation.z}, 15, 0, true)
330 self.head_rotation = rotation
333 mob.look_around = function(self)
334 local pos = self.object:get_pos()
336 local player_found = false
337 for _,object in ipairs(minetest.get_objects_inside_radius(pos, 6)) do
338 if object:is_player() and player_found == false then
341 --look at player's camera
342 local pos2 = object:get_pos()
343 pos2.y = pos2.y + 1.625
344 self.move_head(self,pos2)
347 --stare straight if not found
348 if player_found == false then
349 self.move_head(self,nil)
353 mob.on_step = function(self, dtime)
354 self.move(self,dtime)
355 self.set_animation(self)
356 self.look_around(self)
359 minetest.register_entity("mob:pig", mob)
363 head.initial_properties = {
366 collide_with_objects = false,
367 collisionbox = {0, 0, 0, 0, 0, 0},
369 visual_size = {x = 1.1, y = 1.1},
372 "head.png","nose.png"
376 --automatic_face_movement_dir = 0.0,
377 --automatic_face_movement_max_rotation_per_sec = 600,
380 --remove the head if no body
381 head.on_step = function(self, dtime)
382 if self.parent == nil then
386 minetest.register_entity("mob:head", head)