]> git.lizzy.rs Git - Crafter.git/blob - mods/mob/api/movement.lua
Optimize a ton of globals to locals
[Crafter.git] / mods / mob / api / movement.lua
1 local minetest,math,vector = minetest,math,vector
2 --index all mods
3 local all_walkable_nodes = {}
4 minetest.register_on_mods_loaded(function()
5         for name in pairs(minetest.registered_nodes) do
6                 if name ~= "air" and name ~= "ignore" then
7                         if minetest.get_nodedef(name,"walkable") then
8                                 table.insert(all_walkable_nodes,name)
9                         end
10                 end
11         end
12 end)
13
14 --
15 mobs.create_movement_functions = function(def,mob_register)
16         --makes the mob swim
17         mob_register.swim = function(self,dtime)
18                 local pos = self.object:get_pos()
19                 pos.y = pos.y + 0.3
20                 local node = minetest.get_node(pos).name
21                 self.swimming = false
22                 if node == "main:water" or node =="main:waterflow" then
23                         local vel = self.object:get_velocity()
24                         local goal = 3
25                         local acceleration = vector.new(0,goal-vel.y,0)
26                         --jump out of the water
27                         if (vel.x == 0 and self.direction.x ~= 0) or (vel.z == 0 and self.direction.z ~= 0) then
28                                 self.object:set_velocity(vector.new(vel.x,5,vel.z))
29                         --else swim
30                         else
31                                 self.object:add_velocity(acceleration)
32                         end
33                         self.swimming = true
34                 end
35         end
36
37         local get_group = minetest.get_item_group
38         local get_node = minetest.get_node
39         mob_register.hurt_inside = function(self,dtime)
40                 if self.hp > 0 and self.hurt_inside_timer <= 0 then
41                         local pos = self.object:get_pos()
42                         local noder = get_node(pos).name
43                         local hurty = get_group(noder, "hurt_inside")
44                         if hurty > 0 then
45                                 self.object:punch(self.object, 2, 
46                                         {
47                                         full_punch_interval=1.5,
48                                         damage_groups = {damage=hurty},
49                                 })
50                         end
51                         local firey = get_group(noder, "fire")
52                         if not self.on_fire and firey > 0 then
53                                 start_fire(self.object)
54                         end
55                         self.hurt_inside_timer = 0.25
56                 else
57                         self.hurt_inside_timer = self.hurt_inside_timer - dtime
58                 end
59         end
60
61         --This makes the mob walk at a certain speed and jump
62         if def.movement_type == "walk" then
63                 mob_register.move = function(self,dtime,moveresult)
64                         self.manage_jump_timer(self,dtime)
65                         self.timer = self.timer - dtime
66
67                         --jump
68                         self.jump(self,moveresult)
69                         
70                         --swim
71                         self.swim(self,dtime)
72                         
73                         --print(self.timer)
74                         --direction state change
75                         if self.timer <= 0 and not self.following == true then
76                                 --print("changing direction")
77                                 self.timer = math.random(2,7)
78                                 self.direction = vector.new(math.random()*math.random(-1,1),0,math.random()*math.random(-1,1))
79                                 --local yaw = self.object:get_yaw() + dtime
80                                 self.speed = math.random(0,self.max_speed)
81                                 --self.object:set_yaw(yaw)
82                         end
83
84                         self.hurt_inside(self,dtime)
85
86                         local currentvel = self.object:get_velocity()
87                         local goal = vector.multiply(self.direction,self.speed)
88                         local acceleration = vector.new(goal.x-currentvel.x,0,goal.z-currentvel.z)
89                         if self.whip_turn then
90                                 acceleration = vector.multiply(acceleration, 0.5)
91
92                                 self.whip_turn = self.whip_turn - dtime
93                                 if self.whip_turn <= 0 then
94                                         self.whip_turn = nil
95                                 end
96                         else
97                                 acceleration = vector.multiply(acceleration, 0.05)
98                         end
99                         self.object:add_velocity(acceleration)
100                 end
101                 mob_register.jump = function(self,moveresult)
102                         if moveresult and moveresult.touching_ground and self.direction then
103                                 local pos = self.object:get_pos()
104                                 pos.y = pos.y+0.1
105
106                                 if self.path_data and table.getn(self.path_data) > 0 then
107                                         --smart jump
108                                         local y = math.floor(pos.y+0.5)
109                                         local vel = self.object:get_velocity()
110                                         if y < self.path_data[1].y then
111                                                 self.object:set_velocity(vector.new(vel.x,5,vel.z))
112                                         elseif self.path_data[2] and y < self.path_data[2].y then
113                                                 self.object:set_velocity(vector.new(vel.x,5,vel.z))
114                                         elseif self.path_data[3] and y < self.path_data[3].y then
115                                                 self.object:set_velocity(vector.new(vel.x,5,vel.z))
116                                         elseif ((vel.x == 0 and self.direction.x ~= 0) or (vel.z == 0 and self.direction.z ~= 0)) then
117                                                 self.object:set_velocity(vector.new(vel.x,5,vel.z))
118                                         end
119                                 else
120                                         --assume collisionbox is even x and z
121                                         local modifier = self.object:get_properties().collisionbox[4]*3
122                                         
123
124                                         local pos2 = vector.add(vector.multiply(self.direction,modifier),pos)
125
126                                         local ray = minetest.raycast(pos, pos2, false, false)
127                                         
128                                         local pointed_thing
129
130                                         if ray then
131                                                 pointed_thing = ray:next()
132                                         end
133                                                 
134                                         if pointed_thing then
135                                                 if minetest.get_nodedef(minetest.get_node(pointed_thing.under).name, "walkable") then
136                                                         --print("jump")
137                                                         local vel = self.object:get_velocity()
138                                                         --self.jump_timer = 1+math.random()
139                                                         self.object:set_velocity(vector.new(vel.x,5,vel.z))
140                                                 else
141                                                         --print("velocity check")
142                                                         local vel = self.object:get_velocity()
143                                                         if (vel.x == 0 and self.direction.x ~= 0) or (vel.z == 0 and self.direction.z ~= 0) then
144                                                                 self.object:set_velocity(vector.new(vel.x,5,vel.z))
145                                                         end
146                                                 end
147                                         else
148                                                 --print("velcheck 2")
149                                                 local vel = self.object:get_velocity()
150                                                 if (vel.x == 0 and self.direction.x ~= 0) or (vel.z == 0 and self.direction.z ~= 0) then
151                                                         self.object:set_velocity(vector.new(vel.x,5,vel.z))
152                                                 end
153                                         end
154                                 end
155                         end
156                 end
157         elseif def.movement_type == "jump" then
158                 mob_register.move = function(self,dtime,moveresult)
159                         self.manage_jump_timer(self,dtime)
160                         self.timer = self.timer - dtime
161                         
162                         --jump
163                         self.jump(self,moveresult)
164                         
165                         --swim
166                         self.swim(self,dtime)
167                         
168                         --direction state change
169                         if self.timer <= 0 and not self.following == true then
170                                 --print("changing direction")
171                                 self.timer = math.random(2,7)
172                                 self.direction = vector.new(math.random()*math.random(-1,1),0,math.random()*math.random(-1,1))
173                                 --local yaw = self.object:get_yaw() + dtime
174                                 self.speed = math.random(0,self.max_speed)
175                                 --self.object:set_yaw(yaw)
176                         end
177
178                         self.hurt_inside(self,dtime)    
179                         
180                         local currentvel = self.object:get_velocity()
181                         if currentvel.y ~= 0 then
182                                 local goal = vector.multiply(self.direction,self.speed)
183                                 local acceleration = vector.new(goal.x-currentvel.x,0,goal.z-currentvel.z)
184                                 acceleration = vector.multiply(acceleration, 0.05)
185                                 self.object:add_velocity(acceleration)
186                         end
187                 end
188                 
189                 mob_register.jump = function(self,moveresult)
190                         if moveresult and moveresult.touching_ground and self.direction then
191                                 if self.jump_timer <= 0 then
192                                         if self.make_jump_noise then
193                                                 minetest.sound_play("slime_splat", {object=self.object, gain = 1.0, max_hear_distance = 10,pitch = math.random(80,100)/100})
194                                         end
195                                         local vel = self.object:get_velocity()
196                                         self.object:set_velocity(vector.new(vel.x,5,vel.z))
197                                         if self.following == true then
198                                                 self.jump_timer = 0.5
199                                         else
200                                                 self.jump_timer = 1+math.random()
201                                         end
202                                 else
203                                         self.object:set_velocity(vector.new(0,0,0))
204                                 end
205                         end
206                 end
207         end
208         
209         if def.pathfinds then
210                 mob_register.pathfinding = function(self,dtime)
211                         local acute_pos = vector.floor(vector.add(self.object:get_pos(),0.5))
212                         if self.following and self.following_pos then
213                                 self.pathfinding_timer = self.pathfinding_timer + dtime
214                                 local height_diff
215                                 if self.object:get_pos().y > self.following_pos.y then
216                                         height_diff = math.abs(self.object:get_pos().y-self.following_pos.y)
217                                 elseif self.object:get_pos().y <= self.following_pos.y then
218                                         height_diff = math.abs(self.following_pos.y-self.object:get_pos().y)
219                                 end
220                                 --delete path if height too far
221                                 if self.path_data and height_diff > self.view_distance/2 then
222                                         self.path_data = nil
223                                         self.old_path_pos = nil
224                                         self.old_acute_following_pos = nil
225                                         return
226                                 end
227
228                                 if self.pathfinding_timer >= 0.5 and height_diff <= self.view_distance/2 then
229                                         local acute_following_pos = vector.floor(vector.add(self.following_pos,0.5))
230
231                                         if (not self.old_path_pos or (self.old_path_pos and not vector.equals(acute_pos,self.old_path_pos))) and
232                                                         (not self.old_acute_following_pos or (self.old_acute_following_pos and vector.distance(self.old_acute_following_pos,acute_following_pos) > 2)) then
233                                                 
234                                                 --if a player tries to hide in a node
235                                                 if minetest.get_nodedef(minetest.get_node(acute_following_pos).name, "walkable") then
236                                                         acute_following_pos.y = acute_following_pos.y + 1
237                                                 end
238
239                                                 --if a player tries to stand off the side of a node
240                                                 if not minetest.get_nodedef(minetest.get_node(vector.new(acute_following_pos.x,acute_following_pos.y-1,acute_following_pos.z)).name, "walkable") then
241                                                         local min = vector.subtract(acute_following_pos,1)
242                                                         local max = vector.add(acute_following_pos,1)
243
244                                                         local index_table = minetest.find_nodes_in_area_under_air(min, max, all_walkable_nodes)
245                                                         --optimize this as much as possible
246                                                         for _,i_pos in pairs(index_table) do
247                                                                 if minetest.get_nodedef(minetest.get_node(i_pos).name, "walkable") then
248                                                                         acute_following_pos = vector.new(i_pos.x,i_pos.y+1,i_pos.z)
249                                                                         break
250                                                                 end
251                                                         end
252                                                 end
253                                                 
254                                                 local path = minetest.find_path(acute_pos,acute_following_pos,self.view_distance,1,1,"A*_noprefetch")
255                                                 --if the path fails then raycast down to scare player or accidentally find new path
256                                                 --disabled for extreme cpu usage
257                                                 --[[
258                                                 if not path then
259                                                         local ray = minetest.raycast(acute_following_pos, vector.new(acute_following_pos.x,acute_following_pos.y-self.view_distance,acute_following_pos.z), false, false)
260                                                         for pointed_thing in ray do
261                                                                 if pointed_thing.above then
262                                                                         path = minetest.find_path(self.object:get_pos(),pointed_thing.above,self.view_distance,1,5,"A*_noprefetch")
263                                                                         break
264                                                                 end
265                                                         end
266                                                 end
267                                                 ]]--
268                                                 if path then
269                                                         self.whip_turn = 0.05
270                                                         self.path_data = path
271
272                                                         --remove the first element of the list
273                                                         --shift whole list down
274                                                         for i = 2,table.getn(self.path_data) do
275                                                                 self.path_data[i-1] = self.path_data[i]
276                                                         end
277                                                         self.path_data[table.getn(self.path_data)] = nil
278                                                         
279                                                         --cut corners (go diagonal)
280                                                         if self.path_data and table.getn(self.path_data) >= 3 then
281                                                                 local number = 3
282                                                                 for i = 3,table.getn(self.path_data) do
283                                                                         local pos1 = self.path_data[number-2]
284                                                                         local pos2 = self.path_data[number]
285
286                                                                         --print(number)
287                                                                         --check if diagonal and has direct line of sight
288                                                                         if pos1 and pos2 and pos1.x ~= pos2.x and pos1.z ~= pos2.z and pos1.y == pos2.y then
289                                                                                 local pos3 = vector.divide(vector.add(pos1,pos2),2)
290                                                                                 pos3.y = pos3.y - 1
291                                                                                 local can_cut,_ = minetest.line_of_sight(pos1, pos2)
292                                                                                 if can_cut then
293
294                                                                                         if minetest.get_nodedef(minetest.get_node(pos3).name, "walkable") == true then
295                                                                                                 --shift whole list down
296                                                                                                 --print("removing"..number-1)
297                                                                                                 for z = number-1,table.getn(self.path_data) do
298                                                                                                         self.path_data[z-1] = self.path_data[z]
299                                                                                                 end
300                                                                                                 self.path_data[table.getn(self.path_data)] = nil
301                                                                                                 number = number + 2
302                                                                                         else
303                                                                                                 number = number + 1
304                                                                                         end
305                                                                                 else
306                                                                                         number = number + 1
307                                                                                 end
308                                                                                 if number > table.getn(self.path_data) then
309                                                                                         break
310                                                                                 end
311                                                                         else
312                                                                                 number = number + 1
313                                                                         end
314                                                                 end
315                                                                 --if self.path_data and table.getn(self.path_data) <= 2 then
316                                                                 --      self.path_data = nil
317                                                                 --end
318                                                         end
319                                                 end
320                                                                                                 
321                                                 self.old_path_pos = acute_pos
322                                                 self.old_acute_following_pos = acute_following_pos      
323                                         end
324                                 end
325                         elseif (not self.following and self.path_data) or (self.path_data and height_diff > self.view_distance/2) then
326                                 self.path_data = nil
327                                 self.old_path_pos = nil
328                                 self.old_acute_following_pos = nil
329                         end
330                         --[[
331                         if self.path_data then
332                                 for index,pos_data in pairs(self.path_data) do
333                                         --print(dump(pos_data))
334                                         minetest.add_particle({
335                                                 pos = pos_data,
336                                                 velocity = {x=0, y=0, z=0},
337                                                 acceleration = {x=0, y=0, z=0},
338                                                 expirationtime = 0.01,
339                                                 size = 1,
340                                                 texture = "dirt.png",
341                                         })
342                                 end
343                         end
344                         ]]--
345                         --this is the real time path deletion as it goes along it
346                         if self.swimming == true then
347                                 self.path_data = nil
348                         end
349
350                         if self.path_data and table.getn(self.path_data) > 0 then
351                                 if vector.distance(acute_pos,self.path_data[1]) <= 1 then
352                                         --shift whole list down
353                                         for i = 2,table.getn(self.path_data) do
354                                                 self.path_data[i-1] = self.path_data[i]
355                                         end
356                                         self.path_data[table.getn(self.path_data)] = nil
357                                         self.whip_turn = 0.05
358                                         --if table.getn(self.path_data) == 0 then
359                                         --      self.path_data = nil
360                                         --end
361                                 end
362                         end
363                         --charge at the player
364                         if self.path_data and table.getn(self.path_data) < 2 then
365                                 self.path_data = nil
366                         end
367
368                 end
369         end
370         
371         return(mob_register)
372 end
373