write something there
This commit is contained in:
commit
b4b6c08f4f
8546 changed files with 309825 additions and 0 deletions
13
mods/effervescence/LICENSE
Normal file
13
mods/effervescence/LICENSE
Normal file
|
@ -0,0 +1,13 @@
|
|||
MIT License
|
||||
|
||||
Copyright © 2024 EmptyStar <https://github.com/EmptyStar>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
---
|
||||
|
||||
Texture file `effervescence_particles/textures/effervescence_petals.png` is derived from a texture by Liil/Wilhelmine/Skandarella, licensed MIT. A copy of its license accompanies the texture.
|
40
mods/effervescence/README.md
Normal file
40
mods/effervescence/README.md
Normal file
|
@ -0,0 +1,40 @@
|
|||
# Effervescence
|
||||
|
||||
Add a subtle touch of life to your world with Effervescence! This mod adds ambient particle effects to any world: bustling grasses, blossoming flowers, crumbling stone, and much more.
|
||||
|
||||
## Effects
|
||||
|
||||
Effervescence comes with the following default effects:
|
||||
|
||||
- **dusty** - dust particles that swirl about from dry, dusty nodes
|
||||
- **crumbly** - bits of loose dirt that fall from ceilings made of stone, dirt, or moss
|
||||
- **bustling** - grass, pollen, and spores that lift up from the ground
|
||||
- **snowy** - snowflake squalls that gust up from the ground
|
||||
- **leafy** - gently falling leaves from trees and bushes
|
||||
- **blossoming** - flower petals blown away from flowers
|
||||
- **sporogenic** - bursts of spores shed by mushrooms and certain types of vines and plants
|
||||
- **sparkly** - fine sparkles generated by crystals and ice
|
||||
- **bubbling** - tiny bubbles that form at the surface of water
|
||||
- **walking** - bits of dirt and grass kicked up under players' feet as they walk
|
||||
|
||||
## Advanced Usage
|
||||
|
||||
Effervescence is actually a modpack that comes with three mods:
|
||||
|
||||
- The core `effervescence` mod which provides an API and trigger mechanism for particle effects
|
||||
- `effervescence_decorators` which defines a set of default *decorators* used to place node metadata during mapgen
|
||||
- `effervescence_particles` which defines the default particle effects bundled with Effervescence
|
||||
|
||||
The secondary mods can be disabled in order to remove the bundled particle effects, and the mods can be individually depended upon for adding custom particle effects or decorators. The mod settings can be used to adjust mod behavior for performance, and the API can be used to control the mod programmatically.
|
||||
|
||||
## Notes
|
||||
|
||||
- This mod is primarily targeted at [Asuna](https://content.luanti.org/packages/EmptyStar/asuna/) but will mostly work for other typical sandbox games
|
||||
- This mod utilizes mapgen to determine where particles can occur in the environment which means that environmental particles will only appear in previously unexplored areas
|
||||
- Particle effects will not be applied to nodes placed by players although this can be achieved by adding `effervescence.particles` node metadata, an advanced task ~~covered in the API documentation~~
|
||||
|
||||
## Caveats
|
||||
|
||||
- Particle effects may cause lag on older hardware, especially in areas with lots of dense particle-emitting nodes
|
||||
- This mod adds metadata to the world to determine where particle effects can occur; uninstalling this mod will leave its metadata behind which causes no noticeable effect but will cause your world to permanently take up more space on your device
|
||||
- The color of particles may be wrong for nodes that use hardware coloring
|
273
mods/effervescence/effervescence/init.lua
Normal file
273
mods/effervescence/effervescence/init.lua
Normal file
|
@ -0,0 +1,273 @@
|
|||
-- Settings loaded from Asuna
|
||||
local asuna_amount = asuna.settings.particles.amount
|
||||
local asuna_enabled = asuna_amount ~= "none" and true or false
|
||||
local asuna_chance = (function()
|
||||
if asuna_amount == "less" then
|
||||
return 29
|
||||
elseif asuna_amount == "more" then
|
||||
return 70
|
||||
elseif asuna_amount == "maximum" then
|
||||
return 100
|
||||
else
|
||||
return 1
|
||||
end
|
||||
end)()
|
||||
local asuna_interval = (function()
|
||||
if asuna_amount == "less" then
|
||||
return 5.25
|
||||
elseif asuna_amount == "more" then
|
||||
return 3.5
|
||||
elseif asuna_amount == "maximum" then
|
||||
return 2.0
|
||||
else
|
||||
return 60.0
|
||||
end
|
||||
end)()
|
||||
|
||||
effervescence = {
|
||||
-- Settings loaded from settingtypes.txt
|
||||
settings = {
|
||||
environmental = {
|
||||
enabled = asuna_enabled,
|
||||
interval = asuna_interval,
|
||||
chance = asuna_chance,
|
||||
radius_x = tonumber(core.settings:get("effervescence.environmental.radius_x",18) or 18),
|
||||
radius_y = tonumber(core.settings:get("effervescence.environmental.radius_y",6) or 6),
|
||||
radius_z = tonumber(core.settings:get("effervescence.environmental.radius_z",18) or 18),
|
||||
look_dir_bias = tonumber(core.settings:get("effervescence.environmental.look_dir_bias",4) or 4),
|
||||
},
|
||||
player = {
|
||||
enabled = asuna_enabled,
|
||||
interval = tonumber(core.settings:get("effervescence.player.interval",0.5) or 0.5),
|
||||
},
|
||||
},
|
||||
|
||||
-- Node meta decorator registration
|
||||
decorators = {},
|
||||
register_decorator = function(def)
|
||||
if type(def.name) ~= "string" or effervescence.decorators[def.name] then
|
||||
return false, "invalid name or name already in use"
|
||||
end
|
||||
|
||||
if type(def.apply_to) ~= "function" then
|
||||
return false, "apply_to must be a function"
|
||||
end
|
||||
|
||||
if type(def.decorate) ~= "function" then
|
||||
return false, "decorate must be a function"
|
||||
end
|
||||
|
||||
effervescence.decorators[def.name] = def
|
||||
return true
|
||||
end,
|
||||
|
||||
-- Player-based particle registration
|
||||
player_particles = {},
|
||||
register_player_particles = function(def)
|
||||
if type(def.name) ~= "string" or def.name:find(",") or effervescence.environmental_particles[def.name] then
|
||||
return false, "invalid name or name already in use"
|
||||
end
|
||||
|
||||
if type(def.applies_to) ~= "function" then
|
||||
return false, "applies_to must be a function"
|
||||
end
|
||||
|
||||
if type(def.emit) ~= "function" then
|
||||
return false, "emit must be a function"
|
||||
end
|
||||
|
||||
def.check = (type(def.check) == "function" and def.check) or function() return true end
|
||||
|
||||
effervescence.player_particles[def.name] = def
|
||||
return true
|
||||
end,
|
||||
|
||||
-- Environmental particle registration
|
||||
environmental_particles = {},
|
||||
register_environmental_particles = function(def)
|
||||
if type(def.name) ~= "string" or def.name:find(",") or effervescence.environmental_particles[def.name] then
|
||||
return false, "invalid name or name already in use"
|
||||
end
|
||||
|
||||
if type(def.applies_to) ~= "function" then
|
||||
return false, "applies_to must be a function"
|
||||
end
|
||||
|
||||
if type(def.emit) ~= "function" then
|
||||
return false, "emit must be a function"
|
||||
end
|
||||
|
||||
def.check = (type(def.check) == "function" and def.check) or function() return true end
|
||||
|
||||
effervescence.environmental_particles[def.name] = def
|
||||
return true
|
||||
end,
|
||||
|
||||
-- Add particle to node meta
|
||||
add_particle_meta = function(pos, particle)
|
||||
local meta = core.get_meta(pos)
|
||||
local particles = meta:get("effervescence.particles")
|
||||
if particles then
|
||||
meta:set_string("effervescence.particles",particles .. "," .. particle)
|
||||
else
|
||||
meta:set_string("effervescence.particles",particle)
|
||||
end
|
||||
end,
|
||||
}
|
||||
|
||||
-- Player effect trigger
|
||||
local math_sign = function(number)
|
||||
return (number > 0 and 1) or (number < 0 and -1) or 0
|
||||
end
|
||||
|
||||
local math_round = function(number)
|
||||
return math_sign(number) * math.floor(math.abs(number) + 0.5)
|
||||
end
|
||||
|
||||
local get_look_bias = effervescence.settings.environmental.look_dir_bias > 0 and function(look_dir)
|
||||
local bias = effervescence.settings.environmental.look_dir_bias
|
||||
return math_round(look_dir.x * bias), math_round(look_dir.y * bias / 2), math_round(look_dir.z * bias)
|
||||
end or function() return 0, 0, 0 end
|
||||
|
||||
-- Environmental particles
|
||||
local environmental_particles
|
||||
if effervescence.settings.environmental.enabled then
|
||||
local etime = effervescence.settings.environmental.interval
|
||||
environmental_particles = function(dtime)
|
||||
etime = etime - dtime
|
||||
if etime < 0 then
|
||||
etime = effervescence.settings.environmental.interval
|
||||
local already_emitted = {}
|
||||
for _,player in ipairs(core.get_connected_players()) do
|
||||
if player then
|
||||
local pname = player:get_player_name()
|
||||
local pos = player:get_pos()
|
||||
local look_dir = player:get_look_dir()
|
||||
local bx, by, bz = get_look_bias(look_dir)
|
||||
for _,emitter in ipairs(
|
||||
core.find_nodes_with_meta(
|
||||
pos:offset(-effervescence.settings.environmental.radius_x + bx,-effervescence.settings.environmental.radius_y + by,-effervescence.settings.environmental.radius_z + bz),
|
||||
pos:offset(effervescence.settings.environmental.radius_x + bx,effervescence.settings.environmental.radius_y + by,effervescence.settings.environmental.radius_z + bz)
|
||||
)) do
|
||||
local hash = core.hash_node_position(emitter)
|
||||
if not already_emitted[hash] then
|
||||
local particles = core.get_meta(emitter):get("effervescence.particles")
|
||||
if particles and math.random(1,100) <= effervescence.settings.environmental.chance then
|
||||
particles = particles:split(",")
|
||||
local r = math.random(1,#particles)
|
||||
local len = #particles
|
||||
for i = r, len + r - 1, 1 do
|
||||
local particle = effervescence.environmental_particles[particles[i % len + 1]]
|
||||
if particle and particle:check(emitter) then
|
||||
local pdef = particle:emit(emitter)
|
||||
pdef.playername = pname,
|
||||
core.add_particlespawner(pdef)
|
||||
already_emitted[hash] = true
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
environmental_particles = function()
|
||||
-- no-op; environmental particles are disabled
|
||||
end
|
||||
end
|
||||
|
||||
-- Player walk particles
|
||||
local player_particles
|
||||
if effervescence.settings.player.enabled then
|
||||
-- Lookup map of players to particle trigger time
|
||||
local ptime = {}
|
||||
|
||||
-- Initialize player in map on join
|
||||
core.register_on_joinplayer(function(player)
|
||||
ptime[player:get_player_name()] = effervescence.settings.player.interval
|
||||
end)
|
||||
|
||||
-- Remove player time
|
||||
core.register_on_leaveplayer(function(player)
|
||||
ptime[player:get_player_name()] = nil
|
||||
end)
|
||||
|
||||
-- Particle spawning function
|
||||
player_particles = function(dtime)
|
||||
for _,player in ipairs(core.get_connected_players()) do
|
||||
if player then
|
||||
local pname = player:get_player_name()
|
||||
if ptime[pname] then
|
||||
ptime[pname] = ptime[pname] - dtime
|
||||
if ptime[pname] < 0 then
|
||||
ptime[pname] = effervescence.settings.player.interval
|
||||
for name,particle in pairs(effervescence.player_particles) do
|
||||
if particle:check(player) then
|
||||
core.add_particlespawner(particle:emit(player))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
player_particles = function()
|
||||
-- no-op; player particles are disabled
|
||||
end
|
||||
end
|
||||
|
||||
-- Particle trigger loop
|
||||
core.register_globalstep(function(dtime)
|
||||
environmental_particles(dtime)
|
||||
player_particles(dtime)
|
||||
end)
|
||||
|
||||
-- Identify target nodes and decorators
|
||||
core.register_on_mods_loaded(function()
|
||||
-- Node-particle map setup
|
||||
local npmap = {}
|
||||
for decorator,_ in pairs(effervescence.decorators) do
|
||||
npmap[decorator] = {}
|
||||
end
|
||||
|
||||
for node,ndef in pairs(core.registered_nodes) do
|
||||
-- Environmental particles
|
||||
for particle,pdef in pairs(effervescence.environmental_particles) do
|
||||
local decorators = pdef:applies_to(node,ndef) or {}
|
||||
for _,decorator in ipairs(decorators) do
|
||||
npmap[decorator][node] = npmap[decorator][node] or {}
|
||||
table.insert(npmap[decorator][node],particle)
|
||||
end
|
||||
end
|
||||
|
||||
-- Player particles
|
||||
for particle,pdef in pairs(effervescence.player_particles) do
|
||||
pdef:applies_to(node,ndef)
|
||||
end
|
||||
end
|
||||
|
||||
-- Apply particles to decorators
|
||||
for name,decorator in pairs(effervescence.decorators) do
|
||||
decorator:apply_to(npmap[name])
|
||||
end
|
||||
|
||||
-- Hack for VoxelLibre/Mineclonia
|
||||
local oggcm = core.get_current_modname
|
||||
core.get_current_modname = function()
|
||||
return "effervescence"
|
||||
end
|
||||
|
||||
-- Trigger decorators during mapgen
|
||||
core.register_on_generated(function(minp, maxp, blockseed)
|
||||
for name,decorator in pairs(effervescence.decorators) do
|
||||
decorator:decorate(minp, maxp, blockseed)
|
||||
end
|
||||
end)
|
||||
|
||||
-- Undo hack
|
||||
core.get_current_modname = oggcm
|
||||
end)
|
5
mods/effervescence/effervescence/mod.conf
Normal file
5
mods/effervescence/effervescence/mod.conf
Normal file
|
@ -0,0 +1,5 @@
|
|||
name = effervescence
|
||||
title = Effervescence
|
||||
description = Add a subtle touch of life to your worlds with effervescent particles
|
||||
author = EmptyStar
|
||||
optional_depends = asuna_core, caverealms, everness
|
113
mods/effervescence/effervescence_decorators/init.lua
Normal file
113
mods/effervescence/effervescence_decorators/init.lua
Normal file
|
@ -0,0 +1,113 @@
|
|||
local strategy = {
|
||||
-- decoration-based gennotify node selection strategy
|
||||
gennotify = {
|
||||
register = function(self, name, ddef)
|
||||
ddef.name = name
|
||||
effervescence.register_decorator({
|
||||
name = name,
|
||||
apply_to = function(self, npmap)
|
||||
local nlist = {}
|
||||
for node,particles in pairs(npmap) do
|
||||
table.insert(nlist,node)
|
||||
npmap[node] = table.concat(particles,",")
|
||||
end
|
||||
self.nodes = npmap
|
||||
|
||||
ddef.place_on = nlist
|
||||
core.register_decoration(ddef)
|
||||
|
||||
self.did = core.get_decoration_id(name)
|
||||
core.set_gen_notify({ decoration = true },{ self.did })
|
||||
self.did = "decoration#" .. self.did
|
||||
end,
|
||||
decorate = function(self, minp, maxp, blockseed)
|
||||
local gennotify = core.get_mapgen_object("gennotify")
|
||||
local positions = gennotify[self.did] or {}
|
||||
for _,pos in ipairs(positions) do
|
||||
local node = core.get_node(pos).name
|
||||
local particles = self.nodes[node]
|
||||
if particles then
|
||||
local meta = core.get_meta(pos)
|
||||
meta:set_string("effervescence.particles",particles)
|
||||
end
|
||||
end
|
||||
end,
|
||||
})
|
||||
end,
|
||||
},
|
||||
|
||||
-- random sample node selection strategy
|
||||
sample = {
|
||||
generation_id = 0,
|
||||
nodelist = { init = false },
|
||||
init_decorate = function(self, blockseed)
|
||||
if self.nodelist.init == false then
|
||||
local nodelist = {}
|
||||
for node,_ in pairs(self.nodelist) do
|
||||
table.insert(nodelist,node)
|
||||
end
|
||||
self.nodelist = nodelist
|
||||
end
|
||||
if blockseed ~= self.generation_id then
|
||||
self.generation_id = blockseed
|
||||
local _, emin, emax = core.get_mapgen_object("voxelmanip")
|
||||
self.nodes = core.find_nodes_in_area(emin,emax,self.nodelist,true)
|
||||
end
|
||||
end,
|
||||
register = function(self, name, per)
|
||||
local sampler = self
|
||||
effervescence.register_decorator({
|
||||
name = name,
|
||||
apply_to = function(self, npmap)
|
||||
self.nodes = npmap
|
||||
for node,particles in pairs(npmap) do
|
||||
npmap[node] = table.concat(particles,",")
|
||||
sampler.nodelist[node] = true
|
||||
end
|
||||
end,
|
||||
decorate = function(self, minp, maxp, blockseed)
|
||||
sampler:init_decorate(blockseed)
|
||||
for node,particles in pairs(self.nodes) do
|
||||
local positions = sampler.nodes[node]
|
||||
if positions then
|
||||
local plen = #positions
|
||||
local pcgr = PcgRandom(blockseed)
|
||||
for i = 0, math.floor(plen / per), 1 do
|
||||
local pos = positions[pcgr:next(1,plen)]
|
||||
local meta = core.get_meta(pos)
|
||||
meta:set_string("effervescence.particles",particles)
|
||||
end
|
||||
end
|
||||
end
|
||||
end,
|
||||
})
|
||||
end,
|
||||
},
|
||||
}
|
||||
|
||||
-- Register gennotify decorators
|
||||
strategy.gennotify:register("effervescence:floors",{
|
||||
deco_type = "simple",
|
||||
fill_ratio = 0.00375,
|
||||
decoration = "air",
|
||||
flags = "all_floors",
|
||||
})
|
||||
|
||||
strategy.gennotify:register("effervescence:ceilings",{
|
||||
deco_type = "simple",
|
||||
fill_ratio = 0.00375,
|
||||
decoration = "air",
|
||||
flags = "all_ceilings",
|
||||
})
|
||||
|
||||
strategy.gennotify:register("effervescence:liquid_surface",{
|
||||
deco_type = "simple",
|
||||
fill_ratio = 0.00425,
|
||||
decoration = "air",
|
||||
flags = "liquid_surface",
|
||||
})
|
||||
|
||||
-- Register sample decorators
|
||||
strategy.sample:register("effervescence:many",10)
|
||||
strategy.sample:register("effervescence:few",50)
|
||||
strategy.sample:register("effervescence:rare",250)
|
5
mods/effervescence/effervescence_decorators/mod.conf
Normal file
5
mods/effervescence/effervescence_decorators/mod.conf
Normal file
|
@ -0,0 +1,5 @@
|
|||
name = effervescence_decorators
|
||||
title = Effervescence Builtin Decorators
|
||||
description = Adds useful decorators for Effervescence
|
||||
author = EmptyStar
|
||||
depends = effervescence
|
512
mods/effervescence/effervescence_particles/init.lua
Normal file
512
mods/effervescence/effervescence_particles/init.lua
Normal file
|
@ -0,0 +1,512 @@
|
|||
-- Extract particle graphics from a node texture
|
||||
local function extract_tile(tiles,color)
|
||||
if not tiles then
|
||||
return "blank.png"
|
||||
end
|
||||
|
||||
if not color and tiles.color then
|
||||
color = tiles.color
|
||||
end
|
||||
|
||||
if type(tiles[1]) == "string" then
|
||||
return tiles[1], color
|
||||
elseif type(tiles[1]) == "table" then
|
||||
return tiles[1].name or tiles[1].image, color
|
||||
else
|
||||
return "blank.png"
|
||||
end
|
||||
end
|
||||
|
||||
local function extract_particles(tiles,color,coords)
|
||||
tiles, color = extract_tile(tiles,color)
|
||||
return tiles .. "^[resize:16x16^[sheet:8x8:" .. (coords or (math.random(0,7) .. "," .. math.random(0,7))) .. (color and ("^[multiply:" .. color) or "")
|
||||
end
|
||||
|
||||
-- Get texture blend value
|
||||
local blend_method = core.features.particle_blend_clip and "clip" or "alpha"
|
||||
|
||||
-- Dusty particles (sand, dry grass, etc.)
|
||||
effervescence.register_environmental_particles({
|
||||
name = "effervescence:dusty",
|
||||
check = function(self, pos)
|
||||
return (pos.y < 0 or pos.y > 6) and core.get_node(pos:offset(0,1,0)).name == "air"
|
||||
end,
|
||||
applies_to = function(self, node, def)
|
||||
local groups = def.groups or {}
|
||||
local does_apply = (groups.stone and groups.stone > 0) or (groups.sand and groups.sand > 0) or (groups.everness_sand and groups.everness_sand > 0) or node:find("dry_") or node:find("clay") or node:find("gravel") or node:find("litter$") or node:find("podzol")
|
||||
return does_apply and { "effervescence:floors" }
|
||||
end,
|
||||
emit = function(self, pos)
|
||||
local node = core.get_node(pos)
|
||||
local ndef = core.registered_nodes[node.name]
|
||||
return {
|
||||
amount = math.random(56,64),
|
||||
time = 5,
|
||||
pos = {
|
||||
min = pos:add(vector.new(-6,0.575,-6)),
|
||||
max = pos:add(vector.new(6,1,6)),
|
||||
},
|
||||
minsize = 0.2,
|
||||
maxsize = 0.275,
|
||||
minvel = { x = -1, y = -0.02, z = -1 },
|
||||
maxvel = { x = 1, y = 0.02, z = 1 },
|
||||
minexptime = 6,
|
||||
maxexptime = 8,
|
||||
minacc = {x = -1.5, y = 0, z = -1.5},
|
||||
maxacc = {x = 1.5, y = 0.05, z = 1.5},
|
||||
texture = {
|
||||
name = extract_particles(ndef.tiles,ndef.color),
|
||||
blend = blend_method,
|
||||
},
|
||||
collisiondetection = false,
|
||||
vertical = false,
|
||||
}
|
||||
end,
|
||||
})
|
||||
|
||||
-- Crumbly particles (falling bits of stone, dirt, gravel, etc.)
|
||||
effervescence.register_environmental_particles({
|
||||
name = "effervescence:crumbly",
|
||||
check = function(self, pos)
|
||||
return core.get_node(pos:offset(0,-1,0)).name == "air"
|
||||
end,
|
||||
applies_to = function(self, node, def)
|
||||
local groups = def.groups or {}
|
||||
local does_apply = (groups.stone and groups.stone > 0) or (groups.crumbly and groups.crumbly > 0) or (groups.soil and groups.soil > 0) or node:find("gravel") or node:find("ore") or node:find("moss") or node:find(":dirt_")
|
||||
return does_apply and { "effervescence:ceilings" }
|
||||
end,
|
||||
emit = function(self, pos)
|
||||
local node = core.get_node(pos)
|
||||
local ndef = core.registered_nodes[node.name]
|
||||
return {
|
||||
amount = 6,
|
||||
time = 0.5,
|
||||
pos = {
|
||||
min = pos:add(vector.new(-0.45,-0.75,-0.45)),
|
||||
max = pos:add(vector.new(0.45,-0.9,0.45)),
|
||||
},
|
||||
minvel = {x = 0, y = -5, z = 0},
|
||||
maxvel = {x = -0.1, y = -7, z = -0.1},
|
||||
minacc = {x = -0.1, y = -2, z = -0.1},
|
||||
maxacc = {x = 0.1, y = -3, z = 0.1},
|
||||
minexptime = 0.25,
|
||||
maxexptime = 1,
|
||||
minsize = 0.25,
|
||||
maxsize = 1.25,
|
||||
glow = ndef.glow or ndef.light_source,
|
||||
texture = {
|
||||
name = extract_particles(ndef.tiles,ndef.color),
|
||||
blend = blend_method,
|
||||
},
|
||||
collisiondetection = false,
|
||||
vertical = false,
|
||||
}
|
||||
end,
|
||||
})
|
||||
|
||||
-- Bustling particles (bit of grasses and mosses lifting off the ground)
|
||||
effervescence.register_environmental_particles({
|
||||
name = "effervescence:bustling",
|
||||
check = function(self, pos)
|
||||
return core.get_node(pos:offset(0,1,0)).name == "air"
|
||||
end,
|
||||
applies_to = function(self, node, def)
|
||||
local groups = def.groups or {}
|
||||
local does_apply = node:find("grass") or node:find("moss") or node:find("lichen") or node:find("^ethereal:.+_dirt$") or node:find("litter$") or node:find("mycelium")
|
||||
return does_apply and { "effervescence:floors" }
|
||||
end,
|
||||
emit = function(self, pos)
|
||||
local node = core.get_node(pos)
|
||||
local ndef = core.registered_nodes[node.name]
|
||||
return {
|
||||
amount = math.random(40,48),
|
||||
time = 6,
|
||||
pos = {
|
||||
min = pos:add(vector.new(-8,0.5,-8)),
|
||||
max = pos:add(vector.new(8,0.75,8)),
|
||||
},
|
||||
minvel = {x = -0.5, y = 0.1, z = -0.5},
|
||||
maxvel = {x = 0.5, y = 0.175, z = 0.5},
|
||||
minacc = {x = -0.325, y = 0.325, z = -0.325},
|
||||
maxacc = {x = 0.325, y = 0.5, z = 0.325},
|
||||
minexptime = 6,
|
||||
maxexptime = 12,
|
||||
minsize = 0.25,
|
||||
maxsize = 0.375,
|
||||
glow = ndef.glow or ndef.light_source,
|
||||
texture = {
|
||||
name = extract_particles(ndef.tiles,ndef.color),
|
||||
blend = blend_method,
|
||||
},
|
||||
collisiondetection = false,
|
||||
vertical = false,
|
||||
}
|
||||
end,
|
||||
})
|
||||
|
||||
-- Snowy particles (snowflakes gusting up from the ground)
|
||||
effervescence.register_environmental_particles({
|
||||
name = "effervescence:snowy",
|
||||
check = function(self, pos)
|
||||
return core.get_node(pos:offset(0,1,0)).name == "air"
|
||||
end,
|
||||
applies_to = function(self, node, def)
|
||||
local groups = def.groups or {}
|
||||
local is_snow = (groups.snowy and groups.snowy > 0) or node:find(":snow") or node:find("_with_snow")
|
||||
if is_snow then
|
||||
return { def.drawtype == "nodebox" and "effervescence:rare" or "effervescence:floors" }
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end,
|
||||
emit = function(self, pos)
|
||||
local node = core.get_node(pos)
|
||||
local ndef = core.registered_nodes[node.name]
|
||||
return {
|
||||
amount = math.random(8,40),
|
||||
time = 2,
|
||||
pos = {
|
||||
min = pos:add(vector.new(-3,0,-3)),
|
||||
max = pos:add(vector.new(3,0.4,3)),
|
||||
},
|
||||
minvel = {x = -4, y = 2, z = -4},
|
||||
maxvel = {x = 4, y = 3, z = 4},
|
||||
minacc = {x = -1.75, y = 1, z = -1.75},
|
||||
maxacc = {x = 1.75, y = 1.5, z = 1.75},
|
||||
minexptime = 3,
|
||||
maxexptime = 5,
|
||||
minsize = 0.175,
|
||||
maxsize = 0.225,
|
||||
texture = {
|
||||
name = extract_particles(ndef.tiles,ndef.color),
|
||||
blend = blend_method,
|
||||
},
|
||||
collisiondetection = false,
|
||||
vertical = false,
|
||||
}
|
||||
end,
|
||||
})
|
||||
|
||||
-- Leafy particles (falling leaves and snow on leaves)
|
||||
local leaves = {
|
||||
air = true, -- special exception for easier check logic
|
||||
}
|
||||
|
||||
effervescence.register_environmental_particles({
|
||||
name = "effervescence:leafy",
|
||||
check = function(self, pos)
|
||||
local below = core.get_node(pos:offset(0,-1,0)).name
|
||||
return leaves[below]
|
||||
end,
|
||||
applies_to = function(self, node, def)
|
||||
local groups = def.groups or {}
|
||||
local is_leaves = groups.leaves and groups.leaves > 0
|
||||
if is_leaves then
|
||||
leaves[node] = true
|
||||
return { "effervescence:few" }
|
||||
elseif node:find(":snow$") then
|
||||
return { "effervescence:many" }
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end,
|
||||
emit = function(self, pos)
|
||||
local node = core.get_node(pos)
|
||||
local ndef = core.registered_nodes[node.name]
|
||||
return {
|
||||
amount = math.random(1,2),
|
||||
time = 3,
|
||||
pos = {
|
||||
min = pos:add(vector.new(-0.45,-0.3,-0.45)),
|
||||
max = pos:add(vector.new(0.45,-0.475,0.45)),
|
||||
},
|
||||
minvel = {x = -1, y = -0.75, z = -1},
|
||||
maxvel = {x = 1, y = -1.5, z = 1},
|
||||
minacc = {x = -0.75, y = -1, z = -0.75},
|
||||
maxacc = {x = 0.75, y = -1.5, z = 0.75},
|
||||
minexptime = 4,
|
||||
maxexptime = 6,
|
||||
minsize = 1,
|
||||
maxsize = 1.5,
|
||||
glow = ndef.glow or ndef.light_source,
|
||||
texture = {
|
||||
name = extract_particles(ndef.tiles,ndef.color),
|
||||
blend = blend_method,
|
||||
},
|
||||
collisiondetection = false,
|
||||
vertical = false,
|
||||
}
|
||||
end,
|
||||
})
|
||||
|
||||
-- Blossoming particles (flower petals on the breeze)
|
||||
local colors = {
|
||||
black = "#000000",
|
||||
white = "#ffffff",
|
||||
blue = "#1f75fe",
|
||||
cyan = "#00b7eb",
|
||||
orange = "#ff8c00",
|
||||
yellow = "#ffd700",
|
||||
purple = "#6f2da8",
|
||||
violet = "#6f2da8",
|
||||
magenta = "#ff33cc",
|
||||
red = "#ff2400",
|
||||
pink = "#ffc0cb",
|
||||
green = "#7cfc00",
|
||||
dark_green = "#013220",
|
||||
dark_gray = "#333333",
|
||||
darkgray = "#333333",
|
||||
grey = "#9a9a9a",
|
||||
brown = "#704214",
|
||||
}
|
||||
|
||||
local flowers = { -- special cases listed here
|
||||
["farming:sunflower_8"] = "#ffd700",
|
||||
["farming:cotton_8"] = "#ffffff",
|
||||
["x_farming:cotton_8"] = "#ffffff",
|
||||
["dorwinion:dorwinion_glow_leaves"] = "self",
|
||||
["nightshade:nightshade_glowin_leaves_1"] = "self",
|
||||
["naturalbiomes:heatherflowernode"] = "self",
|
||||
["naturalbiomes:heatherflower2node"] = "self",
|
||||
["naturalbiomes:heatherflower3node"] = "self",
|
||||
["naturalbiomes:heatherflower4node"] = "self",
|
||||
}
|
||||
|
||||
effervescence.register_environmental_particles({
|
||||
name = "effervescence:blossoming",
|
||||
check = function(self, pos)
|
||||
return core.get_node(pos:offset(0,1,0)).name == "air"
|
||||
end,
|
||||
applies_to = function(self, node, def)
|
||||
local groups = def.groups or {}
|
||||
local is_plant = not def.walkable and def.drawtype:find("plant") and not node:find("shroom") and ((groups.flower and groups.flower > 0) or node:find("flower"))
|
||||
if is_plant then
|
||||
local dye = core.get_craft_result({
|
||||
method = "normal",
|
||||
width = 1,
|
||||
items = { node },
|
||||
})
|
||||
if not dye.item:is_empty() and dye.item:get_name():find("dye") then
|
||||
local dgroups = core.registered_items[dye.item:get_name()].groups
|
||||
for color,hex in pairs(colors) do
|
||||
if (dgroups["color_" .. color] and dgroups["color_" .. color] > 0) or (dgroups["basecolor_" .. color] and dgroups["basecolor_" .. color] > 0) then
|
||||
flowers[node] = hex
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return flowers[node] and { "effervescence:many" }
|
||||
end,
|
||||
emit = function(self, pos)
|
||||
local node = core.get_node(pos)
|
||||
local ndef = core.registered_nodes[node.name]
|
||||
return {
|
||||
amount = math.random(2,3),
|
||||
time = 1,
|
||||
pos = {
|
||||
min = pos:add(vector.new(-0.25,-0.125,-0.25)),
|
||||
max = pos:add(vector.new(0.25,0.25,0.25)),
|
||||
},
|
||||
minvel = {x = -3, y = -0.05, z = -3},
|
||||
maxvel = {x = 3, y = 1.5, z = 3},
|
||||
minacc = {x = -0.125, y = -0.125, z = -0.125},
|
||||
maxacc = {x = 0.125, y = 0.75, z = 0.125},
|
||||
minexptime = 3,
|
||||
maxexptime = 5,
|
||||
minsize = 0.375,
|
||||
maxsize = 0.5,
|
||||
glow = ndef.glow or ndef.light_source,
|
||||
texture = {
|
||||
name = extract_particles(flowers[node.name] == "self" and ndef.tiles or {[1] = "effervescence_petals.png"},flowers[node.name] ~= "self" and flowers[node.name]),
|
||||
blend = blend_method,
|
||||
},
|
||||
collisiondetection = true,
|
||||
collision_removal = true,
|
||||
vertical = false,
|
||||
}
|
||||
end,
|
||||
})
|
||||
|
||||
-- Sporogenic particles (primarily mushrooms, glow worms, and certain vines)
|
||||
effervescence.register_environmental_particles({
|
||||
name = "effervescence:sporogenic",
|
||||
check = function(self, pos)
|
||||
return true
|
||||
end,
|
||||
applies_to = function(self, node, def)
|
||||
local groups = def.groups or {}
|
||||
local does_apply = not def.walkable and def.drawtype:find("plant") and (node:find("glow_worm") or node:find("shroom") or node:find("fung") or node:find("myc") or node:find("coral_grass") or node:find("coral_plant") or node:find("[:_]vine") or node:find("spore") or (groups.mushroom and groups.mushroom > 0))
|
||||
return does_apply and { "effervescence:many" }
|
||||
end,
|
||||
emit = function(self, pos)
|
||||
local node = core.get_node(pos)
|
||||
local ndef = core.registered_nodes[node.name]
|
||||
return {
|
||||
amount = math.random(9,12),
|
||||
time = 1.5,
|
||||
pos = {
|
||||
min = pos:add(vector.new(-0.25,-0.1,-0.25)),
|
||||
max = pos:add(vector.new(0.25,0.1,0.25)),
|
||||
},
|
||||
minvel = {x = -0.625, y = -0.075, z = -0.625},
|
||||
maxvel = {x = 0.625, y = -0.05, z = 0.625},
|
||||
minacc = {x = 0, y = -0.25, z = 0},
|
||||
maxacc = {x = 0, y = -0.125, z = 0},
|
||||
minexptime = 5,
|
||||
maxexptime = 8,
|
||||
minsize = 0.25,
|
||||
maxsize = 0.325,
|
||||
glow = ndef.glow or ndef.light_source or 2,
|
||||
texture = {
|
||||
name = extract_particles(ndef.tiles,ndef.color,"4,7"),
|
||||
blend = blend_method,
|
||||
},
|
||||
collisiondetection = true,
|
||||
collision_removal = true,
|
||||
vertical = false,
|
||||
}
|
||||
end,
|
||||
})
|
||||
|
||||
-- Sparkly particles (glistening crystals or ice)
|
||||
local neighbors = {
|
||||
vector.new(0,1,0),
|
||||
vector.new(1,0,0),
|
||||
vector.new(0,0,1),
|
||||
vector.new(-1,0,0),
|
||||
vector.new(0,0,-1),
|
||||
vector.new(0,-1,0),
|
||||
}
|
||||
|
||||
effervescence.register_environmental_particles({
|
||||
name = "effervescence:sparkly",
|
||||
check = function(self, pos)
|
||||
for _,direction in ipairs(neighbors) do
|
||||
if core.get_node(pos:add(direction)).name == "air" then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end,
|
||||
applies_to = function(self, node, def)
|
||||
local does_apply = (def.drawtype:find("plant") and (node:find("[:_]crystal") or node:find("[:_]gem") or node:find("^caverealms:spike$") or node:find("icicle")) or node:find("[:_]ice$") or node:find("[:_]cave_ice$"))
|
||||
return does_apply and { "effervescence:many" }
|
||||
end,
|
||||
emit = function(self, pos)
|
||||
local node = core.get_node(pos)
|
||||
local ndef = core.registered_nodes[node.name]
|
||||
return {
|
||||
amount = math.random(9,11),
|
||||
time = 4,
|
||||
pos = {
|
||||
min = pos:add(vector.new(-0.75,-0.25,-0.75)),
|
||||
max = pos:add(vector.new(0.75,0.575,0.75)),
|
||||
},
|
||||
minvel = {x = 0, y = -0.05, z = 0},
|
||||
maxvel = {x = 0, y = 0, z = 0},
|
||||
minacc = {x = 0, y = 0, z = 0},
|
||||
maxacc = {x = 0, y = 0, z = 0},
|
||||
minexptime = 3,
|
||||
maxexptime = 6,
|
||||
minsize = 0.1,
|
||||
maxsize = 0.2,
|
||||
glow = ndef.glow or ndef.light_source or 2,
|
||||
texture = {
|
||||
name = extract_particles(ndef.tiles,nil,"4,7") .. "^[sheet:4x4:1,1^[opacity:255^[brighten",
|
||||
blend = blend_method,
|
||||
},
|
||||
collisiondetection = false,
|
||||
vertical = false,
|
||||
}
|
||||
end,
|
||||
})
|
||||
|
||||
-- Tiny bubbles at the surface of water
|
||||
effervescence.register_environmental_particles({
|
||||
name = "effervescence:bubbling",
|
||||
check = function(self, pos)
|
||||
return core.get_node(pos:offset(0,1,0)).name == "air"
|
||||
end,
|
||||
applies_to = function(self, node, def)
|
||||
local does_apply = (def.liquidtype == "source" and node:find("[:_]water"))
|
||||
return does_apply and { "effervescence:liquid_surface" }
|
||||
end,
|
||||
emit = function(self, pos)
|
||||
local node = core.get_node(pos)
|
||||
local ndef = core.registered_nodes[node.name]
|
||||
return {
|
||||
amount = math.random(4,6),
|
||||
time = math.random(3,4),
|
||||
pos = {
|
||||
min = pos:add(vector.new(-0.45,0.5,-0.45)),
|
||||
max = pos:add(vector.new(0.45,0.525,0.45)),
|
||||
},
|
||||
minvel = {x = -0.0275, y = -0.025, z = -0.0275},
|
||||
maxvel = {x = 0.0275, y = 0, z = 0.0275},
|
||||
minacc = {x = 0, y = 0, z = 0},
|
||||
maxacc = {x = 0, y = 0, z = 0},
|
||||
minexptime = 1,
|
||||
maxexptime = 2,
|
||||
minsize = 1,
|
||||
maxsize = 1.25,
|
||||
glow = ndef.glow or ndef.light_source,
|
||||
texture = extract_particles(ndef.tiles,ndef.color) .. "^[brighten^[opacity:127",
|
||||
collisiondetection = false,
|
||||
vertical = false,
|
||||
}
|
||||
end,
|
||||
})
|
||||
|
||||
-- Player walk particles (bits of dirt, snow, etc. kicked up underfoot)
|
||||
local walking_nodes = {}
|
||||
|
||||
effervescence.register_player_particles({
|
||||
name = "effervescence:walking",
|
||||
check = function(self, player)
|
||||
local velocity = player:get_velocity()
|
||||
if math.abs(velocity.x) > 0.025 or math.abs(velocity.z) > 0.025 then
|
||||
local below = core.get_node(player:get_pos():offset(0,-0.1,0)).name
|
||||
return walking_nodes[below]
|
||||
end
|
||||
end,
|
||||
applies_to = function(self, node, def)
|
||||
local groups = def.groups or {}
|
||||
local does_apply = (groups.crumbly and groups.crumbly > 0) or (groups.soil and groups.soil > 0) or (groups.sand and groups.sand > 0) or (groups.leaves and groups.leaves > 0) or (groups.snowy and groups.snowy > 0) or node:find(":snow") or node:find("_with_snow")
|
||||
if does_apply then
|
||||
walking_nodes[node] = true
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end,
|
||||
emit = function(self, player)
|
||||
local pos = player:get_pos()
|
||||
local node = core.get_node(pos:offset(0,-0.1,0))
|
||||
local ndef = core.registered_nodes[node.name]
|
||||
return {
|
||||
amount = math.random(3,4),
|
||||
time = 0.25,
|
||||
pos = {
|
||||
min = pos:add(vector.new(-0.45,0.175,-0.45)),
|
||||
max = pos:add(vector.new(0.45,0.275,0.45)),
|
||||
},
|
||||
minvel = {x = -1, y = 0.75, z = -1},
|
||||
maxvel = {x = 1, y = 1, z = 1},
|
||||
minacc = {x = 0, y = -9.81, z = 0},
|
||||
maxacc = {x = 0, y = -9.81, z = 0},
|
||||
minexptime = 1,
|
||||
maxexptime = 1,
|
||||
minsize = 0.75,
|
||||
maxsize = 1,
|
||||
glow = ndef.glow or ndef.light_source,
|
||||
texture = {
|
||||
name = extract_particles(ndef.tiles,ndef.color),
|
||||
blend = blend_method,
|
||||
},
|
||||
collisiondetection = true,
|
||||
collision_removal = true,
|
||||
vertical = false,
|
||||
}
|
||||
end,
|
||||
})
|
5
mods/effervescence/effervescence_particles/mod.conf
Normal file
5
mods/effervescence/effervescence_particles/mod.conf
Normal file
|
@ -0,0 +1,5 @@
|
|||
name = effervescence_particles
|
||||
title = Effervescence Builtin Particle Effects
|
||||
description = Adds builtin effervescent particles that will work with many Luanti games
|
||||
author = EmptyStar
|
||||
depends = effervescence, effervescence_decorators
|
25
mods/effervescence/effervescence_particles/textures/LICENSE
Normal file
25
mods/effervescence/effervescence_particles/textures/LICENSE
Normal file
|
@ -0,0 +1,25 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2022 Skandarella
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
|
||||
Textures and Models by Liil/Wilhelmine/ under (MIT) License (c) 2022
|
||||
Thanks to ShadMOrdre (https://github.com/ShadMOrdre) for fixing the schematics/leaf decay.
|
Binary file not shown.
After Width: | Height: | Size: 746 B |
4
mods/effervescence/modpack.conf
Normal file
4
mods/effervescence/modpack.conf
Normal file
|
@ -0,0 +1,4 @@
|
|||
name = effervescence
|
||||
title = Effervescence
|
||||
description = Adds a subtle touch of life to Luanti worlds with ambient particle effects
|
||||
author = EmptyStar
|
0
mods/effervescence/modpack.txt
Normal file
0
mods/effervescence/modpack.txt
Normal file
30
mods/effervescence/settingtypes.txt
Normal file
30
mods/effervescence/settingtypes.txt
Normal file
|
@ -0,0 +1,30 @@
|
|||
[Environmental Particles]
|
||||
|
||||
# Enable environmental particles? If enabled, newly generated terrain will be decorated with particles that players will see as they explore the world.
|
||||
effervescence.environmental.enabled (Enable environmental particles?) bool true
|
||||
|
||||
# The interval at which environmental particles will generate, every X seconds. A smaller interval will trigger particles more often but will use more system resources and vice-versa.
|
||||
effervescence.environmental.interval (Particle trigger interval) float 5.25 1.0 60.0
|
||||
|
||||
# The percentage chance of environmental particles to generate per node every interval. Lower numbers generate fewer particles and vice-versa.
|
||||
effervescence.environmental.chance (Chance of particles) int 29 1 100
|
||||
|
||||
# The x radius around each player in which effervescent nodes are checked for at each interval.
|
||||
effervescence.environmental.radius_x (Search radius x) int 16 8 32
|
||||
|
||||
# The y radius around each player in which effervescent nodes are checked for at each interval.
|
||||
effervescence.environmental.radius_y (Search radius y) int 8 4 32
|
||||
|
||||
# The z radius around each player in which effervescent nodes are checked for at each interval.
|
||||
effervescence.environmental.radius_z (Search radius z) int 16 8 32
|
||||
|
||||
# The number of nodes to shift a player's search box for effervescent nodes in the environment based on the direction they're looking. This causes more particles to spawn in the direction a player is looking which creates more particles in view at the expense of fewer particles further away in the opposite direction. This number is halved for the y axis.
|
||||
effervescence.environmental.look_dir_bias (Look direction bias) int 4 0 32
|
||||
|
||||
[Player Particles]
|
||||
|
||||
# Show particles at player locations? If true, player-based particles will appear near players.
|
||||
effervescence.player.enabled (Use player particles?) bool true
|
||||
|
||||
# How often to show player walk particles, in seconds.
|
||||
effervescence.player.interval (Player particles interval) float 0.5 0.1 5.0
|
Loading…
Add table
Add a link
Reference in a new issue