Move damage effect to API, add advanced damage checks, add dewpoint influence

This commit is contained in:
Till Affeldt 2020-04-28 01:22:27 +02:00
parent 9e0ed0c256
commit 4df9a61374
6 changed files with 295 additions and 79 deletions

View File

@ -57,8 +57,13 @@ Lower values can possible increase performance.
- ``Dynamically modify nodes`` (default true): - ``Dynamically modify nodes`` (default true):
If set to true, weather packs are allowed to register node update handlers. If set to true, weather packs are allowed to register node update handlers.
These can be used to dynamically place snow layers, melt ice, or hydrate soil. These can be used to dynamically place snow layers, melt ice, or hydrate soil.
- ``Include wind speed in damage checks`` (default true):
If set to true, Climate API will factor in wind speed and obstacles to determine damage sources.
If set to false, a simple check will be used whether the player is outside.
### Visuals ### Weather Effects
- ``Cause player damage`` (default true):
If set to true, dangerous weather presets will damage affected players over time.
- ``Show particle effects`` (default true): - ``Show particle effects`` (default true):
If set to true, weather effects (like rain) are allowed to render particles. If set to true, weather effects (like rain) are allowed to render particles.
Deactivating this feature will prevent some presets from being visible. Deactivating this feature will prevent some presets from being visible.

84
ca_effects/damage.lua Normal file
View File

@ -0,0 +1,84 @@
--[[
# Player Damage Effect
Use this effect to damage a player during dangerous weather events.
Expects a table as the parameter containing the following values:
- value <int> [1]: The amount of damage to be applied per successful roll.
- chance <int> [1]: Defines a 1/x roll per cycle for the player to get damaged. Higher values result in less frequent damage.
- check <table> [nil]: Use an additional outdoors check before applying damage. Consists of the following values:
- type <"light"|"raycast"> ["light"] (Whether the light level should be used a raycast should be performed)
- height <number> [0] (Height offset of weather origin from the player. Only used for raycasts)
- velocity <number> [1] (Velocity of damaging particles. Only used for raycasts)
- use_wind <bool> [true] (Whether the wind should be factored in. Only used for raycasts)
]]
if not minetest.is_yes(minetest.settings:get_bool("enable_damage"))
or not climate_mod.settings.damage then return end
local EFFECT_NAME = "climate_api:damage"
local rng = PcgRandom(7819792)
local function check_hit(player, ray)
local ppos = vector.add(player:get_pos(), {x=0, y=1, z=0})
if ray.type ~= nil and ray.type ~= "light" and ray.type ~= "raycast" then
minetest.log("warning", "[Climate API] Invalid damage check configuration")
return false
end
-- use light level if specified or in performance mode
if ray.type == nil
or ray.type == "light"
or not climate_mod.settings.raycast then
return minetest.get_node_light(ppos, 0.5) == 15
end
-- use raycating to factor in wind speed
local origin = vector.add(ppos, {x = 0, y = ray.height or 0, z = 0 })
if ray.use_wind ~= false then
local wind = climate_api.environment.get_wind()
local velocity = ray.velocity or 1
local windpos = vector.multiply(
vector.normalize(vector.add({ x = 0, y = -velocity, z = 0 }, wind)),
-vector.length(wind)
)
origin = vector.add(origin, windpos)
end
local ray = minetest.raycast(origin, ppos)
local obj = ray:next()
-- found nothing
if obj == nil then return false end
-- found node
if obj.type ~= "object" then return false end
-- found different entity
if not obj.ref:is_player() then return false end
-- found another player
if obj.ref:get_player_name() ~= player:get_player_name() then return false end
return true
end
local function calc_damage(player, dmg)
if dmg.value == nil then dmg.value = 1 end
if dmg.chance == nil then dmg.chance = 1 end
-- check if damage should be applied
if rng:next(1, dmg.chance) ~= 1 then return 0 end
if dmg.check ~= nil then
-- check for obstacles in the way
if not check_hit(player, dmg.check) then return 0 end
end
return dmg.value
end
local function handle_effect(player_data)
for playername, data in pairs(player_data) do
local player = minetest.get_player_by_name(playername)
local hp = player:get_hp()
for weather, dmg in pairs(data) do
hp = hp - calc_damage(player, dmg)
end
-- deal damage to player
player:set_hp(hp, "weather damage")
end
end
climate_api.register_effect(EFFECT_NAME, handle_effect, "tick")
climate_api.set_effect_cycle(EFFECT_NAME, climate_api.MEDIUM_CYCLE)

