1 local minetest,math,vector = minetest,math,vector
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)
15 mobs.create_movement_functions = function(def,mob_register)
17 mob_register.swim = function(self,dtime)
18 local pos = self.object:get_pos()
20 local node = minetest.get_node(pos).name
22 if node == "main:water" or node =="main:waterflow" then
23 local vel = self.object:get_velocity()
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))
31 self.object:add_velocity(acceleration)
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")
45 self.object:punch(self.object, 2,
47 full_punch_interval=1.5,
48 damage_groups = {damage=hurty},
51 local firey = get_group(noder, "fire")
52 if not self.on_fire and firey > 0 then
53 start_fire(self.object)
55 self.hurt_inside_timer = 0.25
57 self.hurt_inside_timer = self.hurt_inside_timer - dtime
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
68 self.jump(self,moveresult)
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)
84 self.hurt_inside(self,dtime)
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)
92 self.whip_turn = self.whip_turn - dtime
93 if self.whip_turn <= 0 then
97 acceleration = vector.multiply(acceleration, 0.05)
99 self.object:add_velocity(acceleration)
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()
106 if self.path_data and table.getn(self.path_data) > 0 then
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))
120 --assume collisionbox is even x and z
121 local modifier = self.object:get_properties().collisionbox[4]*3
124 local pos2 = vector.add(vector.multiply(self.direction,modifier),pos)
126 local ray = minetest.raycast(pos, pos2, false, false)
131 pointed_thing = ray:next()
134 if pointed_thing then
135 if minetest.get_nodedef(minetest.get_node(pointed_thing.under).name, "walkable") then
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))
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))
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))
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
163 self.jump(self,moveresult)
166 self.swim(self,dtime)
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)
178 self.hurt_inside(self,dtime)
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)
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})
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
200 self.jump_timer = 1+math.random()
203 self.object:set_velocity(vector.new(0,0,0))
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
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)
220 --delete path if height too far
221 if self.path_data and height_diff > self.view_distance/2 then
223 self.old_path_pos = nil
224 self.old_acute_following_pos = nil
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))
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
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
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)
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)
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
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")
269 self.whip_turn = 0.05
270 self.path_data = path
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]
277 self.path_data[table.getn(self.path_data)] = nil
279 --cut corners (go diagonal)
280 if self.path_data and table.getn(self.path_data) >= 3 then
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]
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)
291 local can_cut,_ = minetest.line_of_sight(pos1, pos2)
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]
300 self.path_data[table.getn(self.path_data)] = nil
308 if number > table.getn(self.path_data) then
315 --if self.path_data and table.getn(self.path_data) <= 2 then
316 -- self.path_data = nil
321 self.old_path_pos = acute_pos
322 self.old_acute_following_pos = acute_following_pos
325 elseif (not self.following and self.path_data) or (self.path_data and height_diff > self.view_distance/2) then
327 self.old_path_pos = nil
328 self.old_acute_following_pos = nil
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({
336 velocity = {x=0, y=0, z=0},
337 acceleration = {x=0, y=0, z=0},
338 expirationtime = 0.01,
340 texture = "dirt.png",
345 --this is the real time path deletion as it goes along it
346 if self.swimming == true then
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]
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
363 --charge at the player
364 if self.path_data and table.getn(self.path_data) < 2 then