pathfinder.lua 26 KB

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