technic/technic/tools/mining_lasers.lua
Zefram db20250371 Fix laser discharging
Commit a6dae893d6 introduced per-version
charge cost for firing mining lasers, but applies this in addition to
the old fixed cost which it was meant to replace.  Fix by removing the
application of the fixed cost.

The same commit did successfully change the check for a laser having
sufficient charge to fire, so that's based purely on the variable cost.
As a consequence, firing a laser that has just enough charge to cover the
variable cost could cause its charge to go negative.  (For example, by
fully charging a Mk1 laser and then firing it until it empties, resulting
in a charge of -400.)  It turned out that set_RE_wear handled that badly,
producing an over-100% wear value that would wrap to a *low* wear value,
leading to the laser's wear bar looking as if it's fully charged.

To protect against silly wear values, make set_RE_wear clamp the wear
value to avoid wrapping.  Handle specially the case of a fully-discharged
tool, where there was desirable wrapping to zero.
2014-04-22 12:48:55 -04:00

189 lines
5.0 KiB
Lua

local r_corr = 0.25 -- Remove a bit more nodes (if shooting diagonal) to let it look like a hole (sth like antialiasing)
local mining_lasers_list = {
-- {<num>, <range of the laser shots>, <max_charge>, <charge_per_shot>},
{"1", 7, 50000, 1000},
{"2", 14, 200000, 2000},
{"3", 21, 650000, 3000},
}
local f_1 = 0.5 - r_corr
local f_2 = 0.5 + r_corr
local S = technic.getter
minetest.register_craft({
output = 'technic:laser_mk1',
recipe = {
{'default:diamond', 'default:steel_ingot', 'technic:red_energy_crystal'},
{'', 'default:steel_ingot', 'default:steel_ingot'},
{'', '', 'default:copper_ingot'},
}
})
minetest.register_craft({
output = 'technic:laser_mk2',
recipe = {
{'default:diamond', 'default:steel_ingot', 'technic:laser_mk1'},
{'', 'default:steel_ingot', 'technic:green_energy_crystal'},
{'', '', 'default:copper_ingot'},
}
})
minetest.register_craft({
output = 'technic:laser_mk3',
recipe = {
{'default:diamond', 'default:steel_ingot', 'technic:laser_mk2'},
{'', 'default:steel_ingot', 'technic:blue_energy_crystal'},
{'', '', 'default:copper_ingot'},
}
})
local function get_used_dir(dir)
local abs_dir = {x = math.abs(dir.x),
y = math.abs(dir.y),
z = math.abs(dir.z)}
local dir_max = math.max(abs_dir.x, abs_dir.y, abs_dir.z)
if dir_max == abs_dir.x then
local tab = {"x", {x = 1, y = dir.y / dir.x, z = dir.z / dir.x}}
if dir.x >= 0 then
tab[3] = "+"
end
return tab
end
if dir_max == abs_dir.y then
local tab = {"y", {x = dir.x / dir.y, y = 1, z = dir.z / dir.y}}
if dir.y >= 0 then
tab[3] = "+"
end
return tab
end
local tab = {"z", {x = dir.x / dir.z, y = dir.y / dir.z, z = 1}}
if dir.z >= 0 then
tab[3] = "+"
end
return tab
end
local function node_tab(z, d)
local n1 = math.floor(z * d + f_1)
local n2 = math.floor(z * d + f_2)
if n1 == n2 then
return {n1}
end
return {n1, n2}
end
local function laser_node(pos, player)
if minetest.is_protected(pos, player:get_player_name()) then
minetest.record_protection_violation(pos, player:get_player_name())
return
end
local node = minetest.get_node(pos)
if node.name == "air"
or node.name == "ignore"
or node.name == "default:lava_source"
or node.name == "default:lava_flowing" then
return
end
if node.name == "default:water_source"
or node.name == "default:water_flowing" then
minetest.remove_node(pos)
minetest.add_particle(pos,
{x=0, y=2, z=0},
{x=0, y=-1, z=0},
1.5,
8,
false,
"smoke_puff.png")
return
end
if player then
minetest.node_dig(pos, node, player)
end
end
local function laser_nodes(pos, dir, player, range)
local t_dir = get_used_dir(dir)
local dir_typ = t_dir[1]
if t_dir[3] == "+" then
f_tab = {0, range}
else
f_tab = {-range,0}
end
local d_ch = t_dir[2]
if dir_typ == "x" then
for d = f_tab[1],f_tab[2],1 do
local x = d
local ytab = node_tab(d_ch.y, d)
local ztab = node_tab(d_ch.z, d)
for _, y in pairs(ytab) do
for _, z in pairs(ztab) do
laser_node({x = pos.x + x, y = pos.y + y, z = pos.z + z}, player)
end
end
end
return
end
if dir_typ == "y" then
for d = f_tab[1], f_tab[2] do
local xtab = node_tab(d_ch.x, d)
local y = d
local ztab = node_tab(d_ch.z, d)
for _, x in pairs(xtab) do
for _, z in pairs(ztab) do
laser_node({x = pos.x + x, y = pos.y + y, z = pos.z + z}, player)
end
end
end
return
end
for d = f_tab[1], f_tab[2] do
local xtab = node_tab(d_ch.x, d)
local ytab = node_tab(d_ch.y, d)
local z = d
for _, x in pairs(xtab) do
for _, y in pairs(ytab) do
laser_node({x = pos.x + x, y = pos.y + y, z = pos.z + z}, player)
end
end
end
end
local function laser_shoot(player, range, particle_texture, sound)
local playerpos = player:getpos()
local dir = player:get_look_dir()
local startpos = {x = playerpos.x, y = playerpos.y + 1.6, z = playerpos.z}
local mult_dir = vector.multiply(dir, 50)
minetest.add_particle(startpos, dir, mult_dir, range / 11, 1, false, particle_texture)
laser_nodes(vector.round(startpos), dir, player, range)
minetest.sound_play(sound, {pos = playerpos, gain = 1.0, max_hear_distance = range})
end
for _, m in pairs(mining_lasers_list) do
technic.register_power_tool("technic:laser_mk"..m[1], m[3])
minetest.register_tool("technic:laser_mk"..m[1], {
description = S("Mining Laser Mk%d"):format(m[1]),
inventory_image = "technic_mining_laser_mk"..m[1]..".png",
stack_max = 1,
on_use = function(itemstack, user)
local meta = minetest.deserialize(itemstack:get_metadata())
if not meta or not meta.charge then
return
end
-- If there's enough charge left, fire the laser
if meta.charge >= m[4] then
meta.charge = meta.charge - m[4]
laser_shoot(user, m[2], "technic_laser_beam_mk"..m[1]..".png", "technic_laser_mk"..m[1])
technic.set_RE_wear(itemstack, meta.charge, m[3])
itemstack:set_metadata(minetest.serialize(meta))
end
return itemstack
end,
})
end