View File

@ -1,96 +1,198 @@
--[[ --[[
# Particle Effect # Particle Effect
Use this effect to render downfall using particles. Use this effect to render downfall or similar visuals using particles.
Expects a table as the parameter containing the following values: Expects a table as the parameter containing information for the spawner.
- amount <number>: The quantity of spawned particles per cycle All values for ParticleSpawner definitions are valid.
- EITHER texture <string>: The image file name See https://minetest.gitlab.io/minetest/definition-tables/#particlespawner-definition
- OR textures <table>: A list of possible texture variants
- falling_speed <number>: The downwards speed Furthermore, the following default values have been changed:
- min_pos <number>: Bottom-left corner of spawn position (automatically adjusted by wind) - time <int> [0.5] (reduced time results in smoother position updates, but more lag)
- max_pos <number>: Top-right corner of spawn position (automatically adjusted by wind) - collisiondetection <bool> [true]
- acceleration <vector> (optional): Particle acceleration in any direction - collision_removal <bool> [true]
- exptime <number>: Time of life of particles - playername <string> [current player] (Set to empty string to show for everyone)
- time <number> (optional): The time of life of particle spawners (defaults to 0.5)
- EITHER size <number>: Size of the particles The following optional values have been introduced or expanded for convenience:
- OR min_size <number> and max_size <number>: Minimum and maximum size - size <int> [nil] (Overrides both minsize and maxsize if set)
- vertical <bool> (optional): Whether particles should rotate in 2D space only (default depends on falling vector) - boxsize <vector> [nil] (Overrides minpos and maxpos based on specified sizes per direction with the player in the center)
- boxsize <number> [nil] (If set to a number, the resulting vector will have the specified size in all directions)
- v_offset <int> [0] (Use in conjunctin with boxsize. Adds specified height to minpos and maxpos y-coordinates)
- minvel <int> [nil] (Overrides minvel with a downward facing vector of specified length)
- maxvel <int> [nil] (Overrides maxvel with a downward facing vector of specified length)
- velocity <vector | int> [nil] (Overrides both minvel and maxvel if set)
- minacc <int> [nil] (Overrides minacc with a downward facing vector of specified length)
- maxacc <int> [nil] (Overrides maxacc with a downward facing vector of specified length)
- acceleration <vector | int> [nil] (Overrides both minacc and maxacc if set)
- attach_to_player <bool> [false] (Overrides attached object with current player)
The following new behaviours have been introduced:
- use_wind <bool> [true] (Adjusts velocity and position for current windspeed)
- detached <bool> [false] (Unless enabled, considers positions as relative to current player like being attached)
- vertical <bool> [nil] (Unless explicitly set, will be automatically set based on direction of velocity)
]] ]]
if not climate_mod.settings.particles then return end if not climate_mod.settings.particles then return end
local EFFECT_NAME = "climate_api:particles" local EFFECT_NAME = "climate_api:particles"
local function get_particle_texture(particles) -- parse config by injecting default values and adding additional parameters
if type(particles.textures) == "nil" or next(particles.textures) == nil then local function parse_config(player, particles)
return particles.texture -- override default values with more useful ones
end local defaults = {
return particles.textures[math.random(#particles.textures)] time = 0.5,
end
local function spawn_particles(player, particles)
local ppos = player:get_pos()
local wind = climate_api.environment.get_wind()
local amount = particles.amount * climate_mod.settings.particle_count
local texture = get_particle_texture(particles)
local vel = vector.new({
x = wind.x,
y = -particles.falling_speed,
z = wind.z
})
if particles.acceleration == nil then
particles.acceleration = vector.new({x=0, y=0, z=0})
end
local wind_pos = vector.multiply(
vector.normalize(vel),
-vector.length(wind)
)
wind_pos.y = 0
local minp = vector.add(vector.add(ppos, particles.min_pos), wind_pos)
local maxp = vector.add(vector.add(ppos, particles.max_pos), wind_pos)
if particles.time == nil then
particles.time = 0.5
end
if particles.vertical == nil then
particles.vertical = math.abs(vector.normalize(vel).y) >= 0.6
end
if particles.size ~= nil then
particles.min_size = particles.size
particles.max_size = particles.size
end
minetest.add_particlespawner({
amount = amount,
time = particles.time,
minpos = minp,
maxpos = maxp,
minvel = vel,
maxvel = vel,
minacc = particles.acceleration,
maxacc = particles.acceleration,
minexptime = particles.exptime,
maxexptime = particles.exptime,
minsize = particles.min_size,
maxsize = particles.max_size,
collisiondetection = true, collisiondetection = true,
collision_removal = true, collision_removal = true,
vertical = particles.vertical, playername = player:get_player_name(),
texture = texture, use_wind = true,
player = player:get_player_name() attach_to_player = false,
}) detached = false
}
-- inject missing default values into specified config
local config = climate_api.utility.merge_tables(defaults, particles)
-- scale particle amount based on mod config
if particles.amount ~= nil then
config.amount = particles.amount * climate_mod.settings.particle_count
end
-- restore default visibility if specified
if particles.playername == "" then
config.playername = nil
end
-- provide easier param for exptime
if particles.expirationtime ~= nil then
config.minexptime = particles.expirationtime
config.maxexptime = particles.expirationtime
config.expirationtime = nil
end
-- provide easier param for size
if particles.size ~= nil then
config.minsize = particles.size
config.maxsize = particles.size
config.size = nil
end
-- randomly select a texture when given a table
if type(particles.texture) == "table" then
config.texture = particles.texture[math.random(#particles.texture)]
end
if particles.pos ~= nil then
config.minpos = particles.pos
config.maxpos = particles.pos
config.pos = nil
end
-- provide easier size based param for position
if type(particles.boxsize) == "number" then
particles.boxsize = {
x = particles.boxsize,
y = particles.boxsize,
z = particles.boxsize
}
end
if particles.boxsize ~= nil then
local size_x = particles.boxsize.x or 0
local size_y = particles.boxsize.y or 0
local size_z = particles.boxsize.z or 0
local v_offset = particles.v_offset or 0
v_offset = v_offset + (size_y / 2)
config.minpos = {
x = -size_x / 2,
y = v_offset - (size_y / 2),
z = -size_z / 2
}
config.maxpos = {
x = size_x / 2,
y = v_offset + (size_y / 2),
z = size_z / 2
}
config.size_x = nil
config.size_y = nil
config.size_z = nil
config.v_offset = nil
end
-- provide easy param to define unanimous falling speed
if particles.velocity ~= nil then
particles.minvel = particles.velocity
particles.maxvel = particles.velocity
config.velocity = nil
end
if type(particles.minvel) == "number" then
config.minvel = { x = 0, y = -particles.minvel, z = 0 }
end
if type(particles.maxvel) ~= nil then
config.maxvel = { x = 0, y = -particles.maxvel, z = 0 }
end
-- provide easy param to define unanimous falling acceleration
if particles.acceleration ~= nil then
particles.minacc = particles.acceleration
particles.maxacc = particles.acceleration
config.acceleration = nil
end
if type(particles.minacc) == "number" then
config.minacc = { x = 0, y = -particles.minacc, z = 0 }
end
if type(particles.maxacc) == "number" then
config.maxacc = { x = 0, y = -particles.maxacc, z = 0 }
end
-- attach particles to current player if specified
if config.attach_to_player then
config.attached = player
end
config.attach_to_player = nil
-- attach coordinates to player unless specified or already attached
if (not config.detached) and config.attached == nil then
local ppos = player:get_pos()
config.minpos = vector.add(config.minpos, ppos)
config.maxpos = vector.add(config.maxpos, ppos)
end
config.detached = nil
-- move particles in wind direction
if config.use_wind then
local wind = climate_api.environment.get_wind()
-- adjust velocity to include wind
config.minvel = vector.add(config.minvel, wind)
config.maxvel = vector.add(config.maxvel, wind)
-- adjust spawn position for better visibility
local vel = vector.multiply(vector.add(config.minvel, config.maxvel), 0.5)
local windpos = vector.multiply(
vector.normalize(vel),
-vector.length(wind)
)
config.minpos = vector.add(config.minpos, windpos)
config.maxpos = vector.add(config.maxpos, windpos)
end
config.use_wind = nil
-- if unspecified, use 2D or 3D rotation based on movement direction
if particles.vertical == nil then
local vel = vector.multiply(vector.add(config.minvel, config.maxvel), 0.5)
config.vertical = math.abs(vector.normalize(vel).y) >= 0.6
end
return config
end end
local function handle_effect(player_data) local function handle_effect(player_data)
for playername, data in pairs(player_data) do for playername, data in pairs(player_data) do
local player = minetest.get_player_by_name(playername) local player = minetest.get_player_by_name(playername)
for weather, value in pairs(data) do for weather, value in pairs(data) do
spawn_particles(player, value) local config = parse_config(player, value)
minetest.add_particlespawner(config)
end end
end end
end end

View File

@ -25,6 +25,8 @@ end
-- load settings from config file -- load settings from config file
climate_mod.settings = { climate_mod.settings = {
damage = get_setting_bool("damage", true),
raycast = get_setting_bool("raycast", true),
particles = get_setting_bool("particles", true), particles = get_setting_bool("particles", true),
skybox = get_setting_bool("skybox", true), skybox = get_setting_bool("skybox", true),
sound = get_setting_bool("sound", true), sound = get_setting_bool("sound", true),
@ -72,6 +74,7 @@ dofile(modpath.."/lib/commands.lua")
dofile(modpath .. "/lib/influences.lua") dofile(modpath .. "/lib/influences.lua")
-- import predefined environment effects -- import predefined environment effects
dofile(modpath .. "/ca_effects/damage.lua")
dofile(modpath .. "/ca_effects/hud_overlay.lua") dofile(modpath .. "/ca_effects/hud_overlay.lua")
dofile(modpath .. "/ca_effects/particles.lua") dofile(modpath .. "/ca_effects/particles.lua")
dofile(modpath .. "/ca_effects/skybox.lua") dofile(modpath .. "/ca_effects/skybox.lua")

View File

@ -14,6 +14,19 @@ climate_api.register_influence("base_humidity", function(pos)
return minetest.get_humidity(pos) return minetest.get_humidity(pos)
end) end)
-- see https://en.wikipedia.org/wiki/Dew_point#Simple_approximation
climate_api.register_influence("dewpoint", function(pos)
local heat = climate_api.environment.get_heat(pos)
local humidity = climate_api.environment.get_humidity(pos)
return heat - (9/25 * (100 - humidity))
end)
climate_api.register_influence("base_dewpoint", function(pos)
local heat = minetest.get_heat(pos)
local humidity = minetest.get_humidity(pos)
return heat - (9/25 * (100 - humidity))
end)
climate_api.register_influence("biome", function(pos) climate_api.register_influence("biome", function(pos)
local data = minetest.get_biome_data(pos) local data = minetest.get_biome_data(pos)
local biome = minetest.get_biome_name(data.biome) local biome = minetest.get_biome_name(data.biome)

View File

@ -14,8 +14,17 @@ climate_api_particle_count (Multiplicator for used particles) float 1 0.1 2
# These can be used to dynamically place snow layers, melt ice, or hydrate soil. # These can be used to dynamically place snow layers, melt ice, or hydrate soil.
climate_api_block_updates (Dynamically modify nodes) bool true climate_api_block_updates (Dynamically modify nodes) bool true
# If set to true, Climate API will factor in wind speed and obstacles to determine damage sources.
# If set to false, a simple check will be used whether the player is outside.
# Only applied if climate_api_damage is also set to true.
climate_api_raycast (Include wind speed in damage checks) bool true
[Weather Effects]
# If set to true, dangerous weather presets will damage affected players over time.
climate_api_damage (Cause player damage) bool true
[Visuals]
# If set to true, weather effects (like rain) are allowed to render particles. # If set to true, weather effects (like rain) are allowed to render particles.
# Deactivating this feature will prevent some presets from being visible. # Deactivating this feature will prevent some presets from being visible.
# For performance considerations it is recommended to decrease the amount of particles instead. # For performance considerations it is recommended to decrease the amount of particles instead.