functions.lua 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  1. local S = mobs_npc.S
  2. -- show random message from list
  3. mobs_npc.npc_talk = function(self, player, message_list)
  4. local name = player:get_player_name() or ""
  5. local messages = message_list or self.messages or {"??"}
  6. if messages then
  7. local msg = messages[math.random(#messages)]
  8. minetest.chat_send_player(name, "<" .. (self.nametag or "") .. "> " .. msg)
  9. end
  10. end
  11. -- drop random item from list
  12. mobs_npc.drop_trade = function(self, player, item, item_list)
  13. local w_inv = player:get_wielded_item()
  14. if item ~= w_inv:get_name() then return end
  15. local name = player:get_player_name()
  16. if not mobs.is_creative(name) then
  17. w_inv:take_item()
  18. player:set_wielded_item(w_inv)
  19. end
  20. local pos = self.object:get_pos()
  21. local drops = item_list
  22. local drop = drops[math.random(#drops)]
  23. local chance = 1
  24. if type(drop) == "table" then
  25. chance = drop[2] or 1
  26. drop = drop[1]
  27. end
  28. if not minetest.registered_items[drop]
  29. or math.random(chance) > 1 then
  30. drop = "default:clay_lump"
  31. end
  32. local obj = minetest.add_item(pos, {name = drop})
  33. local dir = player:get_look_dir()
  34. obj:set_velocity({x = -dir.x, y = 1.5, z = -dir.z})
  35. return true
  36. end
  37. -- check for simple_dialogs mod and setup
  38. local context = {}
  39. mobs_npc.useDialogs = "N"
  40. minetest.register_on_leaveplayer(function(player)
  41. context[player:get_player_name()] = nil
  42. end)
  43. if minetest.get_modpath("simple_dialogs") then
  44. mobs_npc.useDialogs = "Y"
  45. simple_dialogs.register_varloader(function(npcself, playername)
  46. simple_dialogs.save_dialog_var(npcself, "NPCNAME", npcself.nametag, playername)
  47. simple_dialogs.save_dialog_var(npcself, "STATE", npcself.state, playername)
  48. simple_dialogs.save_dialog_var(npcself, "FOOD", npcself.food, playername)
  49. simple_dialogs.save_dialog_var(npcself, "HEALTH", npcself.health, playername)
  50. simple_dialogs.save_dialog_var(npcself, "owner", npcself.owner, playername)
  51. end)
  52. simple_dialogs.register_hook(function(npcself, playername,hook)
  53. if hook.func == "TELEPORT" then
  54. if npcself.owner then
  55. --check to see if the player has 'bring' teleport privliges
  56. local player_privs = minetest.get_player_privs(npcself.owner)
  57. if player_privs["bring"] then
  58. --validate x,y,z coords
  59. if hook.parm and hook.parmcount and hook.parmcount > 2 then
  60. local pos = {
  61. x = tonumber(hook.parm[1]),
  62. y = tonumber(hook.parm[2]),
  63. z = tonumber(hook.parm[3])
  64. }
  65. if pos.x and pos.y and pos.z
  66. and pos.x > -31500 and pos.x < 31500
  67. and pos.y > -31500 and pos.y < 31500
  68. and pos.z > -31500 and pos.z < 31500 then
  69. local player = minetest.get_player_by_name(playername)
  70. if player then
  71. player:set_pos(pos) end
  72. end
  73. end
  74. end
  75. end
  76. return "EXIT"
  77. end
  78. end)
  79. end
  80. -- Kilarin's formspec functions
  81. function mobs_npc.get_controls_formspec(name, self)
  82. self.id = set_npc_id(self) -- make sure id is set
  83. local currentordermode = self.order
  84. local npcId = self.id
  85. local orderArray = {"wander", "stand", "follow"}
  86. local currentorderidx = 1
  87. for i = 1, 3 do --this seems like a clumsy way to do this
  88. if orderArray[i] == currentordermode then
  89. currentorderidx = i
  90. break
  91. end
  92. end
  93. -- Make npc controls formspec
  94. local text = "NPC Controls"
  95. local size = mobs_npc.useDialogs == "Y" and "size[15,10]" or "size[3.85,2.8]"
  96. local formspec = {
  97. size,
  98. "label[0.375,0.5;", minetest.formspec_escape(text), "]",
  99. "dropdown[0.375,1.25; 3,0.6;ordermode;wander,stand,follow;", currentorderidx, "]",
  100. "button[0.375,2;3,0.8;exit;Exit]"
  101. }
  102. if mobs_npc.useDialogs == "Y" then
  103. simple_dialogs.add_dialog_control_to_formspec(name, self, formspec, 0.375, 3.4)
  104. end
  105. table.concat(formspec, "")
  106. --store npc id in local context so we can use it when the form is returned
  107. context[name] = npcId
  108. return table.concat(formspec, "")
  109. end
  110. -- receive and do orders given through form
  111. minetest.register_on_player_receive_fields(function(player, formname, fields)
  112. local pname = player:get_player_name()
  113. if formname ~= "mobs_npc:controls" then
  114. if context[pname] then context[pname] = nil end
  115. return
  116. end
  117. local npcId = context[pname] or nil --get the npc id from local context
  118. local npcself = get_npcself_from_id(npcId)
  119. if npcself ~= nil then
  120. if fields["exit"] then
  121. minetest.close_formspec(pname, "mobs_npc:controls")
  122. elseif fields["ordermode"] then
  123. local pname = player:get_player_name()
  124. npcself.order = fields["ordermode"]
  125. if npcself.order == "wander" then
  126. -- minetest.chat_send_player(pname, S("NPC will wander."))
  127. elseif npcself.order == "stand" then
  128. npcself.state = "stand"
  129. npcself.attack = nil
  130. npcself:set_animation("stand")
  131. npcself:set_velocity(0)
  132. -- minetest.chat_send_player(pname, S("NPC stands still."))
  133. elseif npcself.order == "follow" then
  134. -- minetest.chat_send_player(pname, S("NPC will follow you."))
  135. end
  136. end
  137. if mobs_npc.useDialogs == "Y" then
  138. simple_dialogs.process_simple_dialog_control_fields(pname, npcself, fields)
  139. end
  140. end
  141. end)
  142. -- check if npc has id set otherwise create one
  143. function set_npc_id(npcself)
  144. if not npcself.id then
  145. npcself.id = (math.random(1, 1000) * math.random(1, 10000))
  146. .. npcself.name .. (math.random(1, 1000) ^ 2)
  147. end
  148. return npcself.id
  149. end
  150. --this function finds an npcself in the luaentities list given an npcId
  151. function get_npcself_from_id(npcId)
  152. if npcId == nil then return nil end
  153. for k, v in pairs(minetest.luaentities) do
  154. if v.object and v.id and v.id == npcId then
  155. return v
  156. end
  157. end
  158. end
  159. --This code comes almost exclusively from the trader and inventory of mobf, by Sapier.
  160. --The copyright notice below is from mobf:
  161. -------------------------------------------------------------------------------
  162. -- Mob Framework Mod by Sapier
  163. --
  164. -- You may copy, use, modify or do nearly anything except removing this
  165. -- copyright notice.
  166. -- And of course you are NOT allow to pretend you have written it.
  167. --
  168. --! @file inventory.lua
  169. --! @brief component containing mob inventory related functions
  170. --! @copyright Sapier
  171. --! @author Sapier
  172. --! @date 2013-01-02
  173. --
  174. --! @defgroup Inventory Inventory subcomponent
  175. --! @brief Component handling mob inventory
  176. --! @ingroup framework_int
  177. --! @{
  178. --
  179. -- Contact sapier a t gmx net
  180. -------------------------------------------------------------------------------
  181. -- This code has been heavily modified by isaiah658.
  182. -- Trades are saved in entity metadata so they always stay the same after
  183. -- initially being chosen. Also the formspec uses item image buttons instead of
  184. -- inventory slots.
  185. local function add_goods(self, race)
  186. local trade_index = 1
  187. local trades_already_added = {}
  188. local trader_pool_size = 10
  189. local item_pool_size = #race.items -- get number of items on list
  190. self.trades = {}
  191. if item_pool_size < trader_pool_size then
  192. trader_pool_size = item_pool_size
  193. end
  194. for i = 1, trader_pool_size do
  195. -- If there are more trades than the amount being added, they are
  196. -- randomly selected. If they are equal, there is no reason to randomly
  197. -- select them
  198. local random_trade = nil
  199. if item_pool_size == trader_pool_size then
  200. random_trade = i
  201. else
  202. while random_trade == nil do
  203. local num = math.random(item_pool_size)
  204. if trades_already_added[num] == nil then
  205. trades_already_added[num] = true
  206. random_trade = num
  207. end
  208. end
  209. end
  210. if math.random(0, 100) > race.items[random_trade][3] then
  211. self.trades[trade_index] = {
  212. race.items[random_trade][1],
  213. race.items[random_trade][2]
  214. }
  215. trade_index = trade_index + 1
  216. end
  217. end
  218. end
  219. function mobs_npc.shop_trade(self, clicker, race)
  220. self.id = set_npc_id(self) -- make sure id is set
  221. if not self.game_name then
  222. self.game_name = tostring(race.names[math.random(1, #race.names)])
  223. self.nametag = S("Trader @1", self.game_name)
  224. self.object:set_properties({
  225. nametag = self.nametag,
  226. nametag_color = "#00FF00"
  227. })
  228. end
  229. if self.trades == nil then
  230. add_goods(self, race)
  231. end
  232. local player = clicker:get_player_name() or ""
  233. minetest.chat_send_player(player,
  234. S("[NPC] <Trader @1> Hello, @2, have a look at my wares.",
  235. self.game_name, player))
  236. -- Make formspec trade list
  237. local formspec_trade_list = ""
  238. local x, y
  239. for i = 1, 10 do
  240. if self.trades[i] and self.trades[i] ~= "" then
  241. if i < 6 then
  242. x = 0.5
  243. y = i - 0.5
  244. else
  245. x = 4.5
  246. y = i - 5.5
  247. end
  248. formspec_trade_list = formspec_trade_list
  249. .. "item_image_button[".. x ..",".. y ..";1,1;"
  250. .. self.trades[i][2] .. ";prices#".. i .."#".. self.id ..";]"
  251. .. "item_image_button[".. x + 2 ..",".. y ..";1,1;"
  252. .. self.trades[i][1] .. ";goods#".. i .."#".. self.id ..";]"
  253. .. "image[".. x + 1 ..",".. y ..";1,1;gui_arrow_blank.png]"
  254. end
  255. end
  256. minetest.show_formspec(player, "mobs_npc:trade", "size[8,10]"
  257. .. default.gui_bg_img
  258. .. default.gui_slots
  259. .. "label[0.5,-0.1;" .. S("Trader @1's stock:", self.game_name) .. "]"
  260. .. formspec_trade_list
  261. .. "list[current_player;main;0,6;8,4;]"
  262. )
  263. end
  264. minetest.register_on_player_receive_fields(function(player, formname, fields)
  265. if formname ~= "mobs_npc:trade" then return end
  266. if fields then
  267. local trade = ""
  268. for k, v in pairs(fields) do
  269. trade = tostring(k)
  270. end
  271. local id = trade:split("#")[3]
  272. local self = nil
  273. if id ~= nil then
  274. for k, v in pairs(minetest.luaentities) do
  275. if v.object and v.id and v.id == id then
  276. self = v
  277. break
  278. end
  279. end
  280. end
  281. if self ~= nil then
  282. local trade_number = tonumber(trade:split("#")[2])
  283. if trade_number ~= nil and self.trades[trade_number] ~= nil then
  284. local price = self.trades[trade_number][2]
  285. local goods = self.trades[trade_number][1]
  286. local inv = player:get_inventory()
  287. if inv:contains_item("main", price) then
  288. inv:remove_item("main", price)
  289. local leftover = inv:add_item("main", goods)
  290. if leftover:get_count() > 0 then
  291. -- drop item(s) in front of player
  292. local droppos = player:get_pos()
  293. local dir = player:get_look_dir()
  294. droppos.x = droppos.x + dir.x
  295. droppos.z = droppos.z + dir.z
  296. minetest.add_item(droppos, leftover)
  297. end
  298. end
  299. end
  300. end
  301. end
  302. end)