Replace wlight mod with wielded_light v2021-07-15...

https://content.minetest.net/packages/bell07/wielded_light/
master
Jordan Irwin 2021-07-20 16:34:50 -07:00
parent 2f773cfaa7
commit bfa6bbb612
23 changed files with 610 additions and 1066 deletions

View File

@ -63,7 +63,7 @@ The game includes the mods from the default [minetest_game](https://github.com/m
* lighting/
* [glow][] ([GPL][lic.gpl2.0]) -- version: [4c015a0 Git][ver.glow] *2019-02-05*
* [ilights][] ([LGPL][lic.lgpl3.0]) -- version: [2021-02-25-01][ver.ilights] ([patched][patch.ilights])
* [wlight][] ([MIT][lic.wlight]) -- version: [1.0][ver.wlight] *2021-06-26*
* [wielded_light][] ([GPL][lic.gpl3.0]) -- version: 2021-07-15
* materials/
* [amber][] ([LGPL][lic.lgpl2.1]) -- version: [56627fa Git][ver.amber] *2017-10-29* ([patched][patch.amber])
* [basic_materials][] ([LGPL][lic.lgpl3.0]) -- version: [2021-01-30][ver.basic_materials]
@ -245,7 +245,6 @@ The game includes the mods from the default [minetest_game](https://github.com/m
* [vehicles](https://forum.minetest.net/viewtopic.php?t=15610)
* [villagers](https://forum.minetest.net/viewtopic.php?t=17915)
* [weather](https://content.minetest.net/packages/theFox/weather/)
* [wielded_light](https://content.minetest.net/packages/bell07/wielded_light/)
* [wildcow](https://forum.minetest.net/viewtopic.php?t=25739)
* [witches](https://content.minetest.net/packages/FreeLikeGNU/witches/)
* [xpro](https://forum.minetest.net/viewtopic.php?t=20499) (XP and Levels)
@ -424,8 +423,8 @@ The game includes the mods from the default [minetest_game](https://github.com/m
[weather]: https://forum.minetest.net/viewtopic.php?t=5245
[whinny]: https://github.com/AntumMT/mod-whinny
[whitelist]: https://forum.minetest.net/viewtopic.php?t=8434
[wielded_light]: https://content.minetest.net/packages/bell07/wielded_light/
[windmill]: https://forum.minetest.net/viewtopic.php?id=7440
[wlight]: https://content.minetest.net/packages/AntumDeluge/wlight/
[workbench]: https://forum.minetest.net/viewtopic.php?t=14085
[xtraarmor]: https://forum.minetest.net/viewtopic.php?t=16645
@ -512,7 +511,6 @@ The game includes the mods from the default [minetest_game](https://github.com/m
[lic.whinny]: mods/mobiles/whinny/LICENSE.txt
[lic.whitelist]: mods/admin/whitelist/LICENSE.txt
[lic.windmill]: mods/buildings/windmill/README.md
[lic.wlight]: mods/lighting/wlight/LICENSE.txt
[lic.workbench]: mods/tools/workbench/LICENSE.txt
[lic.cc0]: doc/licenses/CC0.txt
@ -681,7 +679,6 @@ The game includes the mods from the default [minetest_game](https://github.com/m
[ver.whinny]: https://github.com/AntumMT/mod-whinny/tree/32f2d9f
[ver.whitelist]: https://github.com/AntumMT/mod-whitelist/releases/tag/v1.1
[ver.windmill]: https://github.com/Sokomine/windmill/tree/47b029d
[ver.wlight]: https://github.com/AntumMT/mod-wlight/releases/tag/v1.0
[ver.workbench]: https://github.com/AntumMT/mod-workbench/releases/tag/v1.0
[patch.3d_armor]: https://github.com/AntumMT/mp-3d_armor/tree/e3e9877

View File

@ -1709,14 +1709,6 @@ coloredwood_enable_stairsplus = true
####################
# *** wlight ***
## Enables wlight:megatorch item.
# type: bool
# default: true
#wlight.enable_megatorch = true
# *** wardrobe ***
## Number of skins shown per page.

View File

@ -0,0 +1,24 @@
# wielded_light mod for Minetest
Idea taken from torches_wieldlight in https://github.com/minetest-mods/torches, but written from scratch and usable for all shining items.
![Screenshot](https://github.com/bell07/minetest-wielded_light/raw/master/screenshot.png)
All bright nodes with light value > 2 lighten the player environment if wielded, with value fewer by 2. (Torch 13->11 for example)
Dependencies: none
License: [GPL-3](https://github.com/bell07/minetest-wielded_light/blob/master/LICENSE)
Shining API:
`function wielded_light.update_light(pos, light_level)`
Enable or update the shining at pos with light_level for 0.6 seconds. Can be used in any on_step call to get other entitys shining for example
`wielded_light.register_item_light(itemname, light_level)`
Override or set custom light level to an item. This does not change the item/node definition, just the lighting in this mod.
`function wielded_light.update_light_by_item(stack, pos)`
Update light at pos using item shining settings -from registered item_light or from item definition

View File

@ -0,0 +1 @@
default?

View File

@ -0,0 +1,581 @@
local mod_name = minetest.get_current_modname()
-- Node replacements that emit light
-- Sets of lighting_node={ node=original_node, level=light_level }
local lighting_nodes = {}
-- The nodes that can be replaced with lighting nodes
-- Sets of original_node={ [1]=lighting_node_1, [2]=lighting_node_2, ... }
local lightable_nodes = {}
-- Prefixes used for each node so we can avoid overlap
-- Pairs of prefix=original_node
local lighting_prefixes = {}
-- node_name=true pairs of lightable nodes that are liquids and can flood some light sources
local lightable_liquids = {}
-- How often will the positions of lights be recalculated
local update_interval = 0.2
-- How long until a previously lit node should be updated - reduces flicker
local removal_delay = update_interval * 0.5
-- How often will a node attempt to check itself for deletion
local cleanup_interval = update_interval * 3
-- How far in the future will the position be projected based on the velocity
local velocity_projection = update_interval * 1
-- How many light levels should an item held in the hand be reduced by, compared to the placed node
-- does not apply to manually registered light levels
local level_delta = 2
-- item=light_level pairs of registered wielded lights
local shiny_items = {}
-- List of custom callbacks for each update step
local update_callbacks = {}
local update_player_callbacks = {}
-- position={id=light_level} sets of known about light sources and their levels by position
local active_lights = {}
--[[ Sets of entities being tracked, in the form:
entity_id = {
obj = entity,
items = {
category_id..entity_id = {
level = light_level,
item? = item_name
}
},
update = true | false,
pos? = position_vector,
offset? = offset_vector,
}
]]
local tracked_entities = {}
-- position=true pairs of positions that need to be recaculated this update step
local light_recalcs = {}
--[[
Using 2-digit hex codes for categories
Starts at 00, ends at FF
This makes it easier extract `uid` from `cat_id..uid` by slicing off 2 characters
The category ID must be of a fixed length (2 characters)
]]
local cat_id = 0
local cat_codes = {}
local function get_light_category_id(cat)
-- If the category id does not already exist generate a new one
if not cat_codes[cat] then
if cat_id >= 256 then
error("Wielded item category limit exceeded, maximum 256 wield categories")
end
local code = string.format("%02x", cat_id)
cat_id = cat_id+1
cat_codes[cat] = code
end
-- If the category id does exist, return it
return cat_codes[cat]
end
-- Log an error coming from this mod
local function error_log(message, ...)
minetest.log("error", "[Wielded Light] " .. (message:format(...)))
end
-- Is a node lightable and a liquid capable of flooding some light sources
local function is_lightable_liquid(pos)
local node = minetest.get_node_or_nil(pos)
if not node then return end
return lightable_liquids[node.name]
end
-- Check if an entity instance still exists in the world
local function is_entity_valid(entity)
return entity and (entity.obj:is_player() or entity.obj:get_entity_name() or false)
end
-- Get the projected position of an entity based on its velocity, rounded to the nearest block
local function entity_pos(obj, offset)
if not offset then offset = { x=0, y=0, z=0 } end
return wielded_light.get_light_position(
vector.round(
vector.add(
vector.add(
offset,
obj:get_pos()
),
vector.multiply(
obj:get_player_velocity(),
velocity_projection
)
)
)
)
end
-- Add light to active light list and mark position for update
local function add_light(pos, id, light_level)
if not active_lights[pos] then
active_lights[pos] = {}
end
if active_lights[pos][id] ~= light_level then
-- minetest.log("error", "add "..id.." "..pos.." "..tostring(light_level))
active_lights[pos][id] = light_level
light_recalcs[pos] = true
end
end
-- Remove light from active light list and mark position for update
local function remove_light(pos, id)
if not active_lights[pos] then return end
-- minetest.log("error", "rem "..id.." "..pos)
active_lights[pos][id] = nil
minetest.after(removal_delay, function ()
light_recalcs[pos] = true
end)
end
-- Track an entity's position and update its light, will be called on every update step
local function update_entity(entity)
local pos = entity_pos(entity.obj, entity.offset)
local pos_str = pos and minetest.pos_to_string(pos)
-- If the position has changed, remove the old light and mark the entity for update
if entity.pos and pos_str ~= entity.pos then
entity.update = true
for id,_ in pairs(entity.items) do
remove_light(entity.pos, id)
end
end
-- Update the recorded position
entity.pos = pos_str
-- If the position is still loaded, pump the timer up so it doesn't get removed
if pos then
-- If the entity is marked for an update, add the light in the position if it emits light
if entity.update then
for id, item in pairs(entity.items) do
if item.level > 0 and not (item.floodable and is_lightable_liquid(pos)) then
add_light(pos_str, id, item.level)
else
remove_light(pos_str, id)
end
end
end
minetest.get_node_timer(pos):start(cleanup_interval)
end
entity.update = false
end
-- Replace a lighting node with its original counterpart
local function reset_lighting_node(pos)
local existing_node = minetest.get_node(pos)
local lighting_node = wielded_light.get_lighting_node(existing_node.name)
if not lighting_node then
return
end
minetest.swap_node(pos, { name = lighting_node.node })
end
-- Will be run once the node timer expires
local function cleanup_timer_callback(pos, elapsed)
local pos_str = minetest.pos_to_string(pos)
local lights = active_lights[pos_str]
-- If no active lights for this position, remove itself
if not lights then
reset_lighting_node(pos)
else
-- Clean up any tracked entities for this position that no longer exist
for id,_ in pairs(lights) do
local uid = string.sub(id,3)
local entity = tracked_entities[uid]
if not is_entity_valid(entity) then
remove_light(pos_str, id)
end
end
minetest.get_node_timer(pos):start(cleanup_interval)
end
end
-- Recalculate the total light level for a given position and update the light level there
local function recalc_light(pos)
-- If not in active lights list we can't do anything
if not active_lights[pos] then return end
-- Calculate the light level of the node
local any_light = false
local max_light = 0
for id, light_level in pairs(active_lights[pos]) do
any_light = true
if light_level > max_light then
max_light = light_level
end
end
-- Convert the position back to a vector
local pos_vec = minetest.string_to_pos(pos)
-- If no items in this position, delete it from the list and remove any light node
if not any_light then
active_lights[pos] = nil
reset_lighting_node(pos_vec)
return
end
-- If no light in this position remove any light node
if max_light == 0 then
reset_lighting_node(pos_vec)
return
end
-- Limit the light level
max_light = math.min(max_light, minetest.LIGHT_MAX)
-- Get the current light level in this position
local name = minetest.get_node(pos_vec).name
local old_value = wielded_light.level_of_lighting_node(name) or 0
-- If the light level has changed, set the coresponding light node and initiate the cleanup timer
if old_value ~= max_light then
local node_name = lightable_nodes[name] and name or lighting_nodes[name].node
minetest.swap_node(pos_vec, {
name = lightable_nodes[node_name][max_light]
})
minetest.get_node_timer(pos_vec):start(cleanup_interval)
end
end
local timer = 0
-- Will be run on every global step
local function global_timer_callback(dtime)
-- Only run once per update interval, global step will be called much more often than that
timer = timer + dtime;
if timer < update_interval then
return
end
timer = 0
-- Run all custom player callbacks for each player
local connected_players = minetest.get_connected_players()
for _,callback in pairs(update_player_callbacks) do
for _, player in pairs(connected_players) do
callback(player)
end
end
-- Run all custom callbacks
for _,callback in pairs(update_callbacks) do
callback()
end
-- Look at each tracked entity and update its position
for uid, entity in pairs(tracked_entities) do
if is_entity_valid(entity) then
update_entity(entity)
else
-- If the entity no longer exists, stop tracking it
tracked_entities[uid] = nil
end
end
-- Recalculate light levels
for pos,_ in pairs(light_recalcs) do
recalc_light(pos)
end
light_recalcs = {}
end
--- Shining API ---
wielded_light = {}
-- Registers a callback to be called every time the update interval is passed
function wielded_light.register_lightstep(callback)
table.insert(update_callbacks, callback)
end
-- Registers a callback to be called for each player every time the update interval is passed
function wielded_light.register_player_lightstep(callback)
table.insert(update_player_callbacks, callback)
end
-- Returns the node name for a given light level
function wielded_light.lighting_node_of_level(light_level, prefix)
return mod_name..":"..(prefix or "")..light_level
end
-- Gets the light level for a given node name, inverse of lighting_node_of_level
function wielded_light.level_of_lighting_node(node_name)
local lighting_node = wielded_light.get_lighting_node(node_name)
if lighting_node then
return lighting_node.level
end
end
-- Check if a node name is one of the wielded light nodes
function wielded_light.get_lighting_node(node_name)
return lighting_nodes[node_name]
end
-- Register any node as lightable, register all light level variations for it
function wielded_light.register_lightable_node(node_name, property_overrides, custom_prefix)
-- Node name must be string
if type(node_name) ~= "string" then
error_log("You must provide a node name to be registered as lightable, '%s' given.", type(node_name))
return
end
-- Node must already be registered
local original_definition = minetest.registered_nodes[node_name]
if not original_definition then
error_log("The node '%s' cannot be registered as lightable because it does not exist.", node_name)
return
end
-- Decide the prefix for the lighting node
local prefix = custom_prefix or node_name:gsub(":", "_", 1, true) .. "_"
if lighting_prefixes[prefix] then
error_log("The lighting prefix '%s' cannot be used for '%s' as it is already used for '%s'.", prefix, node_name, lighting_prefixes[prefix])
return
end
lighting_prefixes[prefix] = node_name
-- Default for property overrides
if not property_overrides then property_overrides = {} end
-- Copy the node definition and provide required settings for a lighting node
local new_definition = table.copy(original_definition)
new_definition.on_timer = cleanup_timer_callback
new_definition.paramtype = "light"
new_definition.mod_origin = mod_name
new_definition.groups = new_definition.groups or {}
new_definition.groups.not_in_creative_inventory = 1
-- Allow any properties to be overridden on registration
for prop, val in pairs(property_overrides) do
new_definition[prop] = val
end
-- If it's a liquid, we need to stop it flowing
if new_definition.groups.liquid then
new_definition.liquid_range = 0
lightable_liquids[node_name] = true
end
-- Register the lighting nodes
lightable_nodes[node_name] = {}
for i=1, minetest.LIGHT_MAX do
local lighting_node_name = wielded_light.lighting_node_of_level(i, prefix)
-- Index for quick finding later
lightable_nodes[node_name][i] = lighting_node_name
lighting_nodes[lighting_node_name] = {
node = node_name,
level = i
}
-- Copy the base definition and apply the light level
local level_definition = table.copy(new_definition)
level_definition.light_source = i
-- If it's a liquid, we need to stop it replacing itself with the original
if level_definition.groups.liquid then
level_definition.liquid_alternative_source = lighting_node_name
level_definition.liquid_alternative_flowing = lighting_node_name
end
minetest.register_node(lighting_node_name, level_definition)
end
end
-- Check if node can have a wielded light node placed in it
function wielded_light.is_lightable_node(node_pos)
local name = minetest.get_node(node_pos).name
if lightable_nodes[name] then
return true
elseif wielded_light.get_lighting_node(name) then
return true
end
return false
end
-- Gets the closest position to pos that's a lightable node
function wielded_light.get_light_position(pos)
local around_vector = {
{x=0, y=0, z=0},
{x=0, y=1, z=0}, {x=0, y=-1, z=0},
{x=1, y=0, z=0}, {x=-1, y=0, z=0},
{x=0, y=0, z=1}, {x=0, y=0, z=1},
}
for _, around in ipairs(around_vector) do
local light_pos = vector.add(pos, around)
if wielded_light.is_lightable_node(light_pos) then
return light_pos
end
end
end
-- Gets the emitted light level of a given item name
function wielded_light.get_light_def(item_name)
-- Invalid item? No light
if not item_name or item_name == "" then
return 0, false
end
-- If the item is cached return the cached level
local cached_definition = shiny_items[item_name]
if cached_definition then
return cached_definition.level, cached_definition.floodable
end
-- Get the item definition
local stack = ItemStack(item_name)
local itemdef = stack:get_definition()
-- If invalid, no light
if not itemdef then
return 0, false
end
-- Get the light level of an item from its definition
-- Reduce the light level by level_delta - original functionality
-- Limit between 0 and the max light level
return math.min(math.max((itemdef.light_source or 0) - level_delta, 0), minetest.LIGHT_MAX), itemdef.floodable
end
-- Register an item as shining
function wielded_light.register_item_light(item_name, light_level, floodable)
if shiny_items[item_name] then
if light_level then
shiny_items[item_name].level = light_level
end
if floodable ~= nil then
shiny_items[item_name].floodable = floodable
end
else
if floodable == nil then
local stack = ItemStack(item_name)
local itemdef = stack:get_definition()
floodable = itemdef.floodable
end
shiny_items[item_name] = {
level = light_level,
floodable = floodable or false
}
end
end
-- Mark an item as floodable or not
function wielded_light.register_item_floodable(item_name, floodable)
if floodable == nil then floodable = true end
if shiny_items[item_name] then
shiny_items[item_name].floodable = floodable
else
local calced_level = wielded_light.get_light_def(item_name)
shiny_items[item_name] = {
level = calced_level,
floodable = floodable
}
end
end
-- Keep track of an item entity. Should be called once for an item
function wielded_light.track_item_entity(obj, cat, item)
local light_level, light_is_floodable = wielded_light.get_light_def(item)
-- If the item does not emit light do not track it
if light_level <= 0 then return end
-- Generate the uid for the item and the id for the light category
local uid = tostring(obj)
local id = get_light_category_id(cat)..uid
-- Create the main tracking object for this item instance if it does not already exist
if not tracked_entities[uid] then
tracked_entities[uid] = { obj=obj, items={}, update = true }
end
-- Create the item tracking object for this item + category
tracked_entities[uid].items[id] = { level=light_level, floodable=light_is_floodable }
-- Add the light in on creation so it's immediate
local pos = entity_pos(obj)
local pos_str = pos and minetest.pos_to_string(pos)
if pos_str then
if not (light_is_floodable and is_lightable_liquid(pos)) then
add_light(pos_str, id, light_level)
end
end
tracked_entities[uid].pos = pos_str
end
-- A player's light should appear near their head not their feet
local player_height_offset = { x=0, y=1, z=0 }
-- Keep track of a user / player entity. Should be called as often as the user updates
function wielded_light.track_user_entity(obj, cat, item)
-- Generate the uid for the player and the id for the light category
local uid = tostring(obj)
local id = get_light_category_id(cat)..uid
-- Create the main tracking object for this player instance if it does not already exist
if not tracked_entities[uid] then
tracked_entities[uid] = { obj=obj, items={}, offset = player_height_offset, update = true }
end
local tracked_entity = tracked_entities[uid]
local tracked_item = tracked_entity.items[id]
-- If the item being tracked for the player changes, update the item tracking object for this item + category
if not tracked_item or tracked_item.item ~= item then
local light_level, light_is_floodable = wielded_light.get_light_def(item)
tracked_entity.items[id] = { level=light_level, item=item, floodable=light_is_floodable }
tracked_entity.update = true
end
end
-- Setup --
-- Wielded item shining globalstep
minetest.register_globalstep(global_timer_callback)
-- Dropped item on_step override
-- https://github.com/minetest/minetest/issues/6909
local builtin_item = minetest.registered_entities["__builtin:item"]
local item = {
on_step = function(self, dtime, ...)
builtin_item.on_step(self, dtime, ...)
-- Register an item once for tracking
-- If it's already being tracked, exit
if self.wielded_light then return end
self.wielded_light = true
local stack = ItemStack(self.itemstring)
local item_name = stack:get_name()
wielded_light.track_item_entity(self.object, "item", item_name)
end
}
setmetatable(item, {__index = builtin_item})
minetest.register_entity(":__builtin:item", item)
-- Track a player's wielded item
wielded_light.register_player_lightstep(function (player)
wielded_light.track_user_entity(player, "wield", player:get_wielded_item():get_name())
end)
-- Register helper nodes
local water_name = "default:water_source"
if minetest.get_modpath("hades_core") then
water_name = "hades_core:water_source"
end
wielded_light.register_lightable_node("air", nil, "")
wielded_light.register_lightable_node(water_name, nil, "water_")
wielded_light.register_lightable_node("default:river_water_source", nil, "river_water_")
---TEST
--wielded_light.register_item_light('default:dirt', 14)

View File

@ -0,0 +1,2 @@
name = wielded_light
optional_depends = default, hades_core

Binary file not shown.

After

Width:  |  Height:  |  Size: 329 KiB

View File

@ -1,24 +0,0 @@
The MIT License (MIT)
Copyright © 2021 Jordan Irwin (AntumDeluge)
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.
Code by Echo & petermaloney originally licensed under CC0

View File

@ -1,72 +0,0 @@
## Walking Light
### Description:
A mod for [Minetest](http://minetest.net/) that illuminates the area around a player when wielding a light-emitting item. Also supports equipped armor with [3d_armor](https://content.minetest.net/packages/stu/3d_armor/).
![screenshot](screenshot.png)
#### History:
- forked from [v0.6][forum.echo] of Echo's *walking_light*
- forked from [Git commit 766ef0f](https://github.com/petermaloney/walking_light/tree/766ef0f) of petermaloney's *walking_light*
### Licensing:
- Code: [MIT](LICENSE.txt)
- Textures: [CC0](https://creativecommons.org/publicdomain/zero/1.0/legalcode)
### Requirements:
```
- Minetest minimum version: 5.0.0
- Depends: none
- Optional depends: default (for torch & megatorch)
- Privileges: server (for using chat commands)
```
### Usage:
Main methods:
```
wlight.register_item(iname, radius)
- Registers an item to emit light when wielded.
- `iname`: Item technical name.
- `radius`: Distance light will reach (max: 10).
wlight.register_armor(iname, radius, litem)
- Registers an item to emit light when equipped in armor inventory.
- `iname`: Item technical name.
- `radius`: Distance light will reach (max: 10).
- `litem`: Whether or not this item should also be registered with `wlight.register_item` (default: true).
```
Settings:
```
wlight.enable_megatorch
- Enables wlight:megatorch item.
- default: true
```
### Links:
- [![ContentDB](https://content.minetest.net/packages/AntumDeluge/wlight/shields/title/)](https://content.minetest.net/packages/AntumDeluge/wlight/)
- [Forum](https://forum.minetest.net/viewtopic.php?t=26938)
- [Git repo](https://github.com/AntumMT/mod-wlight)
- [API](https://antummt.github.io/mod-wlight/docs/api.html)
- [Changelog](changelog.txt)
- [TODO](TODO.txt)
- Echo's *walking_light*:
- [Forum][forum.echo]
- [v0.6 release](https://github.com/AntumMT/mod-wlight/releases/tag/v0.6)
- petermaloney's *walking_light*:
- [Git repo](https://github.com/petermaloney/walking_light)
**Alternative Mods:**
- [![wielded_light](https://content.minetest.net/packages/bell07/wielded_light/shields/title/)](https://content.minetest.net/packages/bell07/wielded_light/)
[forum.echo]: https://forum.minetest.net/viewtopic.php?t=2621

View File

@ -1,4 +0,0 @@
TODO:
- wlight_debug should only enable debugging for player invoking command
- use spherical area when calculating radius for chat commands

View File

@ -1,636 +0,0 @@
--- wlight Methods
--
-- @topic methods
-- list of all players seen by core.register_on_joinplayer
local players = {}
-- all player positions last time light was updated: {player_name : {x, y, z}}
local player_positions = {}
-- all light positions of light that currently is created {player_name : {i: {x, y, z}}
local light_positions = {}
-- last item seen wielded by players
local last_wielded = {}
-- toggles debug mode
local wlight_debug = false
-- name of light node, changed by toggling debug mode
wlight_node = nil
--- Sets debugging mode.
--
-- @tparam bool enabled Determines if debugging is enabled.
function wlight.set_debug(enabled)
wlight_debug = enabled
end
--- Checks if debugging is enabled.
--
-- @treturn bool
function wlight.debug_enabled()
return wlight_debug == true
end
-- list of items that use walking light when wielded
local light_items = {}
-- list of items that use walking light when equipped as armor
local light_armor = {}
--- Registers an item to emit light when wielded.
--
-- @tparam string iname Item technical name.
-- @tparam[opt] int radius Distance light will reach (max: 10).
function wlight.register_item(iname, radius)
if radius and radius > 10 then
wlight.log("warning", "light radius too high, setting to 10")
radius = 10
end
local def = {radius=radius}
for li in pairs(light_items) do
if iname == li then
wlight.log("warning", "\"" .. iname .. "\" is already light item.")
return
end
end
light_items[iname] = def
end
--- DEPRECATED
--
-- Use `wlight.register_item`
function wlight.addLightItem(item)
wlight.log("warning",
"\"wlight.addLightItem\" is deprecated, use \"wlight.register_item\"")
return wlight.register_item(item)
end
--- Registers an item to emit light when equipped in armor inventory.
--
-- Note: light radius will be overridden by light-emitting item being wielded.
--
-- @tparam string iname Item technical name.
-- @tparam[opt] int radius Distance light will reach (max: 10).
-- @tparam[opt] bool litem Whether or not this item should also be registered with `wlight.register_item`.
function wlight.register_armor(iname, radius, litem)
if radius and radius > 10 then
wlight.log("warning", "light radius too high, setting to 10")
radius = 10
end
local def = {radius=radius}
if litem == nil then litem = true end
for la in pairs(light_armor) do
if iname == la then
wlight.log("warning", "\"" .. iname .. "\" is already light armor.")
end
end
light_armor[iname] = def
if litem then wlight.register_item(iname, radius) end
end
--- Retrieves list of items registered as emitting light when wielded.
--
-- @treturn table Table indexed by key.
function wlight.get_light_items()
return light_items
end
--- DEPRECATED
--
-- Use `wlight.get_light_items`
function wlight.getLightItems()
wlight.log("warning",
"\"wlight.getLightItems\" is deprecated, use \"wlight.get_light_items\"")
return wlight.get_light_items()
end
-- from http://lua-users.org/wiki/IteratorsTutorial
-- useful for removing things from a table because removing from the middle makes it skip elements otherwise
local function ripairs(t)
local function ripairs_it(t, i)
i = i-1
local v = t[i]
if v == nil then return v end
return i, v
end
return ripairs_it, t, #t+1
end
-- formats a vector with shorter output than dump
local function dumppos(pos)
if pos == nil then
return "nil"
end
local x = "nil"
if pos.x then
x = pos.x
end
local y = "nil"
if pos.y then
y = pos.y
end
local z = "nil"
if pos.z then
z = pos.z
end
return "(" .. x .. "," .. y .. "," .. z .. ")"
end
-- formats a table containing vectors with shorter output than dump
local function dumppostable(t)
if t == nil then
return "nil"
end
if #t == 0 then
return "0{}"
end
ret = #t .. "{\n"
for i, pos in ipairs(t) do
ret = ret .. " " .. dumppos(pos) .. "\n"
end
ret = ret .. "}"
return ret
end
local function mt_get_node_or_nil(pos)
if pos == nil then
wlight.log("error", "mt_get_node_or_nil(), pos is nil")
print(debug.traceback("Current Callstack:\n"))
return nil
end
local node = core.get_node_or_nil(pos)
if not node then
-- Load the map at pos and try again
core.get_voxel_manip():read_from_map(pos, pos)
node = core.get_node(pos)
end
-- If node.name is "ignore" here, the map probably isn't generated at pos.
return node
end
--- Adds a node to the world.
--
-- @tparam table pos Position.
-- @tparam table sometable
function wlight.mt_add_node(pos, sometable)
if pos == nil then
wlight.log("error", "wlight.mt_add_node(), pos is nil")
print(debug.traceback("Current Callstack:\n"))
return nil
end
if sometable == nil then
wlight.log("error", "wlight.mt_add_node(), sometable is nil")
print(debug.traceback("Current Callstack:\n"))
return nil
end
core.add_node(pos, sometable)
end
--- DEPRECATED
--
-- Use `wlight.mt_add_node`
function mt_add_node(pos, sometable)
wlight.log("warning", "\"mt_add_node\" is deprecated, use \"wlight.mt_add_node\"")
return wlight.mt_add_node(pos, sometable)
end
local function round(num)
return math.floor(num + 0.5)
end
---
--
-- @tparam table pos1
-- @tparam table pos2
-- @treturn bool
function wlight.poseq(pos1, pos2)
if pos1 == nil and pos2 == nil then
return true
end
if pos1 == nil or pos2 == nil then
return false
end
return pos1.x == pos2.x and pos1.y == pos2.y and pos1.z == pos2.z
end
--- DEPRECATED
--
-- Use `wlight.poseq`
function poseq(pos1, pos2)
wlight.log("warning", "\"poseq\" is deprecated, use \"wlight.poseq\"")
return wlight.poseq(pos1, pos2)
end
-- return true if the player moved since last player_positions update
local function player_moved(player)
local player_name = player:get_player_name()
local pos = player:get_pos()
local rounded_pos = vector.round(pos)
local oldpos = player_positions[player_name]
if oldpos == nil or not wlight.poseq(rounded_pos, oldpos) then
-- if oldpos is nil, we assume they just logged in, so consider them moved
return true
end
return false
end
-- same as table.remove(t, remove_pos), but uses wlight.poseq instead of comparing references (does lua have comparator support, so this isn't needed?)
local function table_remove_pos(t, remove_pos)
for i, pos in ipairs(t) do
if wlight.poseq(pos, remove_pos) then
table.remove(t, i)
break
end
end
end
-- same as t[remove_pos], but uses wlight.poseq instead of comparing references (does lua have comparator support, so this isn't needed?)
local function table_contains_pos(t, remove_pos)
for i, pos in ipairs(t) do
if wlight.poseq(pos, remove_pos) then
return true
end
end
return false
end
-- same as table.insert(t, pos) but makes sure it is not duplicated
local function table_insert_pos(t, pos)
if not table_contains_pos( pos ) then
table.insert(t, pos)
end
end
local function is_light(node)
if node ~= nil and node ~= "ignore" and (node.name == "wlight:light" or node.name == "wlight:light_debug") then
return true
end
return false
end
--- Removes light at the given position.
--
-- @tparam[opt] ObjectRef player
-- @tparam table pos Posistion where light will be removed.
function wlight.remove_light(player, pos)
local player_name
if player then
player_name = player:get_player_name()
end
local node = mt_get_node_or_nil(pos)
if is_light(node) then
wlight.mt_add_node(pos, {type="node", name="air"})
if player_name then
table_remove_pos(light_positions[player_name], pos)
end
else
if node ~= nil then
wlight.log("warning", "wlight.remove_light(), pos = "
.. dumppos(pos) .. ", tried to remove light but node was " .. node.name)
table_remove_pos(light_positions[player_name], pos)
else
wlight.log("warning", "wlight.remove_light(), pos = "
.. dumppos(pos) .. ", tried to remove light but node was nil")
end
end
end
--- DEPRECATED
--
-- Use `wlight.remove_light`
function remove_light(player, pos)
wlight.log("warning", "\"remove_light\" is deprecated, use \"wlight.remove_light\"")
return wlight.remove_light(player, pos)
end
-- removes all light owned by a player
local function remove_light_player(player)
local player_name = player:get_player_name()
for i, old_pos in ripairs(light_positions[player_name]) do
if old_pos then
wlight.remove_light(player, old_pos)
end
end
end
local function can_add_light(pos)
local node = mt_get_node_or_nil(pos)
if node == nil or node == "ignore" then
-- if node is nil (unknown) or ignore (not generated), then we don't do anything.
return false
elseif node.name == "air" then
return true
elseif is_light(node) then
return true
end
return false
end
-- old function returns pos instead of table, for only one position
local function pick_light_position_regular(player, pos)
if can_add_light(pos) then
return {pos}
end
local pos2
-- if pos is not possible, try the old player position first, to make it more likely that it has a line of sight
local player_name = player:get_player_name()
local oldplayerpos = player_positions[player_name]
if oldplayerpos and can_add_light(vector.new(oldplayerpos.x, oldplayerpos.y + 1, oldplayerpos.z)) then
return oldplayerpos
end
-- if not, try all positions around the pos
pos2 = vector.new(pos.x + 1, pos.y, pos.z)
if can_add_light(pos2) then
return {pos2}
end
pos2 = vector.new(pos.x - 1, pos.y, pos.z)
if can_add_light(pos2) then
return {pos2}
end
pos2 = vector.new(pos.x, pos.y, pos.z + 1)
if can_add_light(pos2) then
return {pos2}
end
pos2 = vector.new(pos.x, pos.y, pos.z - 1)
if can_add_light(pos2) then
return {pos2}
end
pos2 = vector.new(pos.x, pos.y + 1, pos.z)
if can_add_light(pos2) then
return {pos2}
end
pos2 = vector.new(pos.x, pos.y - 1, pos.z)
if can_add_light(pos2) then
return {pos2}
end
return nil
end
-- new function, returns table
local function pick_light_position_radius(player, pos, radius)
local ret = {}
if can_add_light(pos) then
table.insert(ret, pos)
end
local pos2
local step = 4
local unstep = 1/step
for x = pos.x - radius, pos.x + radius, step do
for y = pos.y - radius, pos.y + radius, step do
for z = pos.z - radius, pos.z + radius, step do
pos2 = vector.new(round(x*unstep)*step, round(y*unstep)*step, round(z*unstep)*step)
local distance = math.sqrt(math.pow(pos.x - x, 2) + math.pow(pos.y - y, 2) + math.pow(pos.z - z, 2))
if distance <= radius and can_add_light( pos2 ) then
table.insert(ret, pos2)
end
end
end
end
return ret
end
local function pick_light_position(player, pos, radius)
if radius then
return pick_light_position_radius(player, pos, radius)
end
return pick_light_position_regular(player, pos)
end
-- adds light at the given position
local function add_light(player, pos)
local player_name = player:get_player_name()
local node = mt_get_node_or_nil(pos)
if node == nil or node == "ignore" then
-- don't do anything for nil (non-loaded) or ignore (non-generated) blocks, so we don't want to overwrite anything there
return false
elseif node.name == "air" then
-- when the node that is already there is air, add light
wlight.mt_add_node(pos, {type="node", name=wlight_node})
if not table_contains_pos(light_positions[player_name], pos) then
table_insert_pos(light_positions[player_name], pos)
end
return true
elseif is_light(node) then
-- no point in adding light where it is already, but we should assign it to the player so it gets removed (in case it has no player)
if not table_contains_pos(light_positions[player_name], pos) then
table_insert_pos(light_positions[player_name], pos)
end
return true
end
return false
end
-- returns a string, the name of the item found that is a light item
local function get_wielded_light_item(player)
local wielded_item = player:get_wielded_item():get_name()
if wielded_item ~= "" and wlight.is_light_item(wielded_item) then
return wielded_item
end
-- check equipped armor
if core.get_modpath("3d_armor") then
local player_name = player:get_player_name()
if player_name then
local armor_inv = core.get_inventory({type="detached", name=player_name.."_armor"})
if armor_inv then
for k, stack in pairs(armor_inv:get_list("armor")) do
local item_name = stack:get_name()
if wlight.is_light_armor(item_name) then
return item_name, true
end
end
end
end
end
return nil
end
-- updates all the light around the player, depending on what they are wielding
local function update_light_player(player)
-- if there is no player, there can be no update
if not player then
return
end
-- figure out if they wield light; this will be nil if not
local wielded_item, is_armor = get_wielded_light_item(player)
local player_name = player:get_player_name()
local pos = player:get_pos()
local rounded_pos = vector.round(pos)
-- check for a nil node where the player is; if it is nil, we assume the block is not loaded, so we return without updating player_positions
-- that way, it should add light next step
local node = mt_get_node_or_nil(rounded_pos)
if node == nil or node == "ignore" then
return
end
if not player_moved(player) and wielded_item == last_wielded[player_name] then
-- no update needed if the wiedled light item is the same as before (including nil), and the player didn't move
return
end
last_wielded[player_name] = wielded_item;
local wantlightpos = nil
local wantpos = vector.new(rounded_pos.x, rounded_pos.y + 1, rounded_pos.z)
if wielded_item then
local radius
if is_armor then
radius = light_armor[wielded_item].radius
else
radius = light_items[wielded_item].radius
end
-- decide where light should be
wantlightpos = pick_light_position(player, wantpos, radius)
end
if wielded_item and wantlightpos then
-- add light that isn't already there
for i, newpos in ipairs(wantlightpos) do
add_light(player, newpos)
end
end
-- go through all light owned by the player to remove all but what should be kept
for i, oldlightpos in ripairs(light_positions[player_name]) do
if not wantlightpos or oldlightpos and oldlightpos.x and not table_contains_pos(wantlightpos, oldlightpos) then
wlight.remove_light(player, oldlightpos)
end
end
player_positions[player_name] = vector.round(pos)
end
local function update_light_all()
-- go through all players to check
for i, player_name in ipairs(players) do
local player = core.get_player_by_name(player_name)
update_light_player(player)
end
end
--- Checks if an item is registered as emitting light when wielded.
--
-- @tparam string iname Item technical name.
-- @treturn bool `true` if item is registered.
function wlight.is_light_item(iname)
for li in pairs(light_items) do
if iname == li then
return true
end
end
return false
end
--- Checks if an item is registered as emitting light when equipped in armor inventory.
--
-- @tparam string iname Item technical name.
-- @treturn bool `true` if item is registered.
function wlight.is_light_armor(iname)
for la in pairs(light_armor) do
if iname == la then
return true
end
end
return false
end
--- Checks if player is wielding a light-emitting item.
--
-- @tparam ObjectRef player Player to be checked.
-- @treturn bool `true` if player is wielding registered item.
function wlight.wields_light(player)
return get_wielded_light_item(player) ~= nil
end
core.register_on_joinplayer(function(player)
local player_name = player:get_player_name()
table.insert(players, player_name)
last_wielded[player_name] = get_wielded_light_item(player)
local pos = player:get_pos()
player_positions[player_name] = nil
light_positions[player_name] = {}
update_light_player(player)
end)
core.register_on_leaveplayer(function(player)
local player_name = player:get_player_name()
for i, v in ipairs(players) do
if v == player_name then
table.remove(players, i)
end
end
last_wielded[player_name] = false
remove_light_player(player)
player_positions[player_name] = nil
end)
core.register_globalstep(function(dtime)
for i, player_name in ipairs(players) do
local player = core.get_player_by_name(player_name)
if player ~= nil then
update_light_player(player)
else
table.remove(players, i)
end
end
end)
--- Updates light node texture.
--
-- If debugging, node will display a marker, otherwise will be transparent.
function wlight.update_node()
if wlight_debug then
wlight_node = "wlight:light_debug"
else
wlight_node = "wlight:light"
end
end
wlight.update_node()

View File

@ -1,30 +0,0 @@
v1.0
----
- forked from petermaloney @ Git commit 766ef0f ( https://github.com/petermaloney/walking_light/tree/766ef0f )
- changed code license to MIT
- added mod.conf
- re-created underlay image
- some code cleanup & optimization
- hand is not checked for light
- added global method for registering an armor item
- segregated code into modules
- removed walking_light:pick_mese & walking_light:helmet_diamond (mods should register their own light items)
- megatorch can be disabled via setting
- added localization support
- added option to set custom light radius when registering item or armor
- removed method "walking_light.register_tool"
- renamed to "wlight"
- consolidated chat commands into single "wlight" command
- fixed "/wlight add" command not able to illuminate more than one node
petermaloney
------------
- forked from walking_light v0.6 by Echo ( https://forum.minetest.net/viewtopic.php?t=2621 )
- bug fixes & code optimizations
- light updated on player login
- added 3d_armor support
- added items walking_light:helmet_diamond & walking_light:megatorch
- added global method for registering items for use with walking_light
- replaced deprecated methods & parameters
- prefixed chat commands with "walking_light"

View File

@ -1,103 +0,0 @@
--- wlight Chat Commands
--
-- @topic commands
local S = core.get_translator(wlight.modname)
--- Manages lighted nodes and debugging.
--
-- **Parameters:**
--
-- @chatcmd wlight
-- @tparam command Command action to be executed.
-- @tparam[opt] radius Area radius (default: 20).
-- @usage /wlight <command> [radius]
--
-- Commands:
-- - add: Add lighting to current position.
-- - remove: Remove lighting from current position.
-- - debug: Toggle illuminated nodes visible mark for debugging.
--
-- Options:
-- - radius: Area radius (add default: 0, remove default: 20).
--
-- Example:
-- /wlight add 5
core.register_chatcommand(wlight.modname, {
params = "<" .. S("command") .. "> [" .. S("radius") .. "]",
privs = {server=true},
description = S("Manage lighted nodes and debugging.")
.. "\n\n" .. S("Commands:")
.. "\n add: " .. S("Add lighting to current position.")
.. "\n remove: " .. S("Remove lighting from current position.")
.. "\n debug: " .. S("Toggle illuminated nodes visible mark for debugging.")
.. "\n\n" .. S("Options:")
.. "\n " .. S("radius") .. ": " .. S("Area radius (add default: 0, remove default: 20)."),
func = function(name, param)
local command
local radius
if param:find(" ") then
local params = param:split(" ")
command = params[1]
radius = tonumber(params[2])
else
command = param
end
if not radius then
radius = 0
if command == "remove" then
radius = 20
end
end
if command == "" then
core.chat_send_player(name, "\n" .. S("Missing command parameter."))
return false
end
if (command == "add" or command == "remove") and not radius then
core.chat_send_player(name, "\n" .. S("Missing radius parameter."))
return false
end
local pos = vector.round(core.get_player_by_name(name):get_pos())
if command == "debug" then
wlight.set_debug(not wlight.debug_enabled())
wlight.update_node()
elseif command == "add" then
pos = vector.new(pos.x, pos.y + 1, pos.z)
if pos then
local pmin = {x=pos.x-radius, y=pos.y-radius, z=pos.z-radius}
local pmax = {x=pos.x+radius, y=pos.y+radius, z=pos.z+radius}
local near_nodes = core.find_nodes_in_area(pmin, pmax, "air", true)
if near_nodes.air then
for _, npos in ipairs(near_nodes.air) do
wlight.mt_add_node(npos, {type="node", name=wlight_node})
end
end
end
elseif command == "remove" then
for _, v in ipairs({"wlight:light", "wlight:light_debug"}) do
local point = core.find_node_near(pos, radius, v)
while point do
wlight.remove_light(nil, point)
local oldpoint = point
point = core.find_node_near(pos, radius, v)
if wlight.poseq(oldpoint, point) then
return false, S("Failed... infinite loop detected.")
end
end
end
else
core.chat_send_player(name, "\n" .. S("Unknown command: @1", command))
return false
end
return true, S("Done.")
end,
})

View File

@ -1,38 +0,0 @@
wlight = {}
wlight.modname = core.get_current_modname()
wlight.modpath = core.get_modpath(wlight.modname)
-- override walking_light
walking_light = wlight
function wlight.log(lvl, msg)
if not msg then
msg = lvl
lvl = nil
end
msg = "[" .. wlight.modname .. "] " .. msg
if not lvl then
core.log(msg)
else
core.log(lvl, msg)
end
end
local scripts = {
"settings",
"api",
"chat",
"nodes",
}
for _, sc in ipairs(scripts) do
dofile(wlight.modpath .. "/" .. sc .. ".lua")
end
if core.registered_items["default:torch"] then
wlight.register_item("default:torch")
end

View File

@ -1,20 +0,0 @@
Translated by:
# chat.lua
Manage lighted nodes and debugging.=
Commands:=
Options:=
command=
radius=
Missing command parameter.=
Missing radius parameter.=
Add lighting to current position.=
Remove lighting from current position.=
Toggle illuminated nodes visible mark for debugging.=
Area radius (add default: 0, remove default: 20).=
Failed... infinite loop detected.=
Done.=
# nodes.lua
Megatorch=

View File

@ -1,5 +0,0 @@
name = wlight
description = Adds items that will illuminate area around player when wielded or equipped.
version = 1.0
author = Echo, petermaloney, Jordan Irwin (AntumDeluge)
optional_depends = default, walking_light

View File

@ -1,100 +0,0 @@
local S = core.get_translator(wlight.modname)
core.register_node("wlight:light_debug", {
drawtype = "plantlike",
tiles = {"wlight_inv_underlay.png"},
inventory_image = core.inventorycube("wlight_inv_underlay.png"),
paramtype = "light",
walkable = false,
is_ground_content = true,
sunlight_propagates = true,
light_source = 13,
selection_box = {
type = "fixed",
fixed = {0, 0, 0, 0, 0, 0},
},
})
core.register_alias("walking_light:light_debug", "wlight:light_debug")
core.register_node("wlight:light", {
drawtype = "glasslike",
tiles = {"wlight_light.png"},
inventory_image = core.inventorycube("wlight_light.png"),
paramtype = "light",
walkable = false,
is_ground_content = true,
sunlight_propagates = true,
light_source = 13,
selection_box = {
type = "fixed",
fixed = {0, 0, 0, 0, 0, 0},
},
})
core.register_alias("walking_light:light", "wlight:light")
if wlight.enable_megatorch and core.get_modpath("default") then
core.register_node("wlight:megatorch", {
description = S("Megatorch"),
drawtype = "torchlike",
tiles = {
{
name = "default_torch_on_floor_animated.png",
animation = {
type = "vertical_frames",
aspect_w = 16,
aspect_h = 16,
length = 3.0,
},
},
{
name = "default_torch_on_ceiling_animated.png",
animation = {
type = "vertical_frames",
aspect_w = 16,
aspect_h = 16,
length = 3.0,
},
},
{
name = "default_torch_animated.png",
animation = {
type = "vertical_frames",
aspect_w = 16,
aspect_h = 16,
length = 3.0,
},
},
},
inventory_image = "default_torch_on_floor.png",
wield_image = "default_torch_on_floor.png",
paramtype = "light",
paramtype2 = "wallmounted",
sunlight_propagates = true,
is_ground_content = false,
walkable = false,
light_source = 13,
selection_box = {
type = "wallmounted",
wall_top = {-0.1, 0.5-0.6, -0.1, 0.1, 0.5, 0.1},
wall_bottom = {-0.1, -0.5, -0.1, 0.1, -0.5+0.6, 0.1},
wall_side = {-0.5, -0.3, -0.1, -0.5+0.3, 0.3, 0.1},
},
groups = {choppy=2, dig_immediate=3, flammable=1, attached_node=1},
legacy_wallmounted = true,
})
wlight.register_item("wlight:megatorch", 10)
core.register_craft({
output = "wlight:megatorch",
recipe = {
{"default:torch", "default:torch", "default:torch"},
{"default:torch", "default:torch", "default:torch"},
{"default:torch", "default:torch", "default:torch"},
}
})
core.register_alias("walking_light:megatorch", "wlight:megatorch")
end

Binary file not shown.

Before

Width:  |  Height:  |  Size: 283 KiB

View File

@ -1,12 +0,0 @@
--- wlight Settings
--
-- @topic settings
--- Enables wlight:megatorch item.
--
-- @setting wlight.enable_megatorch
-- @settype bool
-- @default true
wlight.enable_megatorch = core.settings:get_bool("wlight.enable_megatorch", true)

View File

@ -1,3 +0,0 @@
# Enables wlight:megatorch item.
wlight.enable_megatorch (Enable megatorch) bool true

Binary file not shown.

Before

Width:  |  Height:  |  Size: 174 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 128 B

View File

@ -2115,12 +2115,6 @@ slingshot.ammos (Registered ammos) string default:mese_crystal=5
trampoline.damage_absorb (Trampoline damage absorption) int 100 0 100
[*wlight]
# Enables wlight:megatorch item.
wlight.enable_megatorch (Enable megatorch) bool true
[*wardrobe]
# Number of skins shown per page.