Vanessa Dannenberg
2018-10-31 44cb8df048e09b64214f59db73a3fd23cfe12e77
technic/radiation.lua
@@ -54,8 +54,15 @@
   ["default:lava_source"] = 17,
   ["default:mese"] = 21,
   ["default:mossycobble"] = 15,
   ["default:nyancat"] = 1000,
   ["default:nyancat_rainbow"] = 1000,
   ["default:tinblock"] = 37,
   ["pbj_pup:pbj_pup"] = 10000,
   ["pbj_pup:pbj_pup_candies"] = 10000,
   ["gloopblocks:rainbow_block_diagonal"] = 5000,
   ["gloopblocks:rainbow_block_horizontal"] = 10000,
   ["default:nyancat"] = 10000,
   ["default:nyancat_rainbow"] = 10000,
   ["nyancat:nyancat"] = 10000,
   ["nyancat:nyancat_rainbow"] = 10000,
   ["default:obsidian"] = 18,
   ["default:obsidian_glass"] = 18,
   ["default:sand"] = 10,
@@ -70,6 +77,7 @@
   ["default:stone_with_gold"] = 34,
   ["default:stone_with_iron"] = 20,
   ["default:stone_with_mese"] = 17,
   ["default:stone_with_tin"] = 19,
   ["default:stonebrick"] = 17,
   ["default:water_flowing"] = 2.8,
   ["default:water_source"] = 5.6,
@@ -135,12 +143,10 @@
   ["moreblocks:wood_tile_up"] = 1.7,
   ["moreores:mineral_mithril"] = 18,
   ["moreores:mineral_silver"] = 21,
   ["moreores:mineral_tin"] = 19,
   ["moreores:mithril_block"] = 26,
   ["moreores:silver_block"] = 53,
   ["moreores:tin_block"] = 37,
   ["snow:snow_brick"] = 2.8,
   ["technic:brass_block"] = 43,
   ["basic_materials:brass_block"] = 43,
   ["technic:carbon_steel_block"] = 40,
   ["technic:cast_iron_block"] = 40,
   ["technic:chernobylite_block"] = 40,
@@ -230,30 +236,23 @@
formula scales down the difference between shielded and unshielded
safe distances, avoiding the latter becoming impractically large.
Damage is processed at rates down to 0.25 HP/s, which in the absence of
Damage is processed at rates down to 0.2 HP/s, which in the absence of
shielding is attained at the distance specified by the "radioactive"
group value.  Computed damage rates below 0.25 HP/s result in no
group value.  Computed damage rates below 0.2 HP/s result in no
damage at all to the player.  This gives the player an opportunity
to be safe, and limits the range at which source/player interactions
need to be considered.
--]]
local abdomen_offset = 1
local cache_scaled_shielding = {}
local rad_dmg_cutoff = 0.25
local rad_dmg_cutoff = 0.2
local radiated_players = {}
local function dmg_player(pos, o, strength)
   local pl_pos = o:getpos()
   pl_pos.y = pl_pos.y + abdomen_offset
   local shielding = 0
   local dist = vector.distance(pos, pl_pos)
   for ray_pos in technic.trace_node_ray(pos,
         vector.direction(pos, pl_pos), dist) do
      local shield_name = minetest.get_node(ray_pos).name
      shielding = shielding + node_radiation_resistance(shield_name) * 0.1
   end
   local dmg = (strength * strength) /
      (math.max(0.75, dist * dist) * math.exp(shielding))
   if dmg < rad_dmg_cutoff then return end
local armor_enabled = technic.config:get_bool("enable_radiation_protection")
local entity_damage = technic.config:get_bool("enable_entity_radiation_damage")
local longterm_damage = technic.config:get_bool("enable_longterm_radiation_damage")
local function apply_fractional_damage(o, dmg)
   local dmg_int = math.floor(dmg)
   -- The closer you are to getting one more damage point,
   -- the more likely it will be added.
@@ -261,7 +260,75 @@
      dmg_int = dmg_int + 1
   end
   if dmg_int > 0 then
      o:set_hp(math.max(o:get_hp() - dmg_int, 0))
      local new_hp = math.max(o:get_hp() - dmg_int, 0)
      o:set_hp(new_hp)
      return new_hp == 0
   end
   return false
end
local function calculate_base_damage(node_pos, object_pos, strength)
   local shielding = 0
   local dist = vector.distance(node_pos, object_pos)
   for ray_pos in technic.trace_node_ray(node_pos,
         vector.direction(node_pos, object_pos), dist) do
      local shield_name = minetest.get_node(ray_pos).name
      shielding = shielding + node_radiation_resistance(shield_name) * 0.025
   end
   local dmg = (strength * strength) /
      (math.max(0.75, dist * dist) * math.exp(shielding))
   if dmg < rad_dmg_cutoff then return end
   return dmg
end
local function calculate_damage_multiplier(object)
   local ag = object.get_armor_groups and object:get_armor_groups()
   if not ag then
      return 0
   end
   if ag.immortal then
      return 0
   end
   if ag.radiation then
      return 0.01 * ag.radiation
   end
   if ag.fleshy then
      return math.sqrt(0.01 * ag.fleshy)
   end
   return 0
