mirror of
https://github.com/t-affeldt/climate_api.git
synced 2024-12-22 17:00:36 +01:00
Move damage effect to API, add advanced damage checks, add dewpoint influence
This commit is contained in:
parent
9e0ed0c256
commit
4df9a61374
@ -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
84
ca_effects/damage.lua
Normal 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)
|
@ -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,
|
||||||
|
collisiondetection = true,
|
||||||
|
collision_removal = true,
|
||||||
|
playername = player:get_player_name(),
|
||||||
|
use_wind = true,
|
||||||
|
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
|
end
|
||||||
|
|
||||||
local function spawn_particles(player, particles)
|
-- 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()
|
local ppos = player:get_pos()
|
||||||
local wind = climate_api.environment.get_wind()
|
config.minpos = vector.add(config.minpos, ppos)
|
||||||
|
config.maxpos = vector.add(config.maxpos, ppos)
|
||||||
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
|
end
|
||||||
|
config.detached = nil
|
||||||
|
|
||||||
local wind_pos = vector.multiply(
|
-- 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.normalize(vel),
|
||||||
-vector.length(wind)
|
-vector.length(wind)
|
||||||
)
|
)
|
||||||
wind_pos.y = 0
|
config.minpos = vector.add(config.minpos, windpos)
|
||||||
local minp = vector.add(vector.add(ppos, particles.min_pos), wind_pos)
|
config.maxpos = vector.add(config.maxpos, windpos)
|
||||||
local maxp = vector.add(vector.add(ppos, particles.max_pos), wind_pos)
|
|
||||||
|
|
||||||
if particles.time == nil then
|
|
||||||
particles.time = 0.5
|
|
||||||
end
|
end
|
||||||
|
config.use_wind = nil
|
||||||
|
|
||||||
|
-- if unspecified, use 2D or 3D rotation based on movement direction
|
||||||
if particles.vertical == nil then
|
if particles.vertical == nil then
|
||||||
particles.vertical = math.abs(vector.normalize(vel).y) >= 0.6
|
local vel = vector.multiply(vector.add(config.minvel, config.maxvel), 0.5)
|
||||||
|
config.vertical = math.abs(vector.normalize(vel).y) >= 0.6
|
||||||
end
|
end
|
||||||
|
|
||||||
if particles.size ~= nil then
|
return config
|
||||||
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,
|
|
||||||
collision_removal = true,
|
|
||||||
vertical = particles.vertical,
|
|
||||||
texture = texture,
|
|
||||||
player = player:get_player_name()
|
|
||||||
})
|
|
||||||
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
|
||||||
|
3
init.lua
3
init.lua
@ -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")
|
||||||
|
@ -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)
|
||||||
|
@ -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.
|
||||||
|
Loading…
Reference in New Issue
Block a user