init.lua 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543
  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. if stack == "physics" then
  241. pref:set_physics_override(combine_data(data, stack))
  242. elseif stack == "eye_offset" then
  243. local v1, v2, v3 = unpack(combine_data(data, stack))
  244. pref:set_eye_offset(v1, v2, v3)
  245. elseif stack == "properties" then
  246. local pname = pref:get_player_name()
  247. local new_props = filter_properties(combine_data(data, stack))
  248. local old_props = pova.last_properties[pname] or {}
  249. local changed_props = {}
  250. for k, v in pairs(new_props) do
  251. if not equals(old_props[k], v) then
  252. changed_props[k] = v
  253. end
  254. end
  255. pref:set_properties(changed_props)
  256. pova.last_properties[pname] = new_props
  257. elseif stack == "nametag" then
  258. pref:set_nametag_attributes(combine_data(data, stack))
  259. end
  260. end
  261. -- Get currently active overrides (combining all modifiers in named stack).
  262. function pova.get_active_modifier(pref, stack)
  263. local data = get_player(pref)
  264. return combine_data(data, stack)
  265. end
  266. -- Set default overrides for the named stack. AVOID USING THIS FUNCTION WHEN
  267. -- POSSIBLE. If you call it, you usually need to call it again with original
  268. -- data in order to restore overrides to what they were before.
  269. function pova.set_override(pref, stack, overrides)
  270. local data = get_player(pref)
  271. set_initial_data(data, stack, overrides)
  272. update_player_data(pref, stack, data)
  273. end
  274. -- Add modifier to player's named stack. The modifier is added to the top.
  275. function pova.add_modifier(pref, stack, modifiers, name, mode)
  276. local data = get_player(pref)
  277. if name ~= "" and stack ~= "" then
  278. table.insert(data[stack], {name=name, data=modifiers, mode=get_mode(mode)})
  279. end
  280. update_player_data(pref, stack, data)
  281. end
  282. -- Set named modifier in the player's stack. The modifier is added if it doesn't
  283. -- exist, otherwise it is replaced. If the modifier data is COMPLETELY replaced;
  284. -- existing data is NOT combined with the new data.
  285. function pova.set_modifier(pref, stack, modifiers, name, mode)
  286. local data = get_player(pref)
  287. -- Do not allow setting the default data.
  288. if name ~= "" and stack ~= "" then
  289. local replaced = false
  290. for k, v in ipairs(data[stack]) do
  291. if v.name == name then
  292. v.data = modifiers
  293. replaced = true
  294. break
  295. end
  296. end
  297. if not replaced then
  298. table.insert(data[stack], {name=name, data=modifiers, mode=get_mode(mode)})
  299. end
  300. end
  301. update_player_data(pref, stack, data)
  302. end
  303. -- This is the same as 'pova.set_modifier()', except that if the modifier
  304. -- already exists, the new data is MERGED with the existing data, INSTEAD of
  305. -- totally replacing it. Useful if you want to update a modifier, while
  306. -- providing only a subset of its original data. However, the modifier will be
  307. -- created if it doesn't exist, and put at the top of the named stack. This
  308. -- function also allows you to change the modifer's mode table.
  309. function pova.update_modifier(pref, stack, modifiers, name, mode)
  310. local data = get_player(pref)
  311. -- Do not allow setting the default data.
  312. if name ~= "" and stack ~= "" then
  313. local replaced = false
  314. for k, v in ipairs(data[stack]) do
  315. if v.name == name then
  316. -- Merge new data with existing data, overwriting as needed.
  317. for i, j in pairs(modifiers) do
  318. v.data[i] = j
  319. end
  320. if mode then
  321. v.mode = get_mode(mode)
  322. end
  323. replaced = true
  324. break
  325. end
  326. end
  327. if not replaced then
  328. table.insert(data[stack], {name=name, data=modifiers, mode=get_mode(mode)})
  329. end
  330. end
  331. update_player_data(pref, stack, data)
  332. end
  333. -- Remove modifier by name from named stack. This undoes the effect of adding
  334. -- the named modifier. Does nothing if the named modifier does not exist in the
  335. -- named stack.
  336. function pova.remove_modifier(pref, stack, name)
  337. local data = get_player(pref)
  338. local removed = false
  339. -- Do not allow removing the initial overrides.
  340. if name ~= "" and stack ~= "" then
  341. for k, v in ipairs(data[stack]) do
  342. if v.name == name then
  343. table.remove(data[stack], k)
  344. removed = true
  345. break
  346. end
  347. end
  348. end
  349. if removed then
  350. update_player_data(pref, stack, data)
  351. end
  352. end
  353. -- Shall update modifier timers and remove expired ones.
  354. function pova.globalstep(dtime)
  355. local function work(t, i)
  356. local d = t[i].mode
  357. if d.time >= 0 then
  358. d.time = d.time - dtime
  359. if d.time < 0 then
  360. return false
  361. end
  362. end
  363. return true
  364. end
  365. for pname, data in pairs(pova.players) do
  366. for stack, array in pairs(data) do
  367. local _, c = utility.array_remove(array, work)
  368. if c > 0 then
  369. local pref = minetest.get_player_by_name(pname)
  370. update_player_data(pref, stack, data)
  371. end
  372. end
  373. end
  374. end
  375. -- Remove all modifiers when player leaves game! If there is a bug (and there
  376. -- will be bugs) this allows a player to reset all their modifiers to defaults.
  377. function pova.on_leaveplayer(pref)
  378. local pname = pref:get_player_name()
  379. pova.players[pname] = nil
  380. end
  381. function pova.dump_modifiers(pname, param)
  382. local pref = minetest.get_player_by_name(pname)
  383. if not pref then
  384. return
  385. end
  386. local pref2 = minetest.get_player_by_name(param)
  387. if pref2 and pref2:is_player() then
  388. pref = pref2
  389. end
  390. local tname = pref:get_player_name()
  391. minetest.chat_send_player(pname, "# Server: Dumping modifiers of <" .. rename.gpn(tname) .. ">.")
  392. minetest.chat_send_player(pname, "# Server: " .. ("="):rep(80))
  393. local function round_numbers(t)
  394. for k, v in pairs(t) do
  395. if type(v) == "number" then
  396. t[k] = tonumber(string.format("%.2f", v))
  397. elseif type(v) == "table" then
  398. round_numbers(v)
  399. end
  400. end
  401. end
  402. local data = get_player(pref)
  403. local function dump_stack(pname, data, stack)
  404. minetest.chat_send_player(pname, "# Server: === Dumping \"" .. stack .. "\" ===")
  405. minetest.chat_send_player(pname, "# Server:")
  406. local tb = do_sort(data[stack])
  407. for k, v in ipairs(tb) do
  408. local t = table.copy(v)
  409. round_numbers(t)
  410. local dumps = dump(t)
  411. dumps = dumps:gsub("\n", " ")
  412. dumps = dumps:gsub("%s+", " ")
  413. dumps = dumps:gsub(" = ", "=")
  414. minetest.chat_send_player(pname, "# Server: (" .. k .. "): " .. dumps)
  415. end
  416. minetest.chat_send_player(pname, "# Server:")
  417. end
  418. for k, v in pairs(data) do
  419. dump_stack(pname, data, k)
  420. end
  421. end
  422. if not pova.registered then
  423. pova.registered = true
  424. minetest.register_globalstep(function(...)
  425. return pova.globalstep(...)
  426. end)
  427. minetest.register_chatcommand("pova", {
  428. params = "[<player>]",
  429. description = "List modifiers of self or player.",
  430. privs = {server=true},
  431. func = function(...)
  432. return pova.dump_modifiers(...)
  433. end
  434. })
  435. minetest.register_on_leaveplayer(function(...)
  436. return pova.on_leaveplayer(...)
  437. end)
  438. -- Register mod reloadable.
  439. local c = "pova:core"
  440. local f = pova.modpath .. "/init.lua"
  441. reload.register_file(c, f, false)
  442. end