pathfinder.lua 29 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129
  1. --localizing stuff for performance and easier changing
  2. local get_relevant_players = limbo_respawning.get_living_players
  3. local hash_node_position = minetest.hash_node_position
  4. local get_node_or_nil = minetest.get_node_or_nil
  5. local min = math.min
  6. local max = math.max
  7. local ceil = math.ceil
  8. local floor = math.floor
  9. local sqrt = math.sqrt
  10. local huge = math.huge
  11. local log = math.log
  12. local sign = math.sign
  13. local queue_new = Queue.new
  14. local queue_pop = Queue.pop
  15. local queue_push = Queue.push
  16. local queue_push_back = Queue.push_back
  17. local queue_remove = Queue.remove
  18. local queue_size = Queue.size
  19. local next = next
  20. local pathfinder = {}
  21. defense_mob_api.pathfinder = pathfinder
  22. pathfinder.update_interval = 2.0
  23. pathfinder.max_sector_size = 16
  24. pathfinder.max_sector_count = 800
  25. pathfinder.max_distance = 100
  26. pathfinder.class_names = {}
  27. local classes = {}
  28. local sector_id_counter = 0
  29. local tmp_vec = vector.new()
  30. local reg_nodes = minetest.registered_nodes
  31. local neighbors = {
  32. {x =-1, y = 0, z = 0},
  33. {x = 1, y = 0, z = 0},
  34. {x = 0, y =-1, z = 0},
  35. {x = 0, y = 1, z = 0},
  36. {x = 0, y = 0, z =-1},
  37. {x = 0, y = 0, z = 1},
  38. }
  39. local function pos_key(x, y, z)
  40. tmp_vec.x = x
  41. tmp_vec.y = y
  42. tmp_vec.z = z
  43. return hash_node_position(tmp_vec)
  44. end
  45. -- Returns object {surface=[min_x,min_y,min_z,max_x,max_y,max_z], normal={x,y,z}}
  46. local function compute_sector_interface(sector1, sector2)
  47. local min_x = max(sector1.min_x, sector2.min_x)
  48. local min_y = max(sector1.min_y, sector2.min_y)
  49. local min_z = max(sector1.min_z, sector2.min_z)
  50. local max_x = min(sector1.max_x, sector2.max_x)
  51. local max_y = min(sector1.max_y, sector2.max_y)
  52. local max_z = min(sector1.max_z, sector2.max_z)
  53. local normal = vector.new()
  54. if min_x > max_x
  55. then
  56. min_x = (min_x + max_x) / 2
  57. max_x = min_x
  58. normal.x = sector1.min_x < sector2.min_x and 1 or -1
  59. normal.y = 0
  60. normal.z = 0
  61. elseif min_y > max_y
  62. then
  63. min_y = (min_y + max_y) / 2
  64. max_y = min_y
  65. normal.x = 0
  66. normal.y = sector1.min_y < sector2.min_y and 1 or -1
  67. normal.z = 0
  68. elseif min_z > max_z
  69. then
  70. min_z = (min_z + max_z) / 2
  71. max_z = min_z
  72. normal.x = 0
  73. normal.y = 0
  74. normal.z = sector1.min_z < sector2.min_z and 1 or -1
  75. end
  76. return {
  77. surface = {
  78. min_x, min_y, min_z,
  79. max_x, max_y, max_z
  80. },
  81. normal = normal,
  82. }
  83. end
  84. local function to_world_pos(class, vec)
  85. return {
  86. x = vec.x + (class.x_size - 1 - class.collisionbox[1] - class.collisionbox[4]) / 2,
  87. y = vec.y + (class.y_size - 1 - class.collisionbox[2] - class.collisionbox[5]) / 2,
  88. z = vec.z + (class.z_size - 1 - class.collisionbox[3] - class.collisionbox[6]) / 2,
  89. }
  90. end
  91. local function to_grid_pos(class, vec)
  92. return {
  93. x = floor(vec.x + class.x_offset + 0.5),
  94. y = floor(vec.y + class.y_offset + 0.5),
  95. z = floor(vec.z + class.z_offset + 0.5)
  96. }
  97. end
  98. local function get_player_pos(player)
  99. local pos = player:get_pos()
  100. return floor(pos.x + 0.5), floor(pos.y + 0.5), floor(pos.z + 0.5)
  101. end
  102. local function sector_contains(sector, x, y, z)
  103. return x <= sector.max_x and x >= sector.min_x
  104. and y <= sector.max_y and y >= sector.min_y
  105. and z <= sector.max_z and z >= sector.min_z
  106. end
  107. local function find_containing_sector(class, x, y, z)
  108. for i,s in pairs(class.sectors)
  109. do
  110. if (sector_contains(s, x, y, z))
  111. then
  112. return s
  113. end
  114. end
  115. return nil
  116. end
  117. -- Deletes and queues a sector for regeneration
  118. local function invalidate_sector(sector, class)
  119. local id = sector.id
  120. class.sectors[id] = nil
  121. for i,l in pairs(sector.links)
  122. do
  123. l.links[id] = nil
  124. end
  125. if sector.distance ~= nil and sector.distance <= pathfinder.max_distance
  126. then
  127. queue_push(class.sector_seeds, {sector.min_x,sector.min_y,sector.min_z, nil,0})
  128. -- TODO what if replacement seed is blocked?
  129. end
  130. end
  131. local function invalidate_containing_sector(class, x, y, z)
  132. local sector = find_containing_sector(class, x, y, z)
  133. if sector
  134. then
  135. invalidate_sector(sector, class)
  136. end
  137. end
  138. -- Calculates the distances for each sector
  139. local function calculate_distances(class)
  140. local cost_method = class.cost_method
  141. local sectors = class.sectors
  142. for i,s in pairs(sectors)
  143. do
  144. s.distance = nil
  145. end
  146. local visited_ids = {}
  147. local visit = queue_new()
  148. local players = get_relevant_players()
  149. for _,p in ipairs(players)
  150. do
  151. local x, y, z = get_player_pos(p)
  152. local sector = find_containing_sector(class, x, y, z)
  153. if sector
  154. then
  155. sector.distance = 0
  156. queue_push(visit, sector)
  157. end
  158. end
  159. --iterate upon sectors with players in them
  160. --also recursively iterate upon neighbor sectors which have not been visited or have a lower distance than before
  161. while queue_size(visit) > 0
  162. do
  163. local sector = queue_pop(visit)
  164. visited_ids[sector.id] = true
  165. local distance = sector.distance
  166. for i,l in pairs(sector.links)
  167. do
  168. if not visited_ids[i]
  169. then
  170. local cost = cost_method(class, sector, l)
  171. local new_ldist = distance + cost
  172. local ldist = l.distance
  173. if ldist == nil or ldist > new_ldist
  174. then
  175. l.distance = new_ldist
  176. queue_push(visit, l)
  177. end
  178. end
  179. end
  180. end
  181. end
  182. -- Returns array [x,y,z, parent,parent_dir]
  183. local function find_sector_exits(sector, class)
  184. local sides = {
  185. {0,1,1,
  186. sector.max_x + 1, sector.min_y, sector.min_z,
  187. sector.max_x + 1, sector.max_y, sector.max_z},
  188. {0,1,1,
  189. sector.min_x - 1, sector.min_y, sector.min_z,
  190. sector.min_x - 1, sector.max_y, sector.max_z},
  191. {1,0,1,
  192. sector.min_x, sector.max_y + 1, sector.min_z,
  193. sector.max_x, sector.max_y + 1, sector.max_z},
  194. {1,0,1,
  195. sector.min_x, sector.min_y - 1, sector.min_z,
  196. sector.max_x, sector.min_y - 1, sector.max_z},
  197. {1,1,0,
  198. sector.min_x, sector.min_y, sector.max_z + 1,
  199. sector.max_x, sector.max_y, sector.max_z + 1},
  200. {1,1,0,
  201. sector.min_x, sector.min_y, sector.min_z - 1,
  202. sector.max_x, sector.max_y, sector.min_z - 1},
  203. }
  204. local path_check = class.path_check
  205. local exits = {}
  206. -- Find passable nodes that are cornered by >=2 different sectors or passability
  207. for i,s in ipairs(sides)
  208. do
  209. local xs = s[1]
  210. local ys = s[2]
  211. local zs = s[3]
  212. local min_x = s[4]
  213. local min_y = s[5]
  214. local min_z = s[6]
  215. local max_x = s[7]
  216. local max_y = s[8]
  217. local max_z = s[9]
  218. local map = {}
  219. for z = min_z,max_z,zs
  220. do
  221. for y = min_y,max_y,ys
  222. do
  223. for x = min_x,max_x,xs
  224. do
  225. local hash = pos_key(x, y, z)
  226. -- TODO Supply parent pos to path_check
  227. if path_check(class, vector.new(x, y, z), nil)
  228. then
  229. local val = 0
  230. local esec = find_containing_sector(class, x, y, z)
  231. if esec
  232. then
  233. val = esec.id
  234. end
  235. local edges = 0
  236. if xs ~= 0 and val ~= map[pos_key(x-xs, y, z)]
  237. then
  238. edges = edges + 1
  239. end
  240. if ys ~= 0 and val ~= map[pos_key(x, y-ys, z)]
  241. then
  242. edges = edges + 1
  243. end
  244. if zs ~= 0 and val ~= map[pos_key(x, y, z-zs)]
  245. then
  246. edges = edges + 1
  247. end
  248. if edges >= 2
  249. then
  250. table.insert(exits, {x,y,z, sector,i})
  251. end
  252. map[hash] = val
  253. else
  254. map[hash] = -1
  255. end
  256. if xs == 0 then break end
  257. end
  258. if ys == 0 then break end
  259. end
  260. if zs == 0 then break end
  261. end
  262. end
  263. return exits
  264. end
  265. --optimizing priority
  266. -- Returns a sector object {id, distance, links, min_x,min_y,min_z, max_x,max_y,max_z}
  267. local function generate_sector(class, x, y, z, origin_dir)
  268. local max_sector_span = pathfinder.max_sector_size - 1
  269. local path_check = class.path_check
  270. local is_inside_max_sector_span
  271. do
  272. local hmss = floor(max_sector_span / 2)
  273. local hmss2 = ceil(max_sector_span / 2)
  274. local span_min_x = x - hmss
  275. local span_min_y = y - hmss
  276. local span_min_z = z - hmss
  277. local span_max_x = x + hmss2
  278. local span_max_y = y + hmss2
  279. local span_max_z = z + hmss2
  280. is_inside_max_sector_span = function(x, y, z)
  281. return x >= span_min_x
  282. and x <= span_max_x
  283. and y >= span_min_y
  284. and y <= span_max_y
  285. and z >= span_min_z
  286. and z <= span_max_z
  287. end
  288. end
  289. --this is expanded outward
  290. local min_x = x
  291. local min_y = y
  292. local min_z = z
  293. local max_x = x
  294. local max_y = y
  295. local max_z = z
  296. local passed = {}
  297. local visit = queue_new()
  298. --a todo list of positions to check and maybe expand to
  299. queue_push(visit, {x=x,y=y,z=z})
  300. passed[pos_key(x, y, z)] = true
  301. local floodstart = os.clock()
  302. -- Flood fill all passable positions
  303. --Flood filling takes the majority of this function's runtime
  304. local visited_counter = 0
  305. --limiting it to 100 speeds it up but might cause problems
  306. --from first look still seems to work though
  307. while queue_size(visit) > 0 and visited_counter < 100
  308. do
  309. visited_counter = visited_counter + 1
  310. local pos = queue_pop(visit)
  311. local px = pos.x
  312. local py = pos.y
  313. local pz = pos.z
  314. for i, n in ipairs(neighbors)
  315. do
  316. if i ~= origin_dir
  317. then
  318. local npos = vector.add(pos, n)
  319. local nx = npos.x
  320. local ny = npos.y
  321. local nz = npos.z
  322. local nhash = pos_key(nx, ny, nz)
  323. if not passed[nhash]
  324. and (n.x == sign(nx - x) -- Only spread outward
  325. or n.y == sign(ny - y)
  326. or n.z == sign(nz - z))
  327. and is_inside_max_sector_span(nx, ny, nz) -- Limit to max_sector_span
  328. then
  329. local pass = path_check(class, npos, pos)--takes long
  330. if pass == nil then return nil end --npos is not loaded, no need for sectors
  331. --check if path check passed and not already in a sector
  332. if pass and not find_containing_sector(class, nx, ny, nz)
  333. then
  334. queue_push(visit, npos) --check neighboring position next
  335. passed[nhash] = true --so this one isn't checked twice
  336. --expand min/max sector bounds if needed
  337. min_x = min(min_x, nx)
  338. min_y = min(min_y, ny)
  339. min_z = min(min_z, nz)
  340. max_x = max(max_x, nx)
  341. max_y = max(max_y, ny)
  342. max_z = max(max_z, nz)
  343. end
  344. end
  345. end
  346. end
  347. end
  348. defense_mob_api:track_time("pf_flood_fill", os.clock() - floodstart)
  349. -- Find the largest passable box (or cuboid)
  350. --[[ Using dynamic programming:
  351. S(x,y,z) = { 1 + min S(x-a,y-b,z-c) for 1 <= a+b+c <= 3 and max(x,y,z) == 1, if passable(x,y)
  352. The largest cube is the maximum S, with the top-right-far corner at (x,y,z) in which the maximum S value is found.
  353. Using the largest cube as the starting point, the largest box can be found by finding equal and adjacent S values.
  354. ]]
  355. --[[if you're a nub like me when I first read this: Dynamic programming means we store results we'll need later multiple times in an array so we don't have to recalculate them and get better performance.
  356. convert table that matches coords to a boolean passable value to a 3d array
  357. the values in the array form a gradient which we can use to find the direction to the
  358. other corner of the box
  359. ]]
  360. local x_stride = 1
  361. local y_stride = max_x - min_x + 2 -- = (max_x - min_x + 2) * x_stride
  362. -- the +2 might be because of lua for loops being <= by default
  363. local z_stride = (max_y - min_y + 2) * y_stride
  364. -- Compute S
  365. local s_matrix = {} --[[3d array with values arranged sequentially
  366. like this:
  367. 2d array:
  368. x0 y0 z0
  369. x1 y1 z1
  370. x2 y2 z2
  371. sequential 2d array:
  372. x0 x1 x2 y0 y1 y2 z0 z1 z2
  373. the sequential approach should be faster because pointers are used less and thus less memory accessing happens
  374. ]]
  375. local index = z_stride
  376. for iz = min_z, max_z
  377. do
  378. index = index + y_stride
  379. for iy = min_y, max_y
  380. do
  381. index = index + x_stride
  382. for ix = min_x, max_x
  383. do
  384. if passed[pos_key(ix, iy, iz)]
  385. then
  386. --increment value of lowest neighbor, putting it into current field.
  387. --creates a gradient
  388. local s = 1 + min(
  389. --can propably optimize this a little by precalculating index-x_stride
  390. s_matrix[index - x_stride] or 0,
  391. s_matrix[index - y_stride] or 0,
  392. s_matrix[index - z_stride] or 0,
  393. s_matrix[index - x_stride - y_stride] or 0,
  394. s_matrix[index - x_stride - z_stride] or 0,
  395. s_matrix[index - y_stride - z_stride] or 0,
  396. s_matrix[index - x_stride - y_stride - z_stride] or 0)
  397. s_matrix[index] = s
  398. else
  399. s_matrix[index] = 0
  400. end
  401. index = index + 1
  402. end
  403. end
  404. end
  405. -- Starting at (x,y,z), go up the S gradient until a corner is found
  406. max_x, max_y, max_z = x, y, z
  407. index = (max_z - min_z + 1) * z_stride + (max_y - min_y + 1) * y_stride + (max_x - min_x + 1) * x_stride
  408. while true
  409. do
  410. local s = s_matrix[index] or 0
  411. if (s_matrix[index + x_stride + y_stride + z_stride] or -1) > s --make sure we only go up and don't go where it isn't passable
  412. then
  413. index = index + x_stride + y_stride + z_stride
  414. max_x = max_x + 1
  415. max_y = max_y + 1
  416. max_z = max_z + 1
  417. else
  418. break
  419. end
  420. end
  421. -- (max_x,max_y,max_z) or [index] is now at a corner of a cube
  422. local base_size = s_matrix[index]
  423. -- Now go to the corner of the box (not cube)
  424. --aka turn cube into box
  425. local edge_x, edge_y, edge_z = false, false, false
  426. while true
  427. do
  428. --move diagonally
  429. if (s_matrix[index + x_stride + y_stride] or -1) == base_size and not (edge_x or edge_y)
  430. then
  431. index = index + x_stride + y_stride
  432. max_x = max_x + 1
  433. max_y = max_y + 1
  434. edge_z = true
  435. elseif (s_matrix[index + x_stride + z_stride] or -1) == base_size and not (edge_x or edge_z)
  436. then
  437. index = index + x_stride + z_stride
  438. max_x = max_x + 1
  439. max_z = max_z + 1
  440. edge_y = true
  441. elseif (s_matrix[index + y_stride + z_stride] or -1) == base_size and not (edge_y or edge_z)
  442. then
  443. index = index + y_stride + z_stride
  444. max_y = max_y + 1
  445. max_z = max_z + 1
  446. edge_x = true
  447. --move straight
  448. elseif (s_matrix[index + x_stride] or -1) == base_size and not edge_x
  449. then
  450. index = index + x_stride
  451. max_x = max_x + 1
  452. edge_y = true
  453. edge_z = true
  454. elseif (s_matrix[index + y_stride] or -1) == base_size and not edge_y
  455. then
  456. index = index + y_stride
  457. max_y = max_y + 1
  458. edge_x = true
  459. edge_z = true
  460. elseif (s_matrix[index + z_stride] or -1) == base_size and not edge_z
  461. then
  462. index = index + z_stride
  463. max_z = max_z + 1
  464. edge_x = true
  465. edge_y = true
  466. else
  467. break
  468. end
  469. end
  470. -- (max_x,max_y,max_z) or [index] is now at a corner of the sector to generate
  471. -- Compute extended dimensions
  472. --aka go into other direction, turning the cube even more boxy
  473. local w, h, l = 0, 0, 0
  474. for iw = 1, max_sector_span
  475. do
  476. if s_matrix[index - iw * x_stride] ~= base_size then break end
  477. w = iw
  478. end
  479. for ih = 1, max_sector_span
  480. do
  481. if s_matrix[index - ih * y_stride] ~= base_size then break end
  482. h = ih
  483. end
  484. for il = 1,max_sector_span
  485. do
  486. if s_matrix[index - il * z_stride] ~= base_size then break end
  487. l = il
  488. end
  489. -- Compute final bounds (cube base size + extended dimensions)
  490. min_x = max_x - base_size + 1
  491. min_y = max_y - base_size + 1
  492. min_z = max_z - base_size + 1
  493. local max_dim = max(w,h,l)
  494. if max_dim == w
  495. then
  496. min_x = min_x - w
  497. if s_matrix[index - x_stride - y_stride] == base_size
  498. then
  499. min_y = min_y - h
  500. elseif s_matrix[index - x_stride - z_stride] == base_size
  501. then
  502. min_z = min_z - l
  503. end
  504. elseif max_dim == h
  505. then
  506. min_y = min_y - h
  507. if s_matrix[index - y_stride - x_stride] == base_size
  508. then
  509. min_x = min_x - w
  510. elseif s_matrix[index - y_stride - z_stride] == base_size
  511. then
  512. min_z = min_z - l
  513. end
  514. elseif max_dim == l
  515. then
  516. min_z = min_z - l
  517. if s_matrix[index - z_stride - x_stride] == base_size
  518. then
  519. min_x = min_x - w
  520. elseif s_matrix[index - z_stride - y_stride] == base_size
  521. then
  522. min_y = min_y - h
  523. end
  524. end
  525. sector_id_counter = sector_id_counter + 1
  526. return {
  527. id = sector_id_counter,
  528. distance = nil,
  529. links = {},
  530. min_x = min_x,
  531. min_y = min_y,
  532. min_z = min_z,
  533. max_x = max_x,
  534. max_y = max_y,
  535. max_z = max_z,
  536. }
  537. end
  538. -- Removes sectors and seeds too far away from players
  539. local function prune_sectors(class)
  540. local ceas = os.clock()
  541. defense_mob_api:log("Pruning sectors...")
  542. local max_distance = pathfinder.max_distance
  543. local sectors = class.sectors
  544. local sector_seeds = class.sector_seeds
  545. local players = get_relevant_players()
  546. -- Remove sectors
  547. local to_remove = {}
  548. for i,s in pairs(sectors)
  549. do
  550. if s.distance == nil or s.distance > max_distance
  551. then
  552. to_remove[i] = true
  553. end
  554. end
  555. for i,_ in pairs(to_remove)
  556. do
  557. local s = sectors[i]
  558. sectors[i] = nil
  559. for __,l in pairs(s.links)
  560. do
  561. if not to_remove[l.id]
  562. then
  563. invalidate_sector(l, class)
  564. end
  565. end
  566. end
  567. -- Remove seeds
  568. for i = sector_seeds.last,sector_seeds.first,-1
  569. do
  570. local seed = sector_seeds[i]
  571. local seed_pos = {x=seed[1], y=seed[2], z=seed[3]}
  572. local far = true
  573. for _,p in ipairs(players)
  574. do
  575. local x, y, z = get_player_pos(p)
  576. if vector.distance({x=x,y=y,z=z}, seed_pos) <= max_distance
  577. then
  578. far = false
  579. end
  580. end
  581. if far
  582. then
  583. queue_remove(sector_seeds, i)
  584. end
  585. end
  586. defense_mob_api:track_time("pf_prune_secs", os.clock() - ceas)
  587. end
  588. --Optimizing priority
  589. local function update_class(class)
  590. local max_sector_count = pathfinder.max_sector_count
  591. local max_distance = pathfinder.max_distance
  592. local sectors = class.sectors
  593. local sector_seeds = class.sector_seeds
  594. local path_check = class.path_check
  595. local force_generate = 0
  596. local should_refresh_distances = false
  597. -- Generate new seeds from player positions
  598. local players = get_relevant_players()
  599. for _,p in ipairs(players)
  600. do
  601. local x, y, z = get_player_pos(p)
  602. local sector = find_containing_sector(class, x, y, z)
  603. if not sector
  604. then
  605. queue_push_back(sector_seeds, {x, y, z, nil, 0})
  606. should_refresh_distances = true
  607. force_generate = force_generate + 1
  608. else
  609. local distance = sector.distance
  610. if distance == nil or distance > 0
  611. then
  612. should_refresh_distances = true
  613. end
  614. end
  615. end
  616. -- Grow sector seeds into sectors
  617. local sector_count = 0
  618. for _,__ in pairs(sectors)
  619. do
  620. sector_count = sector_count + 1
  621. end
  622. local ceas = os.clock()
  623. --log propably expensive expensive
  624. local sectors_to_generate = max(ceil(10 / log(sector_count + 10)), 1)
  625. local target_sector_count = min(sector_count + sectors_to_generate, max_sector_count) + force_generate
  626. while sector_count < target_sector_count and queue_size(sector_seeds) > 0
  627. do
  628. local seed = queue_pop(sector_seeds)
  629. local x = seed[1]
  630. local y = seed[2]
  631. local z = seed[3]
  632. -- TODO Supply parent pos to path_check
  633. if not find_containing_sector(class, x, y, z) and
  634. path_check(class, {x = x, y = y, z = z}, nil)
  635. then
  636. local sector_ceas = os.clock()
  637. local new_sector = generate_sector(class, x, y, z, seed[5])
  638. defense_mob_api:track_time("pf_generate_sector", os.clock() - sector_ceas)
  639. local parent = seed[4]
  640. if new_sector
  641. then
  642. should_refresh_distances = true
  643. if not parent or parent.distance == nil or parent.distance < max_distance
  644. then
  645. local id = new_sector.id
  646. sectors[id] = new_sector
  647. sector_count = sector_count + 1
  648. -- Link parent
  649. if parent
  650. then
  651. new_sector.links[parent.id] = parent
  652. parent.links[id] = new_sector
  653. end
  654. -- Generate new seeds and link adjacent sectors
  655. local exits = find_sector_exits(new_sector, class)
  656. for _,e in ipairs(exits)
  657. do
  658. local exited_sector = find_containing_sector(class, e[1], e[2], e[3])
  659. if exited_sector
  660. then
  661. if not exited_sector.links[new_sector.id]
  662. then
  663. exited_sector.links[new_sector.id] = new_sector
  664. new_sector.links[exited_sector.id] = exited_sector
  665. end
  666. else
  667. queue_push(sector_seeds, e)
  668. end
  669. end
  670. end
  671. end
  672. end
  673. end
  674. defense_mob_api:track_time("pf_generate_sectorS", os.clock() - ceas)
  675. -- Update sector distance values
  676. if should_refresh_distances
  677. then
  678. calculate_distances(class)
  679. end
  680. -- TODO Reseed far exits
  681. defense_mob_api:log(class.name .. ": There are " .. sector_count .. " sectors, " .. queue_size(sector_seeds) .. " seeds.")
  682. -- Prune excess sectors
  683. if sector_count + queue_size(sector_seeds) >= max_sector_count
  684. then
  685. prune_sectors(class)
  686. end
  687. end
  688. local function update()
  689. local ceas = os.clock()
  690. for _, c in pairs(classes)
  691. do
  692. update_class(c)
  693. end
  694. defense_mob_api:track_time("pf_update", os.clock() - ceas)
  695. end
  696. -- Cost methods
  697. pathfinder.default_cost_method = {}
  698. function pathfinder.default_cost_method.air(class, src_sector, dst_sector)
  699. local dx = ((dst_sector.min_x + dst_sector.max_x) - (src_sector.min_x + src_sector.max_x)) / 2
  700. local dy = ((dst_sector.min_y + dst_sector.max_y) - (src_sector.min_y + src_sector.max_y)) / 2
  701. local dz = ((dst_sector.min_z + dst_sector.max_z) - (src_sector.min_z + src_sector.max_z)) / 2
  702. return math.sqrt(dx*dx + dy*dy + dz*dz)
  703. end
  704. function pathfinder.default_cost_method.ground(class, src_sector, dst_sector)
  705. local dy = dst_sector.min_y - src_sector.min_y
  706. if dy > class.jump_height
  707. then
  708. return huge
  709. end
  710. local dx = ((dst_sector.min_x + dst_sector.max_x) - (src_sector.min_x + src_sector.max_x)) / 2
  711. local dz = ((dst_sector.min_z + dst_sector.max_z) - (src_sector.min_z + src_sector.max_z)) / 2
  712. if dy >= 0
  713. then
  714. dy = dy * 1.5
  715. else
  716. dy = 1
  717. end
  718. return math.sqrt(dx*dx + dz*dz) + dy
  719. end
  720. --[[optimization for path checks:
  721. flood fill time without this: 0.0107
  722. we make use of the fact that get_node_or_nil is accessed multiple times
  723. for the same position in subsequent path checks. we speed this up by
  724. caching 512 position hashes of non-walkable nodes in a table.
  725. when 512 hashes are stored, we start throwing out one hash each time
  726. a new one is stored.
  727. 512 might not be the ideal number to store but it's definetely better than nothing.
  728. random_access_number first counts the stored hashes. once 512 hashes are
  729. stored the cache_non_walkable function gets replaced. now it stores the index
  730. of the next number to be thrown out.
  731. ]]
  732. local random_access_number = 0
  733. local non_walkable_cache = {}
  734. local function cache_non_walkable(poshash)
  735. --TODO: maybe use an actual replacement policy instead
  736. if random_access_number == 512
  737. then
  738. --replace this function with an impostor
  739. random_access_number = nil
  740. cache_non_walkable = function(poshash)
  741. --throw out a cached hash. This is not really selective
  742. --but it ensures every hash gets thrown out at some point
  743. --making it more selective might speed it up more.
  744. local index = next(non_walkable_cache, random_access_number)
  745. if index == nil --restart from beginning
  746. then
  747. index = next(non_walkable_cache)
  748. end
  749. non_walkable_cache[random_access_number or 0] = nil
  750. random_access_number = index
  751. non_walkable_cache[poshash] = true
  752. end
  753. return
  754. end
  755. non_walkable_cache[poshash] = true
  756. random_access_number = random_access_number + 1
  757. end
  758. -- Path checks
  759. local function path_check_common(class, pos)
  760. for z = pos.z, pos.z + class.z_size - 1
  761. do
  762. tmp_vec.z = z
  763. for y = pos.y, pos.y + class.y_size - 1
  764. do
  765. tmp_vec.y = y
  766. for x = pos.x, pos.x + class.x_size - 1
  767. do
  768. tmp_vec.x = x
  769. local poshash = hash_node_position(tmp_vec)
  770. --check cache first
  771. if not non_walkable_cache[poshash]
  772. then
  773. local node = get_node_or_nil(tmp_vec)
  774. if not node then return nil end --if not loaded
  775. --getting node definition
  776. node = reg_nodes[node.name] or {walkable = true}--unknown node is walkable
  777. if node.walkable
  778. then
  779. return false
  780. end
  781. cache_non_walkable(poshash)
  782. end
  783. end
  784. end
  785. end
  786. return true
  787. end
  788. pathfinder.default_path_check = {}
  789. function pathfinder.default_path_check.air(class, pos, parent)
  790. return path_check_common(class, pos)
  791. end
  792. function pathfinder.default_path_check.ground(class, pos, parent)
  793. if not path_check_common(class, pos)
  794. then
  795. return false
  796. end
  797. -- Find ground
  798. local vertical = parent == nil or (pos.x == parent.x and pos.z == parent.z)
  799. for z = pos.z, pos.z + class.z_size - 1
  800. do
  801. tmp_vec.z = z
  802. for x = pos.x, pos.x + class.x_size - 1
  803. do
  804. tmp_vec.x = x
  805. local last_walkable = false
  806. for y = pos.y, pos.y - class.jump_height - 1, -1
  807. do
  808. tmp_vec.y = y
  809. local poshash = hash_node_position(tmp_vec)
  810. if not non_walkable_cache[poshash]
  811. then
  812. local node = get_node_or_nil(tmp_vec)
  813. if not node then return nil end
  814. --getting node definition
  815. node = reg_nodes[node.name] or {walkable = true}--unknown node is walkable
  816. local walkable = node.walkable
  817. if y < pos.y and walkable and not last_walkable
  818. then
  819. local ground_dist = pos.y - y
  820. if ground_dist == 1 or (ground_dist > 1 and vertical)
  821. then
  822. return true
  823. end
  824. end
  825. last_walkable = walkable
  826. if not walkable
  827. then
  828. cache_non_walkable(poshash)
  829. end
  830. end
  831. last_walkable = false
  832. end
  833. end
  834. end
  835. -- TODO How to allow falls?
  836. return false
  837. end
  838. ----------------
  839. -- PUBLIC API --
  840. ----------------
  841. pathfinder.classes = classes -- For debug
  842. pathfinder.find_containing_sector = find_containing_sector -- For debug
  843. -- Registers a pathfinder class
  844. function pathfinder:register_class(class_name, properties)
  845. table.insert(pathfinder.class_names, class_name)
  846. local class = {
  847. name = class_name,
  848. sectors = {},
  849. sector_seeds = queue_new(),
  850. jump_height = properties.jump_height,
  851. path_check = properties.path_check,
  852. cost_method = properties.cost_method,
  853. collisionbox = properties.collisionbox,
  854. x_offset = properties.collisionbox[1] + 0.01,
  855. y_offset = properties.collisionbox[2] + 0.01,
  856. z_offset = properties.collisionbox[3] + 0.01,
  857. x_size = ceil(properties.collisionbox[4] - properties.collisionbox[1]),
  858. y_size = ceil(properties.collisionbox[5] - properties.collisionbox[2]),
  859. z_size = ceil(properties.collisionbox[6] - properties.collisionbox[3]),
  860. }
  861. classes[class_name] = class
  862. end
  863. local function find_nearest_player(sector, gx, gy, gz)
  864. --find nearest player
  865. local players = get_relevant_players()
  866. local nearest_player = nil
  867. local nearest_dist_sq = huge
  868. for _,p in ipairs(players)
  869. do
  870. local px, py, pz = get_player_pos(p)
  871. if sector_contains(sector, px, py, pz)
  872. then
  873. local dx = px - gx;
  874. local dy = py - gy;
  875. local dz = pz - gz;
  876. local dist_sq = dx*dx + dy*dy + dz*dz;
  877. if dist_sq < nearest_dist_sq
  878. then
  879. nearest_dist_sq = dist_sq
  880. nearest_player = p
  881. end
  882. end
  883. end
  884. if nearest_player
  885. then
  886. return nearest_player:get_pos()
  887. else
  888. return nil
  889. end
  890. end
  891. -- Returns the destination for an entity of class class_name at position (x,y,z) to get to the nearest player
  892. function pathfinder:get_waypoint(class_name, x, y, z)
  893. local ceas = os.clock()
  894. local class = classes[class_name]
  895. local grid_pos = to_grid_pos(class, {x=x, y=y, z=z})
  896. local gx = grid_pos.x
  897. local gy = grid_pos.y
  898. local gz = grid_pos.z
  899. local sector = find_containing_sector(class, gx, gy, gz)
  900. if not sector or sector.distance == nil then return nil end
  901. if sector.distance == 0
  902. then
  903. local nearest_player = find_nearest_player(sector, gx, gy, gz)
  904. if not nearest_player --this happens a lot
  905. then
  906. --double check so pathfinding is more responsive
  907. --we don't get 100% success this way but way more than if we only try once
  908. --cost is surprisingly not that high, propably because most relevant sectors are already generated?
  909. update()
  910. sector = find_containing_sector(class, gx, gy, gz)
  911. if not sector or sector.distance == nil then return nil end
  912. nearest_player = find_nearest_player(sector, gx, gy, gz)
  913. end
  914. return nearest_player
  915. end
  916. local nearest_link = nil
  917. for i,l in pairs(sector.links)
  918. do
  919. if l.distance ~= nil
  920. and (not nearest_link or l.distance < nearest_link.distance)
  921. then
  922. nearest_link = l
  923. end
  924. end
  925. if not nearest_link then return nil end
  926. local interface = compute_sector_interface(sector, nearest_link)
  927. if not interface
  928. then
  929. defense_mob_api:log("Error! No interface found between sectors " .. sector.id .. " and " .. nearest_link.id .. " in " .. class_name)
  930. return nil
  931. end
  932. local surface = interface.surface
  933. local normal = interface.normal
  934. local waypoint = {
  935. x = max(surface[1], min(surface[4], gx)),
  936. y = max(surface[2], min(surface[5], gy)),
  937. z = max(surface[3], min(surface[6], gz))
  938. }
  939. local delta = vector.subtract({x=x,y=y,z=z}, waypoint)
  940. local directed_distance = vector.dot(normal, delta) / vector.length(normal)
  941. local waypoint_offset = vector.multiply(normal, max(-1, min(1, directed_distance + 1)))
  942. defense_mob_api:track_time("waypoint", os.clock() - ceas)
  943. return to_world_pos(class, vector.add(waypoint, waypoint_offset))
  944. end
  945. --update all classes every update_interval seconds
  946. local last_update_time = 0
  947. minetest.register_globalstep(function(dtime)
  948. local gt = minetest.get_gametime()
  949. if last_update_time + pathfinder.update_interval < gt
  950. then
  951. update()
  952. last_update_time = gt
  953. end
  954. end)
  955. minetest.register_on_placenode(function(pos, newnode, placer, oldnode, itemstack, pointed_thing)
  956. for n,c in pairs(classes)
  957. do
  958. invalidate_containing_sector(c, pos.x, pos.y, pos.z)
  959. end
  960. end)
  961. minetest.register_on_dignode(function(pos, oldnode, digger)
  962. for n,c in pairs(classes)
  963. do
  964. invalidate_containing_sector(c, pos.x, pos.y, pos.z)
  965. end
  966. end)