npc.lua 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  1. local context={}
  2. minetest.register_on_joinplayer(
  3. function(player)
  4. context[player:get_player_name()]={}
  5. end
  6. )
  7. minetest.register_on_leaveplayer(
  8. function(player)
  9. context[player:get_player_name()]=nil
  10. end
  11. )
  12. local function round(num)
  13. local dec=(10*num)%10
  14. if dec>=5 then
  15. return math.ceil(num)
  16. else
  17. return math.floor(num)
  18. end
  19. end
  20. local function findPath(npc,ps1,ps2)
  21. return minetest.find_path(ps1,ps2,15,npc.stepheight,npc.fear_height-1,"Dijkstra")
  22. end
  23. local function turnTo(npc,pos)
  24. local currentPos=npc.object:get_pos();
  25. local ve={x=pos.x-currentPos.x,y=pos.y-currentPos.y,z=pos.z-currentPos.z}
  26. local px=math.abs(ve.x)
  27. local pz=math.abs(ve.z)
  28. local yaw
  29. --atan(opposite/adjacent)
  30. if px<pz then
  31. yaw=math.atan(px,pz)
  32. if ve.x<0 then
  33. if ve.z<0 then
  34. yaw=math.rad(180)-yaw
  35. end
  36. elseif ve.x>0 then
  37. if ve.z<0 then
  38. yaw=math.rad(180)+yaw
  39. elseif ve.z>0 then
  40. yaw=math.rad(360)-yaw
  41. end
  42. end
  43. elseif px>pz then
  44. yaw=math.atan(pz,px)
  45. if ve.x<0 then
  46. if ve.z<0 then
  47. yaw=math.rad(90)+yaw
  48. elseif ve.z>0 then
  49. yaw=math.rad(90)-yaw
  50. end
  51. elseif ve.x>0 then
  52. if ve.z<0 then
  53. yaw=math.rad(270)-yaw
  54. elseif ve.z>0 then
  55. yaw=math.rad(270)+yaw
  56. end
  57. end
  58. end
  59. npc:set_yaw(yaw)
  60. end
  61. local function moveTo(npc,pos)
  62. local ps=npc.object:get_pos()
  63. ps.x=round(ps.x)
  64. ps.z=round(ps.z)
  65. if ps.x==pos.x and ps.z==pos.z then
  66. return true
  67. else
  68. turnTo(npc,pos)
  69. npc:set_velocity(npc.walk_velocity)
  70. return false
  71. end
  72. end
  73. local function followPath(npc,path)
  74. if path and #path>0 then
  75. if moveTo(npc,path[1]) then
  76. if #path==1 then
  77. npc.state="stand"
  78. npc:set_animation("stand")
  79. npc.object:set_velocity({x=0,y=0,z=0})
  80. npc.h2=npc.h1
  81. npc.h1=path[1]
  82. end
  83. table.remove(path,1)
  84. end
  85. end
  86. end
  87. local function spiral(cursor,r,fu)
  88. cursor.x=round(cursor.x)
  89. cursor.y=round(cursor.y)
  90. cursor.z=round(cursor.z)
  91. fu(cursor)
  92. cursor.z=cursor.z+1
  93. fu(cursor)
  94. for j=1,2*r+1,2 do
  95. for i=0,j-1,1 do
  96. cursor.x=cursor.x+1
  97. fu(cursor)
  98. end
  99. for i=0,j,1 do
  100. cursor.z=cursor.z-1
  101. fu(cursor)
  102. end
  103. for i=0,j,1 do
  104. cursor.x=cursor.x-1
  105. fu(cursor)
  106. end
  107. for i=0,j+1,1 do
  108. if i+1<=2*r+2
  109. then
  110. cursor.z=cursor.z+1
  111. fu(cursor)
  112. end
  113. end
  114. end
  115. end
  116. local function findPatrolPoint(npc,pos)
  117. local nodes={}
  118. local function add(ps)
  119. if minetest.get_node(ps).name=="mobs_dungeon:patrol_point" then
  120. nodes[#nodes+1]={x=ps.x,y=ps.y,z=ps.z}
  121. end
  122. end
  123. spiral({x=pos.x,y=pos.y-1,z=pos.z},5,add)
  124. if nodes then
  125. for fi=1,#nodes,1 do
  126. if not npc.h2 or (not (nodes[fi].x==npc.h2.x
  127. and nodes[fi].y==npc.h2.y and nodes[fi].z==npc.h2.z)
  128. and not (nodes[fi].x==npc.h1.x
  129. and nodes[fi].y==npc.h1.y and nodes[fi].z==npc.h1.z))
  130. then
  131. return nodes[fi]
  132. end
  133. end
  134. end
  135. return nil
  136. end
  137. mobs:register_mob(
  138. "mobs_dungeon:npc",
  139. {
  140. type="npc",
  141. hp_min=50,
  142. hp_max=100,
  143. armor=50,
  144. passive=false,
  145. walk_velocity=2,
  146. run_velocity=3,
  147. stand_chance=0,
  148. walk_chance=0,
  149. jump=false,
  150. stay_near={},
  151. pushable=false,
  152. view_range=15,
  153. damage=4,
  154. fear_height=3,
  155. suffocation=true,
  156. follow={},
  157. attacks_monsters=true,
  158. attack_npcs=false,
  159. attack_type="dogfight",
  160. runaway_from={},
  161. pathfinding=true,
  162. makes_footstep_sound=true,
  163. drops={},
  164. sounds={
  165. random="mobs_dungeon_npc_random",
  166. death="mobs_dungeon_npc_death",
  167. damage="mobs_dungeon_npc_damage"
  168. },
  169. visual="mesh",
  170. collisionbox={-0.35,-1.0,-0.35, 0.35,0.8,0.35},
  171. textures={{"character.png"}},
  172. child_texture={{"character.png"}},
  173. mesh="mobs_character.b3d",
  174. animation={
  175. speed_normal=30,
  176. speed_run=30,
  177. stand_start=0,
  178. stand_end=79,
  179. walk_start=168,
  180. walk_end=187,
  181. run_start=168,
  182. run_end=187,
  183. punch_start=200,
  184. punch_end=219,
  185. },
  186. owner="",
  187. order="patrol",
  188. on_spawn=function(self)
  189. self.object:set_properties(
  190. {
  191. textures={
  192. "character.png^mobs_dungeon_npc_overlay_"
  193. ..Random:next(1,9)..".png"
  194. }
  195. }
  196. )
  197. end,
  198. on_rightclick=function(self, clicker)
  199. local item=clicker:get_wielded_item()
  200. local name=clicker:get_player_name()
  201. local form={
  202. "size[2,2]",
  203. "real_coordinates[true]",
  204. "position[0,0]",
  205. "anchor[0,0]",
  206. }
  207. if self.owner and self.owner == name then
  208. if self.order == "follow" then
  209. form[#form+1]="dropdown[0,0;1;order;follow,stand,patrol;1]"
  210. elseif self.order == "stand" then
  211. form[#form+1]="dropdown[0,0;1;order;follow,stand,patrol;2]"
  212. else
  213. form[#form+1]="dropdown[0,0;1;order;follow,stand,patrol;3]"
  214. end
  215. end
  216. context[name].npc=self
  217. minetest.show_formspec(name,"mobs_dungeon:menu",table.concat(form,""))
  218. end,
  219. do_custom=function(self,dtime)
  220. if self.state=="attack" or self.state=="runaway" then
  221. self.mdPath={}
  222. return
  223. end
  224. if self.mdPath and #self.mdPath>0 then
  225. followPath(self,self.mdPath)
  226. elseif self.order=="patrol" then
  227. local target=findPatrolPoint(self,self.object:get_pos())
  228. if target then
  229. local path=findPath(self,self.object:get_pos(),target)
  230. if path then
  231. self.mdPath=path
  232. self.state=""
  233. self:set_animation("walk")
  234. followPath(self,path)
  235. end
  236. end
  237. end
  238. end
  239. }
  240. )
  241. local orders={
  242. follow=function(npc)
  243. npc.order="follow"
  244. npc.state="walk"
  245. npc.mdPath=nil
  246. end,
  247. stand=function(npc)
  248. npc.order="stand"
  249. npc.state="stand"
  250. npc.mdPath=nil
  251. npc:set_animation("stand")
  252. npc:set_velocity({x=0,y=0,z=0})
  253. end,
  254. patrol=function(npc)
  255. npc.order="patrol"
  256. end
  257. }
  258. minetest.register_on_player_receive_fields(
  259. function(player,formname,fields)
  260. if formname~="mobs_dungeon:menu" then
  261. return
  262. end
  263. if fields.order then
  264. local npc=context[player:get_player_name()].npc
  265. npc.order=fields.order
  266. orders[fields.order](npc)
  267. end
  268. end
  269. )
  270. mobs:spawn(
  271. {
  272. name="mobs_dungeon:npc",
  273. nodes={"mobs_dungeon:patrol_point"},
  274. min_light=8,
  275. chance=5000,
  276. active_object_count=1,
  277. max_height=0,
  278. }
  279. )
  280. mobs:register_egg("mobs_dungeon:npc", "NPC", "mobs_dungeon_npc_inventory.png", 0)