simple_api.lua 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586
  1. -- Mobs Api (16th January 2016)
  2. function mob_goTo(self, pos)
  3. end
  4. -- register mob function
  5. function mobs:register_simple_mob(name, def)
  6. print("bad bad")
  7. --[[
  8. local btdata = {
  9. waypoints= {},
  10. counters={},
  11. inv=minetest.create_detached_inventory("main", {}),
  12. history={},
  13. history_queue={},
  14. history_depth=20,
  15. }
  16. btdata.inv:set_size("main", 9)]]
  17. minetest.register_entity(name, {
  18. stepheight = def.stepheight or 0.6,
  19. name = name,
  20. type = def.type,
  21. attack_type = def.attack_type,
  22. fly = def.fly,
  23. fly_in = def.fly_in or "air",
  24. owner = def.owner or "",
  25. order = def.order or "",
  26. on_die = def.on_die,
  27. do_custom = def.do_custom,
  28. jump_height = def.jump_height or 6,
  29. jump_chance = def.jump_chance or 0,
  30. drawtype = def.drawtype, -- DEPRECATED, use rotate instead
  31. rotate = math.rad(def.rotate or 0), -- 0=front, 90=side, 180=back, 270=side2
  32. --lifetimer = def.lifetimer or 180, -- 3 minutes
  33. hp_min = def.hp_min or 5,
  34. hp_max = def.hp_max or 10,
  35. physical = true,
  36. collisionbox = def.collisionbox,
  37. visual = def.visual,
  38. visual_size = def.visual_size or {x = 1, y = 1},
  39. mesh = def.mesh,
  40. makes_footstep_sound = def.makes_footstep_sound or false,
  41. view_range = def.view_range or 5,
  42. pace_velocity = {
  43. walk = def.walk_velocity or 1,
  44. run = def.run_velocity or 2,
  45. },
  46. damage = def.damage or 0,
  47. light_damage = def.light_damage or 0,
  48. water_damage = def.water_damage or 0,
  49. lava_damage = def.lava_damage or 0,
  50. fall_damage = def.fall_damage or 1,
  51. fall_speed = def.fall_speed or -10, -- must be lower than -2 (default: -10)
  52. drops = def.drops or {},
  53. armor = def.armor,
  54. on_rightclick = def.on_rightclick,
  55. arrow = def.arrow,
  56. shoot_interval = def.shoot_interval,
  57. sounds = def.sounds or {},
  58. animation = def.animation,
  59. follow = def.follow,
  60. jump = def.jump or true,
  61. walk_chance = def.walk_chance or 50,
  62. attacks_monsters = def.attacks_monsters or false,
  63. group_attack = def.group_attack or false,
  64. --fov = def.fov or 120,
  65. passive = def.passive or false,
  66. recovery_time = def.recovery_time or 0.5,
  67. knock_back = def.knock_back or 3,
  68. blood_amount = def.blood_amount or 5,
  69. blood_texture = def.blood_texture or "mobs_blood.png",
  70. shoot_offset = def.shoot_offset or 0,
  71. floats = def.floats or 1, -- floats in water by default
  72. replace_rate = def.replace_rate,
  73. replace_what = def.replace_what,
  74. replace_with = def.replace_with,
  75. replace_offset = def.replace_offset or 0,
  76. timer = 0,
  77. env_damage_timer = 0, -- only used when state = "attack"
  78. tamed = false,
  79. pause_timer = 0,
  80. horny = false,
  81. hornytimer = 0,
  82. child = false,
  83. gotten = false,
  84. health = 0,
  85. reach = def.reach or 3,
  86. htimer = 0,
  87. child_texture = def.child_texture,
  88. docile_by_day = def.docile_by_day or false,
  89. time_of_day = 0.5,
  90. fear_height = def.fear_height or 0,
  91. runaway = def.runaway,
  92. runaway_timer = 0,
  93. destination = nil,
  94. pace = "walk",
  95. -- needed for mobf meshes (wolves, deer)
  96. automatic_face_movement_dir = true,
  97. bt_timer = 0,
  98. old_y = 0, -- some sort of weird bug
  99. goTo = mob_goTo,
  100. bt = nil,
  101. btData = nil,
  102. on_step = function(self, dtime)
  103. local btdata = self.btData
  104. local pos = self.object:getpos()
  105. local yaw = self.object:getyaw() or 0
  106. btdata.pos = pos
  107. btdata.yaw = yaw
  108. btdata.mob = self
  109. --btdata.inv = self.object:get_inventory()
  110. --btdata.inv:set_size("main", 9)
  111. --btdata.inv:add_item("main", "default:tree 50")
  112. self.bt_timer = self.bt_timer + dtime
  113. --print("bt_timer "..self.bt_timer)
  114. if self.bt_timer > 2 then
  115. -- debug -- print("\n<<< start >>>")
  116. -- inventories cannot be serialized and cause the game to crash if
  117. -- placed in the entity's table
  118. local inv = minetest.get_inventory({type="detached", name=self.inv_id})
  119. btdata.inv = inv
  120. bt.tick(self.bt, btdata)
  121. -- debug -- print("<<< end >>>\n")
  122. -- so clear it out after running the behavior trees
  123. btdata.inv = nil
  124. -- the inventory exists on its own
  125. self.bt_timer = 0
  126. end
  127. --self.object:set_inventory(btdata.inv)
  128. btdata.lastpos = pos
  129. if not self.fly then
  130. -- floating in water (or falling)
  131. local v = self.object:getvelocity()
  132. -- going up then apply gravity
  133. if v.y > 0.1 then
  134. self.object:setacceleration({
  135. x = 0,
  136. y = self.fall_speed,
  137. z = 0
  138. })
  139. end
  140. -- in water then float up
  141. if minetest.registered_nodes[node_ok(pos).name].groups.water then
  142. if self.floats == 1 then
  143. self.object:setacceleration({
  144. x = 0,
  145. y = -self.fall_speed / (math.max(1, v.y) ^ 2),
  146. z = 0
  147. })
  148. end
  149. else
  150. -- fall downwards
  151. self.object:setacceleration({
  152. x = 0,
  153. y = self.fall_speed,
  154. z = 0
  155. })
  156. -- fall damage
  157. if self.fall_damage == 1
  158. and self.object:getvelocity().y == 0 then
  159. local d = self.old_y - self.object:getpos().y
  160. if d > 5 then
  161. self.object:set_hp(self.object:get_hp() - math.floor(d - 5))
  162. effect(pos, 5, "tnt_smoke.png")
  163. print("death by falling")
  164. if check_for_death(self, pos) then
  165. return
  166. end
  167. end
  168. self.old_y = self.object:getpos().y
  169. end
  170. end
  171. end
  172. -- -- knockback timer
  173. -- if self.pause_timer > 0 then
  174. --
  175. -- self.pause_timer = self.pause_timer - dtime
  176. --
  177. -- if self.pause_timer < 1 then
  178. -- self.pause_timer = 0
  179. -- end
  180. --
  181. -- return
  182. -- end
  183. -- attack timer
  184. -- self.timer = self.timer + dtime
  185. -- if self.state ~= "attack" then
  186. --
  187. -- if self.timer < 1 then
  188. -- return
  189. -- end
  190. --
  191. -- self.timer = 0
  192. -- end
  193. -- never go over 100
  194. if self.timer > 100 then
  195. self.timer = 1
  196. end
  197. -- node replace check (cow eats grass etc.)
  198. -- handled by btree -- replace(self, pos)
  199. -- mob plays random sound at times
  200. if self.sounds.random
  201. and math.random(1, 100) == 1 then
  202. minetest.sound_play(self.sounds.random, {
  203. object = self.object,
  204. max_hear_distance = self.sounds.distance
  205. })
  206. end
  207. -- environmental damage timer (every 1 second)
  208. self.env_damage_timer = self.env_damage_timer + dtime
  209. if (self.state == "attack" and self.env_damage_timer > 1)
  210. or self.state ~= "attack" then
  211. self.env_damage_timer = 0
  212. do_env_damage(self)
  213. -- custom function (defined in mob lua file)
  214. if self.do_custom then
  215. self.do_custom(self)
  216. end
  217. end
  218. if self.destination ~= nil then
  219. --print("destination ")
  220. local dist = distance(pos, self.destination)
  221. -- print("walk dist ".. dist)
  222. local s = self.destination
  223. local vec = {
  224. x = pos.x - s.x,
  225. y = pos.y - s.y,
  226. z = pos.z - s.z
  227. }
  228. -- tprint(vec)
  229. if vec.x ~= 0
  230. or vec.z ~= 0 then
  231. yaw = (math.atan(vec.z / vec.x) + math.pi / 2) - self.rotate
  232. if s.x > pos.x then
  233. yaw = yaw + math.pi
  234. end
  235. self.object:setyaw(yaw)
  236. end
  237. -- anyone but standing npc's can move along
  238. if dist > (self.approachDistance or .1) then
  239. if (self.jump
  240. and get_velocity(self) <= 0.5
  241. and self.object:getvelocity().y == 0)
  242. or (self.object:getvelocity().y == 0
  243. and self.jump_chance > 0) then
  244. do_jump(self)
  245. end
  246. set_velocity(self, self.pace_velocity[self.pace] or self.pace_velocity.walk)
  247. set_animation(self, "walk")
  248. else
  249. -- we have arrived
  250. self.destination = nil
  251. set_velocity(self, 0)
  252. set_animation(self, "stand")
  253. end
  254. end
  255. end,
  256. on_punch = function(self, hitter, tflp, tool_capabilities, dir)
  257. print("got punched !!!!")
  258. -- weapon wear
  259. local weapon = hitter:get_wielded_item()
  260. local punch_interval = 1.4
  261. if tool_capabilities then
  262. punch_interval = tool_capabilities.full_punch_interval or 1.4
  263. end
  264. if weapon:get_definition()
  265. and weapon:get_definition().tool_capabilities then
  266. weapon:add_wear(math.floor((punch_interval / 75) * 9000))
  267. hitter:set_wielded_item(weapon)
  268. end
  269. -- weapon sounds
  270. if weapon:get_definition().sounds ~= nil then
  271. local s = math.random(0, #weapon:get_definition().sounds)
  272. minetest.sound_play(weapon:get_definition().sounds[s], {
  273. object = hitter,
  274. max_hear_distance = 8
  275. })
  276. else
  277. minetest.sound_play("default_punch", {
  278. object = hitter,
  279. max_hear_distance = 5
  280. })
  281. end
  282. local hp = self.object:get_hp()
  283. local pos = self.object:getpos()
  284. print("punched (hp: "..hp..")at "..pos.x..", "..pos.y..", "..pos.z)
  285. -- exit here if dead
  286. if check_for_death(self, pos) then
  287. print("died from punch")
  288. return
  289. end
  290. print("didn't die from punch")
  291. -- blood_particles
  292. if self.blood_amount > 0
  293. and not disable_blood then
  294. local pos = self.object:getpos()
  295. pos.y = pos.y + (-self.collisionbox[2] + self.collisionbox[5]) / 2
  296. effect(pos, self.blood_amount, self.blood_texture)
  297. end
  298. -- knock back effect
  299. if self.knock_back > 0 then
  300. local v = self.object:getvelocity()
  301. local r = 1.4 - math.min(punch_interval, 1.4)
  302. local kb = r * 5
  303. self.object:setvelocity({
  304. x = (dir.x or 0) * kb,
  305. y = 2,
  306. z = (dir.z or 0) * kb
  307. })
  308. self.pause_timer = r
  309. end
  310. end,
  311. on_activate = function(self, staticdata, dtime_s)
  312. self.btData = {
  313. groupID = "default",
  314. waypoints= {},
  315. paths= {},
  316. counters={},
  317. history={},
  318. history_queue={},
  319. history_depth=20,
  320. posStack={},
  321. }
  322. local btdata = self.btData
  323. self.inv_id= name..":"..math.random(1, 2000000000)
  324. --print(btdata.id)
  325. btdata.lastpos = self.object:getpos()
  326. if type(def.pre_activate) == "function" then
  327. def.pre_activate(self, static_data, dtime_s)
  328. end
  329. -- load entity variables
  330. if staticdata then
  331. local tmp = minetest.deserialize(staticdata)
  332. if tmp then
  333. for _,stat in pairs(tmp) do
  334. self[_] = stat
  335. end
  336. end
  337. else
  338. self.object:remove()
  339. return
  340. end
  341. local inventory = minetest.create_detached_inventory(self.inv_id, {})
  342. inventory:set_size("main", 9)
  343. -- select random texture, set model and size
  344. if not self.base_texture then
  345. self.base_texture = def.textures[math.random(1, #def.textures)]
  346. self.base_mesh = def.mesh
  347. self.base_size = self.visual_size
  348. self.base_colbox = self.collisionbox
  349. end
  350. -- set texture, model and size
  351. local textures = self.base_texture
  352. local mesh = self.base_mesh
  353. local vis_size = self.base_size
  354. local colbox = self.base_colbox
  355. -- specific texture if gotten
  356. if self.gotten == true
  357. and def.gotten_texture then
  358. textures = def.gotten_texture
  359. end
  360. -- specific mesh if gotten
  361. if self.gotten == true
  362. and def.gotten_mesh then
  363. mesh = def.gotten_mesh
  364. end
  365. -- set child objects to half size
  366. if self.child == true then
  367. vis_size = {
  368. x = self.base_size.x / 2,
  369. y = self.base_size.y / 2
  370. }
  371. if def.child_texture then
  372. textures = def.child_texture[1]
  373. end
  374. colbox = {
  375. self.base_colbox[1] / 2,
  376. self.base_colbox[2] / 2,
  377. self.base_colbox[3] / 2,
  378. self.base_colbox[4] / 2,
  379. self.base_colbox[5] / 2,
  380. self.base_colbox[6] / 2
  381. }
  382. end
  383. if self.health == 0 then
  384. self.health = math.random (self.hp_min, self.hp_max)
  385. end
  386. self.object:set_hp(self.health)
  387. self.object:set_armor_groups({fleshy = self.armor})
  388. self.old_y = self.object:getpos().y
  389. self.object:setyaw(math.random(1, 360) / 180 * math.pi)
  390. self.sounds.distance = (self.sounds.distance or 10)
  391. self.textures = textures
  392. self.mesh = mesh
  393. self.collisionbox = colbox
  394. self.visual_size = vis_size
  395. -- set anything changed above
  396. self.object:set_properties(self)
  397. update_tag(self)
  398. if type(def.post_activate) == "function" then
  399. def.post_activate(self, static_data, dtime_s)
  400. end
  401. end,
  402. get_staticdata = function(self)
  403. -- remove mob when out of range unless tamed
  404. if mobs.remove
  405. and self.remove_ok
  406. and not self.tamed then
  407. --print ("REMOVED", self.remove_ok, self.name)
  408. self.object:remove()
  409. return nil
  410. end
  411. self.remove_ok = true
  412. self.attack = nil
  413. self.following = nil
  414. self.state = "stand"
  415. if self.btData ~= nil then
  416. self.btData.inv = nil -- just in case
  417. self.btData.mob = nil -- just in case
  418. end
  419. -- used to rotate older mobs
  420. if self.drawtype
  421. and self.drawtype == "side" then
  422. self.rotate = math.rad(90)
  423. end
  424. local tmp = {}
  425. for _,stat in pairs(self) do
  426. local t = type(stat)
  427. if t ~= 'function'
  428. and t ~= 'nil'
  429. and t ~= 'userdata' then
  430. tmp[_] = self[_]
  431. end
  432. end
  433. -- print('===== '..self.name..'\n'.. dump(tmp)..'\n=====\n')
  434. return minetest.serialize(tmp)
  435. end,
  436. })
  437. end -- END mobs:register_mob function
  438. -- set content id's
  439. local c_air = minetest.get_content_id("air")
  440. local c_ignore = minetest.get_content_id("ignore")
  441. local c_obsidian = minetest.get_content_id("default:obsidian")
  442. local c_brick = minetest.get_content_id("default:obsidianbrick")
  443. local c_chest = minetest.get_content_id("default:chest_locked")