end
local function calculate_object_center(object)
   if object:is_player() then
      return {x=0, y=abdomen_offset, z=0}
   end
   return {x=0, y=0, z=0}
end
local function dmg_object(pos, object, strength)
   local obj_pos = vector.add(object:getpos(), calculate_object_center(object))
   local mul
   if armor_enabled or entity_damage then
      -- we need to check may the object be damaged even if armor is disabled
      mul = calculate_damage_multiplier(object)
      if mul == 0 then
         return
      end
   end
   local dmg = calculate_base_damage(pos, obj_pos, strength)
   if not dmg then
      return
   end
   if armor_enabled then
      dmg = dmg * mul
   end
   apply_fractional_damage(object, dmg)
   if longterm_damage and object:is_player() then
      local pn = object:get_player_name()
      radiated_players[pn] = (radiated_players[pn] or 0) + dmg
   end
end
@@ -271,20 +338,43 @@
   local max_dist = strength * rad_dmg_mult_sqrt
   for _, o in pairs(minetest.get_objects_inside_radius(pos,
         max_dist + abdomen_offset)) do
      if o:is_player() then
         dmg_player(pos, o, strength)
      if entity_damage or o:is_player() then
         dmg_object(pos, o, strength)
      end
   end
end
if minetest.setting_getbool("enable_damage") then
if minetest.settings:get_bool("enable_damage") then
   minetest.register_abm({
      label = "Radiation damage",
      nodenames = {"group:radioactive"},
      interval = 1,
      chance = 1,
      action = dmg_abm,
   })
   if longterm_damage then
      minetest.register_globalstep(function(dtime)
         for pn, dmg in pairs(radiated_players) do
            dmg = dmg - (dtime / 8)
            local player = minetest.get_player_by_name(pn)
            local killed
            if player and dmg > rad_dmg_cutoff then
               killed = apply_fractional_damage(player, (dmg * dtime) / 8)
            else
               dmg = nil
            end
            -- on_dieplayer will have already set this if the player died
            if not killed then
               radiated_players[pn] = dmg
            end
         end
      end)
      minetest.register_on_dieplayer(function(player)
         radiated_players[player:get_player_name()] = nil
      end)
   end
end
-- Radioactive materials that can result from destroying a reactor
@@ -294,7 +384,7 @@
   minetest.register_node("technic:corium_"..state, {
      description = S(state == "source" and "Corium Source" or "Flowing Corium"),
      drawtype = (state == "source" and "liquid" or "flowingliquid"),
      [state == "source" and "tiles" or "special_tiles"] = {{
      tiles = {{
         name = "technic_corium_"..state.."_animated.png",
         animation = {
            type = "vertical_frames",
@@ -303,6 +393,28 @@
            length = 3.0,
         },
      }},
      special_tiles = {
         {
            name = "technic_corium_"..state.."_animated.png",
            backface_culling = false,
            animation = {
               type = "vertical_frames",
               aspect_w = 16,
               aspect_h = 16,
               length = 3.0,
            },
         },
         {
            name = "technic_corium_"..state.."_animated.png",
            backface_culling = true,
            animation = {
               type = "vertical_frames",
               aspect_w = 16,
               aspect_h = 16,
               length = 3.0,
            },
         },
      },
      paramtype = "light",
      paramtype2 = (state == "flowing" and "flowingliquid" or nil),
      light_source = (state == "source" and 8 or 5),
@@ -323,7 +435,7 @@
         liquid = 2,
         hot = 3,
         igniter = (griefing and 1 or 0),
         radioactive = (state == "source" and 16 or 8),
         radioactive = (state == "source" and 12 or 6),
         not_in_creative_inventory = (state == "flowing" and 1 or nil),
      },
   })
@@ -343,12 +455,13 @@
        description = S("Chernobylite Block"),
   tiles = {"technic_chernobylite_block.png"},
   is_ground_content = true,
   groups = {cracky=1, radioactive=6, level=2},
   groups = {cracky=1, radioactive=4, level=2},
   sounds = default.node_sound_stone_defaults(),
   light_source = 2,
})
minetest.register_abm({
   label = "Corium: boil-off water (sources)",
   nodenames = {"group:water"},
   neighbors = {"technic:corium_source"},
   interval = 1,
@@ -359,6 +472,7 @@
})
minetest.register_abm({
   label = "Corium: boil-off water (flowing)",
   nodenames = {"technic:corium_flowing"},
   neighbors = {"group:water"},
   interval = 1,
@@ -369,6 +483,7 @@
})
minetest.register_abm({
   label = "Corium: become chernobylite",
   nodenames = {"technic:corium_flowing"},
   interval = 5,
   chance = (griefing and 10 or 1),
@@ -379,6 +494,7 @@
if griefing then
   minetest.register_abm({
      label = "Corium: griefing",
      nodenames = {"technic:corium_source", "technic:corium_flowing"},
      interval = 4,
      chance = 4,