]> git.lizzy.rs Git - Crafter.git/blob - mods/mob/init.lua
775e344697b49ff53c5e101592ab63daf179fde9
[Crafter.git] / mods / mob / init.lua
1 --this is where mobs are defined
2
3 global_mob_table = {}
4
5
6 local path = minetest.get_modpath(minetest.get_current_modname())
7
8 dofile(path.."/spawning.lua")
9 dofile(path.."/items.lua")
10
11
12 --these are helpers to create entities
13 local mob = {}
14 mob.initial_properties = {
15         hp_max = 1,
16         physical = true,
17         collide_with_objects = false,
18         collisionbox = {-0.37, -0.37, -0.37, 0.37, 0.865, 0.37},
19         visual = "mesh",
20         visual_size = {x = 3, y = 3},
21         mesh = "pig.x",
22         textures = {
23                 "body.png","leg.png","leg.png","leg.png","leg.png"
24         },
25         is_visible = true,
26         pointable = true,
27         automatic_face_movement_dir = 0.0,
28         automatic_face_movement_max_rotation_per_sec = 600,
29 }
30 mob.hp = 5
31 mob.mob = true
32 mob.hostile = false
33 mob.timer = 0
34
35
36 mob.get_staticdata = function(self)
37         return minetest.serialize({
38                 --range = self.range,
39                 hp = self.hp,           
40         })
41 end
42
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
51                         self.hp = data.hp
52                 end
53         end
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))
57         
58         --set the head up
59         local head = minetest.add_entity(self.object:get_pos(), "mob:head")
60         if head then
61                 self.child = 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)
66         end
67         
68         --self.object:set_yaw(math.pi*math.random(-1,1)*math.random())
69 end
70 mob.on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir)            
71         local hurt = tool_capabilities.damage_groups.fleshy
72         if not hurt then
73                 hurt = 1
74         end
75         local hp = self.object:get_hp()
76         self.object:set_hp(hp-hurt)
77         if hp > 1 then
78                 minetest.sound_play("hurt", {object=self.object, gain = 1.0, max_hear_distance = 60,pitch = math.random(80,100)/100})
79         end
80         self.hp = hp-hurt
81 end
82 mob.on_death = function(self, killer)
83         local pos = self.object:getpos()
84         pos.y = pos.y + 0.4
85         minetest.sound_play("mob_die", {pos = pos, gain = 1.0})
86         minetest.add_particlespawner({
87                 amount = 40,
88                 time = 0.001,
89                 minpos = pos,
90                 maxpos = pos,
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},
95                 minexptime = 1.1,
96                 maxexptime = 1.5,
97                 minsize = 1,
98                 maxsize = 2,
99                 collisiondetection = false,
100                 vertical = false,
101                 texture = "smoke.png",
102         })
103         local obj = minetest.add_item(pos,"mob:raw_porkchop")
104         self.child:get_luaentity().parent = nil
105 end
106 --repel from players
107 mob.push = function(self)
108         local pos = self.object:getpos()
109         local radius = 1
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()
113                         pos.y = 0
114                         player_pos.y = 0
115                         
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)
123                         
124                         
125                         self.object:add_velocity(acceleration)
126                         
127                         acceleration = vector.multiply(acceleration, 5)
128                         object:add_player_velocity(acceleration)
129                 end
130         end
131 end
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
139                 
140                 --self.object:set_yaw(yaw)
141         end
142         
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)
150
151         --try to jump
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()
155                 if in_front then
156                         in_front = minetest.registered_nodes[minetest.get_node(in_front.under).name].walkable
157                 end
158                 if below then
159                         below = minetest.registered_nodes[minetest.get_node(below.under).name].walkable
160                 end
161                 
162                 if in_front and below then
163                         self.object:add_velocity(vector.new(0,5,0))
164                 end
165         end
166 end
167 --makes the mob swim
168 mob.swim = function(self)
169         local pos = self.object:getpos()
170         pos.y = pos.y + 0.7
171         local node = minetest.get_node(pos).name
172         local vel = self.object:getvelocity()
173         local goal = 3
174         local acceleration = vector.new(0,goal-vel.y,0)
175         self.swimming = false
176         
177         if node == "main:water" or node =="main:waterflow" then
178                 self.swimming = true
179                 self.object:add_velocity(acceleration)
180         end
181 end
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)
186 end
187
188 --converts yaw to degrees
189 local degrees = function(yaw)
190         yaw = yaw + math.pi
191         return(yaw*180.0/math.pi)
192 end
193
194 local degree_round = function(degree)
195         return(degree + 0.5 - (degree + 0.5) % 1)
196 end
197
198 local radians_to_degrees = function(radians)
199         return(radians*180.0/math.pi)
200 end
201
202 --a movement test to move the head
203 mob.move_head = function(self,pos2)
204         if self.child then
205                 --print(self.head_rotation.y)
206                 --if passed a direction to look
207                 if pos2 then
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)))
213                         
214                         --pos is where the head actually is
215                         pos = vector.add(pos,dir)
216                         pos.y = pos.y + 0.36
217                                         
218                         
219                         local head_yaw  = degree_round(degrees(minetest.dir_to_yaw(vector.direction(pos,pos2))))                        
220                         
221                         local new_yaw = (body_yaw-head_yaw)
222
223                         local pitch = 0 
224                         local roll = 0
225                         
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
229                                 
230                                 local triangle = vector.new(vector.distance(pos,pos2),0,pos2.y-pos.y)
231                                 
232                                 local tri_yaw = minetest.dir_to_yaw(triangle)+(math.pi/2)
233                                 
234                                 pitch = radians_to_degrees(tri_yaw)
235                                 
236                                 pitch = math.floor(pitch+90 + 0.5)
237                                 
238                                 
239                                 local goal_yaw = 180-new_yaw
240                                 
241                                 if goal_yaw < 0 then
242                                         goal_yaw = goal_yaw + 360
243                                 end
244                                 
245                                 if goal_yaw > 360 then
246                                         goal_yaw = goal_yaw - 360
247                                 end
248                                 
249                                 local current_yaw = self.head_rotation.y
250                                 
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
255                                 end
256                                 
257                                 --print(current_yaw)
258                                 
259                                 --stop jittering
260                                 if math.abs(math.abs(goal_yaw) - math.abs(current_yaw)) <= 4 then
261                                         --print("skipping:")
262                                         --print(math.abs(goal_yaw) - math.abs(current_yaw))
263                                         current_yaw = goal_yaw
264                                 else
265                                         --print(" NOT SKIPPING")
266                                         --print(math.abs(goal_yaw) - math.abs(current_yaw))
267                                 end
268                                 
269                                 
270                                 local goal_pitch = pitch
271                                 
272                                 local current_pitch = self.head_rotation.z
273                                 
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
278                                 end
279                                 
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)
283                         --nothing to look at
284                         else
285                                 self.return_head_to_origin(self)
286                         end
287                         --                                                                      roll        newyaw      pitch
288                         
289                 --if nothing to look at
290                 else
291                         --print("not looking")
292                         self.return_head_to_origin(self)
293                 end
294         end
295 end
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
300         
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
305                 end
306                 rotation.y = rotation.y - 2
307         elseif rotation.y < 180 then
308                 if rotation.y < 0 then
309                         rotation.y = rotation.y + 360
310                 end
311                 rotation.y = rotation.y + 2
312         end
313         --finish rotation
314         if math.abs(rotation.y)+1 == 180 then
315                 rotation.y = 180
316         end
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
322         end
323         
324         
325         rotation.z = math.floor(rotation.z + 0.5)
326         rotation.y = math.floor(rotation.y + 0.5)
327         --print(rotation.y)
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
331 end
332
333 mob.look_around = function(self)
334         local pos = self.object:get_pos()
335         --STARE O_O
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
339                         --print("test")
340                         player_found = true
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)
345                 end
346         end
347         --stare straight if not found
348         if player_found == false then
349                 self.move_head(self,nil)
350         end
351 end
352
353 mob.on_step = function(self, dtime)
354         self.move(self,dtime)
355         self.set_animation(self)
356         self.look_around(self)
357 end
358
359 minetest.register_entity("mob:pig", mob)
360
361
362 local head = {}
363 head.initial_properties = {
364         hp_max = 1,
365         physical = false,
366         collide_with_objects = false,
367         collisionbox = {0, 0, 0, 0, 0, 0},
368         visual = "mesh",
369         visual_size = {x = 1.1, y = 1.1},
370         mesh = "pig_head.x",
371         textures = {
372                 "head.png","nose.png"
373         },
374         is_visible = true,
375         pointable = false,
376         --automatic_face_movement_dir = 0.0,
377         --automatic_face_movement_max_rotation_per_sec = 600,
378 }
379
380 --remove the head if no body
381 head.on_step = function(self, dtime)
382         if self.parent == nil then
383                 self.object:remove()
384         end
385 end
386 minetest.register_entity("mob:head", head)