301 lines
11 KiB
Lua
301 lines
11 KiB
Lua
local S = minetest.get_translator("lzr_laser")
|
|
|
|
-- List of laser color hex codes.
|
|
-- These are the builtin default colors that cannot be changed.
|
|
lzr_laser.DEFAULT_LASER_COLORS = {
|
|
[lzr_globals.COLOR_RED] = "FF0000",
|
|
[lzr_globals.COLOR_GREEN] = "00FF00",
|
|
[lzr_globals.COLOR_BLUE] = "1212FF",
|
|
[lzr_globals.COLOR_YELLOW] = "FFFF00",
|
|
[lzr_globals.COLOR_CYAN] = "00FFFF",
|
|
[lzr_globals.COLOR_MAGENTA] = "FF00FF",
|
|
[lzr_globals.COLOR_WHITE] = "FFFFFF",
|
|
}
|
|
-- These are the user-changable laser colors that will actually
|
|
-- be used by the lasers in the game.
|
|
-- This table may contain user overrides; otherwise it is
|
|
-- equal to the default colors.
|
|
lzr_laser.LASER_COLORS = table.copy(lzr_laser.DEFAULT_LASER_COLORS)
|
|
|
|
local function read_laser_color_settings()
|
|
for c=1, #lzr_globals.COLOR_NAMES do
|
|
local setting = minetest.settings:get("lzr_laser_color_"..lzr_globals.COLOR_NAMES[c])
|
|
if setting then
|
|
local hexcode = string.match(setting, "[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]")
|
|
if hexcode then
|
|
lzr_laser.LASER_COLORS[c] = hexcode
|
|
minetest.log("action", "[lzr_laser] Custom laser color read from setting 'lzr_laser_color_"..lzr_globals.COLOR_NAMES[c]..": "..hexcode)
|
|
elseif setting ~= "" then
|
|
minetest.log("warning", "[lzr_laser] Setting 'lzr_laser_color_"..lzr_globals.COLOR_NAMES[c].."' has wrong syntax. Must be a 6-digit hexadecimal number (0-9, A-F).")
|
|
end
|
|
end
|
|
end
|
|
end
|
|
read_laser_color_settings()
|
|
|
|
-- List of patterns to apply on lasers to distinguish them by some other
|
|
-- way than just color. Implemented to deal with colorblindness.
|
|
local LASER_PATTERNS = {
|
|
[lzr_globals.COLOR_RED] = nil, -- no pattern, use a solid line
|
|
[lzr_globals.COLOR_GREEN] = { name = "lzr_laser_pattern_lines.png", size = 16 },
|
|
[lzr_globals.COLOR_BLUE] = { name = "lzr_laser_pattern_dots.png", size = 16 },
|
|
[lzr_globals.COLOR_YELLOW] = { name = "lzr_laser_pattern_checkers.png", size = 16 },
|
|
[lzr_globals.COLOR_CYAN] = { name = "lzr_laser_pattern_long_checkers.png", size = 16 },
|
|
[lzr_globals.COLOR_MAGENTA] = { name = "lzr_laser_pattern_alternating.png", size = 16 },
|
|
[lzr_globals.COLOR_WHITE] = { name = "lzr_laser_pattern_holes.png", size = 32 },
|
|
}
|
|
|
|
|
|
|
|
-- Shorthand to generate a laser tile. We don't use texture files,
|
|
-- we generate them on the fly!
|
|
local maketile = function(colorcode, alpha)
|
|
local use_patterns = minetest.settings:get_bool("lzr_patterned_lasers", false)
|
|
local alpha_append = ""
|
|
local pattern_append = ""
|
|
if alpha then
|
|
alpha_append = lzr_globals.LASER_ALPHA_STRING
|
|
end
|
|
local texsize = 16
|
|
if use_patterns then
|
|
local pattern = LASER_PATTERNS[colorcode]
|
|
if pattern ~= nil then
|
|
pattern_append = "^[mask:"..pattern.name
|
|
texsize = pattern.size
|
|
end
|
|
end
|
|
local texture =
|
|
-- Generate the texture base color
|
|
"([fill:"..texsize.."x"..texsize..":"..
|
|
"#"..lzr_laser.LASER_COLORS[colorcode]..alpha_append..")"
|
|
-- Apply a pattern on the laser, if enabled (allows to distinguish
|
|
-- lasers other than by color to circumvent colorblindness)
|
|
.. pattern_append
|
|
|
|
return { name = texture, backface_culling = true }
|
|
end
|
|
|
|
-- List of all possible laser tiles
|
|
if lzr_globals.OPAQUE_LASERS then
|
|
lzr_laser.TILE_LASER_R = maketile(lzr_globals.COLOR_RED)
|
|
lzr_laser.TILE_LASER_G = maketile(lzr_globals.COLOR_GREEN)
|
|
lzr_laser.TILE_LASER_B = maketile(lzr_globals.COLOR_BLUE)
|
|
lzr_laser.TILE_LASER_Y = maketile(lzr_globals.COLOR_YELLOW)
|
|
lzr_laser.TILE_LASER_C = maketile(lzr_globals.COLOR_CYAN)
|
|
lzr_laser.TILE_LASER_M = maketile(lzr_globals.COLOR_MAGENTA)
|
|
lzr_laser.TILE_LASER_W = maketile(lzr_globals.COLOR_WHITE)
|
|
lzr_laser.ALPHA_LASER = "clip"
|
|
else
|
|
lzr_laser.TILE_LASER_R = maketile(lzr_globals.COLOR_RED, true)
|
|
lzr_laser.TILE_LASER_G = maketile(lzr_globals.COLOR_GREEN, true)
|
|
lzr_laser.TILE_LASER_B = maketile(lzr_globals.COLOR_BLUE, true)
|
|
lzr_laser.TILE_LASER_Y = maketile(lzr_globals.COLOR_YELLOW, true)
|
|
lzr_laser.TILE_LASER_C = maketile(lzr_globals.COLOR_CYAN, true)
|
|
lzr_laser.TILE_LASER_M = maketile(lzr_globals.COLOR_MAGENTA, true)
|
|
lzr_laser.TILE_LASER_W = maketile(lzr_globals.COLOR_WHITE, true)
|
|
lzr_laser.ALPHA_LASER = "blend"
|
|
end
|
|
|
|
-- Distance from center to side of the laser beam square. Not really the radius, but it sounds cooler. :P
|
|
local LASER_RADIUS = -1/16
|
|
|
|
-- Register laser nodes, i.e. the actual
|
|
-- laser beams in the map.
|
|
|
|
--[[ This registers a node for every possible laser combination.
|
|
A laser node contains 1 to 3 laser beams, each along the
|
|
X, Y and Z axis. Also, each laser beam can have one of
|
|
each possible laser color (defined by colorcodes from 0
|
|
to lzr_globals.MAX_COLORCODE).
|
|
When there is no laser on any axis, there is no need
|
|
for a laser node.
|
|
|
|
The number of registered nodes is
|
|
(<number of laser colors + 1> ^ 3) - 1
|
|
]]
|
|
|
|
for color_x=0, lzr_globals.MAX_COLORCODE do
|
|
for color_y=0, lzr_globals.MAX_COLORCODE do
|
|
for color_z=0, lzr_globals.MAX_COLORCODE do
|
|
|
|
local dirstring = color_x .. color_y .. color_z
|
|
local dirs = lzr_laser.dirstring_to_dirs(dirstring)
|
|
|
|
-- Count the active axes
|
|
local axes = 0
|
|
local axes_active = {x=false,y=false,z=false}
|
|
for a=1,3 do
|
|
if dirs[a] ~= 0 then
|
|
axes = axes + 1
|
|
if a == 1 then
|
|
axes_active.x = true
|
|
elseif a == 2 then
|
|
axes_active.y = true
|
|
elseif a == 3 then
|
|
axes_active.z = true
|
|
end
|
|
end
|
|
end
|
|
local color2tile = function(colorcode)
|
|
if colorcode == 0 then
|
|
return "blank.png"
|
|
end
|
|
if colorcode == lzr_globals.COLOR_RED then
|
|
return lzr_laser.TILE_LASER_R
|
|
elseif colorcode == lzr_globals.COLOR_GREEN then
|
|
return lzr_laser.TILE_LASER_G
|
|
elseif colorcode == lzr_globals.COLOR_YELLOW then
|
|
return lzr_laser.TILE_LASER_Y
|
|
elseif colorcode == lzr_globals.COLOR_BLUE then
|
|
return lzr_laser.TILE_LASER_B
|
|
elseif colorcode == lzr_globals.COLOR_CYAN then
|
|
return lzr_laser.TILE_LASER_C
|
|
elseif colorcode == lzr_globals.COLOR_MAGENTA then
|
|
return lzr_laser.TILE_LASER_M
|
|
elseif colorcode == lzr_globals.COLOR_WHITE then
|
|
return lzr_laser.TILE_LASER_W
|
|
else
|
|
-- error
|
|
return "no_texture.png"
|
|
end
|
|
end
|
|
|
|
-- Pick a mesh depending on the axes on where to put
|
|
-- a laser beam.
|
|
-- The meshes are made so that the "ends" of the lasers
|
|
-- are "open", i.e. don't have an additional face.
|
|
-- This ensures the laser beam looks like a real
|
|
-- continuous beam reather than a sequence of nodes.
|
|
|
|
local tex, mesh
|
|
-- Single laser
|
|
if axes == 1 then
|
|
-- Simple: Just pick a mesh for the laser direction, and then the color
|
|
if axes_active.x then
|
|
mesh = "lzr_laser_laser_100.obj"
|
|
tex = { color2tile(color_x) }
|
|
elseif axes_active.y then
|
|
mesh = "lzr_laser_laser_010.obj"
|
|
tex = { color2tile(color_y)}
|
|
else
|
|
mesh = "lzr_laser_laser_001.obj"
|
|
tex = { color2tile(color_z) }
|
|
end
|
|
-- 2 lasers crossing
|
|
elseif axes == 2 then
|
|
-- A bit trickier: We have two primary colors from 2 lasers,
|
|
-- but at the crossing, we mix both colors with the 'combined' var.
|
|
if axes_active.x == false then
|
|
mesh = "lzr_laser_laser_011.obj"
|
|
-- Color mixing is just a bitwise OR of 2 colorcodes. Because the colorcodes
|
|
-- are aligned in the correct way for this to work.
|
|
local combined = bit.bor(color_y, color_z)
|
|
tex = { color2tile(combined), color2tile(color_y), color2tile(color_z) }
|
|
elseif axes_active.y == false then
|
|
mesh = "lzr_laser_laser_101.obj"
|
|
local combined = bit.bor(color_x, color_z)
|
|
tex = { color2tile(color_x), color2tile(combined), color2tile(color_z) }
|
|
else
|
|
mesh = "lzr_laser_laser_110.obj"
|
|
local combined = bit.bor(color_x, color_y)
|
|
tex = { color2tile(color_x), color2tile(color_y), color2tile(combined) }
|
|
end
|
|
-- Note
|
|
-- 3 lasers crossing
|
|
elseif axes == 3 then
|
|
-- Easy again: Pick a mesh and 3 colors for the 3 axes
|
|
mesh = "lzr_laser_laser_111.obj"
|
|
tex = { color2tile(color_x), color2tile(color_y), color2tile(color_z) }
|
|
end
|
|
|
|
-- This generates an unique number for each possible laser combination
|
|
local lasergroup = lzr_laser.colors_to_laser_group(color_x, color_y, color_z)
|
|
if tex then
|
|
-- Finally register the laser
|
|
minetest.register_node("lzr_laser:laser_"..dirstring, {
|
|
--~ Laser block description. @1 = color code uniquely identifying the color(s) of the laser beam(s) in that node
|
|
description = S("Laser (@1)", lzr_laser.dirstring_to_colstring(dirstring)),
|
|
paramtype = "light",
|
|
light_source = lzr_globals.LASER_GLOW,
|
|
drawtype = "mesh",
|
|
mesh = mesh,
|
|
sunlight_propagates = true,
|
|
walkable = false,
|
|
use_texture_alpha = lzr_laser.ALPHA_LASER,
|
|
tiles = tex,
|
|
pointable = false,
|
|
buildable_to = true,
|
|
|
|
groups = { laser = lasergroup, not_in_creative_inventory = 1, dig_immediate = 3 },
|
|
drop = "",
|
|
})
|
|
|
|
if axes == 1 then
|
|
-- Combination node: Barrier + laser
|
|
minetest.register_node("lzr_laser:barrier_laser_"..dirstring, {
|
|
--~ Description of a block that is both laser and invisible barrier. @1 = color code uniquely identifying the color(s) of the laser beam(s) in that node
|
|
description = S("Barrier Laser (@1)", lzr_laser.dirstring_to_colstring(dirstring)),
|
|
paramtype = "light",
|
|
light_source = lzr_globals.LASER_GLOW,
|
|
drawtype = "mesh",
|
|
mesh = mesh,
|
|
sunlight_propagates = true,
|
|
walkable = true,
|
|
use_texture_alpha = lzr_laser.ALPHA_LASER,
|
|
tiles = tex,
|
|
buildable_to = false,
|
|
|
|
-- Can't point through or select; player should not be able to
|
|
-- build at the level borders or point anything through it.
|
|
pointable = "blocking",
|
|
pointabilities = {
|
|
nodes = {
|
|
["group:barrier"] = true,
|
|
}
|
|
},
|
|
collision_box = { type = "regular" },
|
|
|
|
groups = { breakable = 1, barrier = 2, laser = lasergroup, not_in_creative_inventory = 1 },
|
|
drop = "",
|
|
})
|
|
|
|
-- Combination node: Rain Membrane + laser
|
|
minetest.register_node("lzr_laser:rain_membrane_laser_"..dirstring, {
|
|
--~ Description of a block that is both laser and invisible barrier that lets rain through but not the player. @1 = color code uniquely identifying the color(s) of the laser beam(s) in that node
|
|
description = S("Rain Membrane Laser (@1)", lzr_laser.dirstring_to_colstring(dirstring)),
|
|
paramtype = "light",
|
|
light_source = lzr_globals.LASER_GLOW,
|
|
drawtype = "mesh",
|
|
mesh = mesh,
|
|
sunlight_propagates = true,
|
|
walkable = true,
|
|
use_texture_alpha = lzr_laser.ALPHA_LASER,
|
|
tiles = tex,
|
|
buildable_to = false,
|
|
|
|
-- Can't point through or select; player should not be able to
|
|
-- build at the level borders or point anything through it.
|
|
pointable = "blocking",
|
|
pointabilities = {
|
|
nodes = {
|
|
["group:barrier"] = true,
|
|
}
|
|
},
|
|
|
|
collision_box = lzr_globals.RAIN_MEMBRANE_COLLISION_BOX,
|
|
selection_box = {
|
|
type = "regular",
|
|
},
|
|
is_ground_content = false,
|
|
|
|
groups = { breakable = 1, rain_membrane = 1, barrier = 2, laser = lasergroup, not_in_creative_inventory = 1 },
|
|
drop = "",
|
|
})
|
|
|
|
end
|
|
|
|
end
|
|
end
|
|
end
|
|
end
|