init.lua 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544
  1. -- Player modifiers mod for Enyekala.
  2. -- Author of source code: MustTest/BlueBird51
  3. -- License of source code: MIT
  4. if not minetest.global_exists("pova") then pova = {} end
  5. pova.modpath = minetest.get_modpath("pova")
  6. pova.players = pova.players or {}
  7. pova.last_properties = pova.last_properties or {}
  8. -- Used to force table.sort() to be stable.
  9. -- This counter increases every time a modifier is added.
  10. pova.counter = pova.counter or 0
  11. -- Properties which pova must never modify (via pref:set_properties()).
  12. local properties_blacklist = {
  13. eye_height = true,
  14. physical = true,
  15. collide_with_objects = true,
  16. colors = true,
  17. use_texture_alpha = true,
  18. spritediv = true,
  19. initial_sprite_basepos = true,
  20. automatic_rotate = true,
  21. automatic_face_movement_dir = true,
  22. automatic_face_movement_max_rotation_per_sec = true,
  23. backface_culling = true,
  24. nametag = true,
  25. nametag_color = true,
  26. static_save = true,
  27. shaded = true,
  28. }
  29. -- Properties which pova may modify (via pref:set_properties()).
  30. local properties_whitelist = {
  31. hp_max = true,
  32. breath_max = true,
  33. zoom_fov = true,
  34. collisionbox = true,
  35. selectionbox = true,
  36. pointable = true,
  37. visual = true,
  38. visual_size = true,
  39. mesh = true,
  40. textures = true,
  41. is_visible = true,
  42. makes_footstep_sound = true,
  43. stepheight = true,
  44. glow = true,
  45. infotext = true,
  46. damage_texture_modifier = true,
  47. show_on_minimap = true,
  48. }
  49. local function filter_properties(data)
  50. local o = {}
  51. for k, v in pairs(data) do
  52. -- Blacklist takes precedence.
  53. if not properties_blacklist[k] then
  54. if properties_whitelist[k] then
  55. o[k] = v
  56. end
  57. end
  58. end
  59. return o
  60. end
  61. local function get_mode(mode)
  62. if type(mode) == "string" then
  63. local o = {
  64. op = mode,
  65. priority = 0,
  66. count = pova.counter,
  67. time = -1,
  68. }
  69. pova.counter = pova.counter + 1
  70. return o
  71. elseif type(mode) == "nil" then
  72. local o = {
  73. priority = 0,
  74. count = pova.counter,
  75. time = -1,
  76. }
  77. pova.counter = pova.counter + 1
  78. return o
  79. else
  80. local o = {
  81. op = mode.op or nil,
  82. priority = mode.priority or 0,
  83. count = pova.counter,
  84. time = mode.time or -1,
  85. }
  86. if o.priority < -999 then
  87. o.priority = -999
  88. end
  89. pova.counter = pova.counter + 1
  90. return o
  91. end
  92. end
  93. -- Get data for player, creating an initial table with default data if needed.
  94. local function get_player(pref)
  95. local players = pova.players
  96. local pname = pref:get_player_name()
  97. local data = players[pname]
  98. if not data then
  99. --minetest.log(dump(pref:get_physics_override()))
  100. --minetest.log(dump({pref:get_eye_offset()}))
  101. --minetest.log(dump(pref:get_properties()))
  102. --minetest.log(dump(pref:get_nametag_attributes()))
  103. -- Initial (default) modifiers MUST be at index 1 in each stack.
  104. -- Initial modifiers are all named "".
  105. players[pname] = {
  106. -- Physics stack.
  107. physics = {
  108. {name="", data=pref:get_physics_override(), mode=get_mode({priority=-1000})},
  109. },
  110. eye_offset = {
  111. {name="", data={pref:get_eye_offset()}, mode=get_mode({priority=-1000})},
  112. },
  113. properties = {
  114. {name="", data=filter_properties(pref:get_properties()), mode=get_mode({priority=-1000})},
  115. },
  116. nametag = {
  117. {name="", data=pref:get_nametag_attributes(), mode=get_mode({priority=-1000})},
  118. },
  119. }
  120. data = players[pname]
  121. end
  122. return data
  123. end
  124. -- Update the initial (default) entry in the player's named stack (index 1).
  125. local function set_initial_data(data, stack, newdata)
  126. local initial = data[stack][1].data
  127. for k, v in pairs(newdata) do
  128. initial[k] = v
  129. end
  130. end
  131. local function stable_sort(a, b)
  132. -- Highest priority entries move to the END of the array.
  133. -- The higher the priority value, the higher the priority (meaning it
  134. -- overrrides stuff with lower priority).
  135. if a.mode.priority < b.mode.priority then
  136. return true
  137. end
  138. -- If the priorities are equal, stable-sort according to counter.
  139. if a.mode.count < b.mode.count then
  140. return true
  141. end
  142. return false
  143. end
  144. local function do_sort(t)
  145. local v = table.copy(t)
  146. table.sort(v, stable_sort)
  147. return v
  148. end
  149. local function mult_visual_size(o, n)
  150. return {x=o.x * n.x, y=o.y * n.y, z=o.z * (n.z or n.x)}
  151. end
  152. -- Combine all modifiers in named stack to a single table. Numbers are
  153. -- multiplied together if meaningful to do so. Boolean flags and other data
  154. -- simply overwrite, with the data at the top of the player's stack taking
  155. -- precedence.
  156. local def_visual_size = {x=1, y=1, z=1}
  157. local function combine_data(data, stack)
  158. local o = {}
  159. if stack == "physics" then
  160. for k, v in ipairs(do_sort(data.physics)) do
  161. for i, j in pairs(v.data) do
  162. if type(j) == "number" then
  163. o[i] = (o[i] or 1.0) * j
  164. else
  165. -- Booleans, etc.
  166. o[i] = j
  167. end
  168. end
  169. end
  170. elseif stack == "eye_offset" then
  171. -- Note: 'eye_offset' is an ARRAY, not a key/value map.
  172. for k, v in ipairs(do_sort(data.eye_offset)) do
  173. for i, j in ipairs(v.data) do
  174. o[i] = j
  175. end
  176. end
  177. elseif stack == "properties" then
  178. for k, v in ipairs(do_sort(data.properties)) do
  179. if not v.mode.op then
  180. for i, j in pairs(v.data) do
  181. if i == "visual_size" then
  182. -- Visual size is *always* multiplied.
  183. o[i] = mult_visual_size(o[i] or def_visual_size, j)
  184. else
  185. o[i] = j
  186. end
  187. end
  188. elseif v.mode.op == "add" then
  189. for i, j in pairs(v.data) do
  190. if i == "visual_size" then
  191. -- Visual size is *always* multiplied.
  192. o[i] = mult_visual_size(o[i] or def_visual_size, j)
  193. else
  194. if type(j) == "number" then
  195. o[i] = (o[i] or 0.0) + j
  196. else
  197. o[i] = j
  198. end
  199. end
  200. end
  201. end
  202. end
  203. elseif stack == "nametag" then
  204. for k, v in ipairs(do_sort(data.nametag)) do
  205. for i, j in pairs(v.data) do
  206. o[i] = j
  207. end
  208. end
  209. end
  210. return o
  211. end
  212. local function equals(a, b)
  213. if a == b then
  214. return true
  215. end
  216. local t1 = type(a)
  217. local t2 = type(b)
  218. if t1 ~= t2 then
  219. return false
  220. end
  221. if t1 ~= "table" then
  222. return false
  223. end
  224. local key_set = {}
  225. for key1, value1 in pairs(a) do
  226. local value2 = b[key1]
  227. if value2 == nil or equals(value1, value2) == false then
  228. return false
  229. end
  230. key_set[key1] = true
  231. end
  232. -- Check if B contains any keys not found in A.
  233. for key2, _ in pairs(b) do
  234. if not key_set[key2] then return false end
  235. end
  236. return true
  237. end
  238. -- Combine all datums in this player's named stack, and apply them.
  239. local function update_player_data(pref, stack, data)
  240. --minetest.chat_send_all('updating!')
  241. if stack == "physics" then
  242. pref:set_physics_override(combine_data(data, stack))
  243. elseif stack == "eye_offset" then
  244. local v1, v2, v3 = unpack(combine_data(data, stack))
  245. pref:set_eye_offset(v1, v2, v3)
  246. elseif stack == "properties" then
  247. local pname = pref:get_player_name()
  248. local new_props = filter_properties(combine_data(data, stack))
  249. local old_props = pova.last_properties[pname] or {}
  250. local changed_props = {}
  251. for k, v in pairs(new_props) do
  252. if not equals(old_props[k], v) then
  253. changed_props[k] = v
  254. end
  255. end
  256. pref:set_properties(changed_props)
  257. pova.last_properties[pname] = new_props
  258. elseif stack == "nametag" then
  259. pref:set_nametag_attributes(combine_data(data, stack))
  260. end
  261. end
  262. -- Get currently active overrides (combining all modifiers in named stack).
  263. function pova.get_active_modifier(pref, stack)
  264. local data = get_player(pref)
  265. return combine_data(data, stack)
  266. end
  267. -- Set default overrides for the named stack. AVOID USING THIS FUNCTION WHEN
  268. -- POSSIBLE. If you call it, you usually need to call it again with original
  269. -- data in order to restore overrides to what they were before.
  270. function pova.set_override(pref, stack, overrides)
  271. local data = get_player(pref)
  272. set_initial_data(data, stack, overrides)
  273. update_player_data(pref, stack, data)
  274. end
  275. -- Add modifier to player's named stack. The modifier is added to the top.
  276. function pova.add_modifier(pref, stack, modifiers, name, mode)
  277. local data = get_player(pref)
  278. if name ~= "" and stack ~= "" then
  279. table.insert(data[stack], {name=name, data=modifiers, mode=get_mode(mode)})
  280. end
  281. update_player_data(pref, stack, data)
  282. end
  283. -- Set named modifier in the player's stack. The modifier is added if it doesn't
  284. -- exist, otherwise it is replaced. If the modifier data is COMPLETELY replaced;
  285. -- existing data is NOT combined with the new data.
  286. function pova.set_modifier(pref, stack, modifiers, name, mode)
  287. local data = get_player(pref)
  288. -- Do not allow setting the default data.
  289. if name ~= "" and stack ~= "" then
  290. local replaced = false
  291. for k, v in ipairs(data[stack]) do
  292. if v.name == name then
  293. v.data = modifiers
  294. replaced = true
  295. break
  296. end
  297. end
  298. if not replaced then
  299. table.insert(data[stack], {name=name, data=modifiers, mode=get_mode(mode)})
  300. end
  301. end
  302. update_player_data(pref, stack, data)
  303. end
  304. -- This is the same as 'pova.set_modifier()', except that if the modifier
  305. -- already exists, the new data is MERGED with the existing data, INSTEAD of
  306. -- totally replacing it. Useful if you want to update a modifier, while
  307. -- providing only a subset of its original data. However, the modifier will be
  308. -- created if it doesn't exist, and put at the top of the named stack. This
  309. -- function also allows you to change the modifer's mode table.
  310. function pova.update_modifier(pref, stack, modifiers, name, mode)
  311. local data = get_player(pref)
  312. -- Do not allow setting the default data.
  313. if name ~= "" and stack ~= "" then
  314. local replaced = false
  315. for k, v in ipairs(data[stack]) do
  316. if v.name == name then
  317. -- Merge new data with existing data, overwriting as needed.
  318. for i, j in pairs(modifiers) do
  319. v.data[i] = j
  320. end
  321. if mode then
  322. v.mode = get_mode(mode)
  323. end
  324. replaced = true
  325. break
  326. end
  327. end
  328. if not replaced then
  329. table.insert(data[stack], {name=name, data=modifiers, mode=get_mode(mode)})
  330. end
  331. end
  332. update_player_data(pref, stack, data)
  333. end
  334. -- Remove modifier by name from named stack. This undoes the effect of adding
  335. -- the named modifier. Does nothing if the named modifier does not exist in the
  336. -- named stack.
  337. function pova.remove_modifier(pref, stack, name)
  338. local data = get_player(pref)
  339. local removed = false
  340. -- Do not allow removing the initial overrides.
  341. if name ~= "" and stack ~= "" then
  342. for k, v in ipairs(data[stack]) do
  343. if v.name == name then
  344. table.remove(data[stack], k)
  345. removed = true
  346. break
  347. end
  348. end
  349. end
  350. if removed then
  351. update_player_data(pref, stack, data)
  352. end
  353. end
  354. -- Shall update modifier timers and remove expired ones.
  355. function pova.globalstep(dtime)
  356. local function work(t, i)
  357. local d = t[i].mode
  358. if d.time >= 0 then
  359. d.time = d.time - dtime
  360. if d.time < 0 then
  361. return false
  362. end
  363. end
  364. return true
  365. end
  366. for pname, data in pairs(pova.players) do
  367. for stack, array in pairs(data) do
  368. local _, c = utility.array_remove(array, work)
  369. if c > 0 then
  370. local pref = minetest.get_player_by_name(pname)
  371. update_player_data(pref, stack, data)
  372. end
  373. end
  374. end
  375. end
  376. -- Remove all modifiers when player leaves game! If there is a bug (and there
  377. -- will be bugs) this allows a player to reset all their modifiers to defaults.
  378. function pova.on_leaveplayer(pref)
  379. local pname = pref:get_player_name()
  380. pova.players[pname] = nil
  381. end
  382. function pova.dump_modifiers(pname, param)
  383. local pref = minetest.get_player_by_name(pname)
  384. if not pref then
  385. return
  386. end
  387. local pref2 = minetest.get_player_by_name(param)
  388. if pref2 and pref2:is_player() then
  389. pref = pref2
  390. end
  391. local tname = pref:get_player_name()
  392. minetest.chat_send_player(pname, "# Server: Dumping modifiers of <" .. rename.gpn(tname) .. ">.")
  393. minetest.chat_send_player(pname, "# Server: " .. ("="):rep(80))
  394. local function round_numbers(t)
  395. for k, v in pairs(t) do
  396. if type(v) == "number" then
  397. t[k] = tonumber(string.format("%.2f", v))
  398. elseif type(v) == "table" then
  399. round_numbers(v)
  400. end
  401. end
  402. end
  403. local data = get_player(pref)
  404. local function dump_stack(pname, data, stack)
  405. minetest.chat_send_player(pname, "# Server: === Dumping \"" .. stack .. "\" ===")
  406. minetest.chat_send_player(pname, "# Server:")
  407. local tb = do_sort(data[stack])
  408. for k, v in ipairs(tb) do
  409. local t = table.copy(v)
  410. round_numbers(t)
  411. local dumps = dump(t)
  412. dumps = dumps:gsub("\n", " ")
  413. dumps = dumps:gsub("%s+", " ")
  414. dumps = dumps:gsub(" = ", "=")
  415. minetest.chat_send_player(pname, "# Server: (" .. k .. "): " .. dumps)
  416. end
  417. minetest.chat_send_player(pname, "# Server:")
  418. end
  419. for k, v in pairs(data) do
  420. dump_stack(pname, data, k)
  421. end
  422. end
  423. if not pova.registered then
  424. pova.registered = true
  425. minetest.register_globalstep(function(...)
  426. return pova.globalstep(...)
  427. end)
  428. minetest.register_chatcommand("pova", {
  429. params = "[<player>]",
  430. description = "List modifiers of self or player.",
  431. privs = {server=true},
  432. func = function(...)
  433. return pova.dump_modifiers(...)
  434. end
  435. })
  436. minetest.register_on_leaveplayer(function(...)
  437. return pova.on_leaveplayer(...)
  438. end)
  439. -- Register mod reloadable.
  440. local c = "pova:core"
  441. local f = pova.modpath .. "/init.lua"
  442. reload.register_file(c, f, false)
  443. end