damage.lua 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. --[[
  2. # Player Damage Effect
  3. Use this effect to damage a player during dangerous weather events.
  4. Expects a table as the parameter containing the following values:
  5. - value <int> [1]: The amount of damage to be applied per successful roll.
  6. - rarity <int> [1]: Defines a 1/x chance per cycle for the player to get damaged. Higher values result in less frequent damage.
  7. - check <table> [nil]: Use an additional outdoors check before applying damage. Consists of the following values:
  8. - type <"light"|"raycast"> ["light"] (Whether the light level should be used a raycast should be performed)
  9. - height <number> [0] (Height offset of weather origin from the player. Only used for raycasts)
  10. - velocity <number> [1] (Velocity of damaging particles. Only used for raycasts)
  11. - use_wind <bool> [true] (Whether the wind should be factored in. Only used for raycasts)
  12. ]]
  13. if not minetest.is_yes(minetest.settings:get_bool("enable_damage"))
  14. or not climate_mod.settings.damage then return end
  15. local EFFECT_NAME = "climate_api:damage"
  16. local rng = PcgRandom(7819792)
  17. local function check_hit(player, ray)
  18. local ppos = vector.add(player:get_pos(), {x=0, y=1, z=0})
  19. if ray.type ~= nil and ray.type ~= "light" and ray.type ~= "raycast" then
  20. minetest.log("warning", "[Climate API] Invalid damage check configuration")
  21. return false
  22. end
  23. -- use light level if specified or in performance mode
  24. if ray.type == nil
  25. or ray.type == "light"
  26. or not climate_mod.settings.raycast then
  27. return minetest.get_node_light(ppos, 0.5) == 15
  28. end
  29. -- use raycating to factor in wind speed
  30. local origin = vector.add(ppos, {x = 0, y = ray.height or 0, z = 0 })
  31. if ray.use_wind ~= false then
  32. local wind = climate_api.environment.get_wind(origin)
  33. local velocity = ray.velocity or 1
  34. local windpos = vector.multiply(
  35. vector.normalize(vector.add({ x = 0, y = -velocity, z = 0 }, wind)),
  36. -vector.length(wind)
  37. )
  38. origin = vector.add(origin, windpos)
  39. end
  40. local ray = minetest.raycast(origin, ppos)
  41. local obj = ray:next()
  42. -- found nothing
  43. if obj == nil then return false end
  44. -- found node
  45. if obj.type ~= "object" then return false end
  46. -- found different entity
  47. if not obj.ref:is_player() then return false end
  48. -- found another player
  49. if obj.ref:get_player_name() ~= player:get_player_name() then return false end
  50. return true
  51. end
  52. local function calc_damage(player, dmg)
  53. if dmg.value == nil then dmg.value = 1 end
  54. if dmg.rarity == nil then dmg.rarity = 1 end
  55. -- check if damage should be applied
  56. if rng:next(1, dmg.rarity) ~= 1 then return 0 end
  57. if dmg.check ~= nil then
  58. -- check for obstacles in the way
  59. if not check_hit(player, dmg.check) then return 0 end
  60. end
  61. return dmg.value
  62. end
  63. local function handle_effect(player_data)
  64. for playername, data in pairs(player_data) do
  65. local player = minetest.get_player_by_name(playername)
  66. local hp = player:get_hp()
  67. for weather, dmg in pairs(data) do
  68. hp = hp - calc_damage(player, dmg)
  69. end
  70. -- deal damage to player
  71. player:set_hp(hp, "weather damage")
  72. end
  73. end
  74. climate_api.register_effect(EFFECT_NAME, handle_effect, "tick")