Merge pull request 'Fireworks update' (#4724) from fireworks_update into master

Reviewed-on: https://git.minetest.land/VoxeLibre/VoxeLibre/pulls/4724
This commit is contained in:
the-real-herowl 2024-12-29 08:08:52 +01:00
commit 8d3f3b4715
28 changed files with 613 additions and 250 deletions

@ -184,3 +184,37 @@ tt.register_snippet(function(itemstring, _, itemstack)
end end
return s:trim() return s:trim()
end) end)
-- Fireworks info
tt.register_snippet(function(itemstring, _, itemstack)
if not itemstack then return end
local def = itemstack:get_definition()
if not def then return end
if not def._vl_fireworks_tt then return end
local s = ""
local meta = itemstack:get_meta()
local stars = meta:get("vl_fireworks:stars") or core.serialize({})
s = s .. def._vl_fireworks_tt(meta:get_float("vl_fireworks:duration"),
core.deserialize(stars))
return s:trim()
end)
tt.register_snippet(function(itemstring, _, itemstack)
if not itemstack then return end
local def = itemstack:get_definition()
if not def then return end
if not def.groups.firework_star or def.groups.firework_star == 0 then return end
local s = ""
local meta = itemstack:get_meta()
local effect = meta:get("vl_fireworks:star_effect") or core.serialize({fn="generic"})
if effect then
s = vl_fireworks.star_tt(core.deserialize(effect))
end
return s
end)

@ -72,6 +72,8 @@ minetest.register_on_mods_loaded(function()
-- Is set to true if it was added in any category besides misc -- Is set to true if it was added in any category besides misc
local nonmisc = false local nonmisc = false
-- Is set to true if it has already been added to the "all" category (special handler)
local all_handled = false
if def.groups.building_block then if def.groups.building_block then
table.insert(inventory_lists["blocks"], name) table.insert(inventory_lists["blocks"], name)
nonmisc = true nonmisc = true
@ -113,6 +115,22 @@ minetest.register_on_mods_loaded(function()
table.insert(inventory_lists["matr"], name) table.insert(inventory_lists["matr"], name)
nonmisc = true nonmisc = true
end end
if def._vl_fireworks_std_durs_forces then
local generic = core.serialize({{fn="generic"}})
for i, tbl in ipairs(def._vl_fireworks_std_durs_forces) do
local stack = ItemStack(name)
local meta = stack:get_meta()
meta:set_float("vl_fireworks:duration", tbl[1])
meta:set_int("vl_fireworks:force", tbl[2])
table.insert(inventory_lists["misc"], stack:to_string())
table.insert(inventory_lists["all"], stack:to_string())
meta:set_string("vl_fireworks:stars", generic)
table.insert(inventory_lists["misc"], stack:to_string())
table.insert(inventory_lists["all"], stack:to_string())
end
nonmisc = true
all_handled = true
end
-- Misc. category is for everything which is not in any other category -- Misc. category is for everything which is not in any other category
if not nonmisc then if not nonmisc then
table.insert(inventory_lists["misc"], name) table.insert(inventory_lists["misc"], name)
@ -133,7 +151,9 @@ minetest.register_on_mods_loaded(function()
table.insert(inventory_lists["brew"], stack:to_string()) table.insert(inventory_lists["brew"], stack:to_string())
table.insert(inventory_lists["all"], stack:to_string()) table.insert(inventory_lists["all"], stack:to_string())
end end
else end
if not all_handled then
table.insert(inventory_lists["all"], name) table.insert(inventory_lists["all"], name)
end end
@ -190,12 +210,11 @@ local function set_inv_search(filter, player)
local inv = minetest.get_inventory({ type = "detached", name = "creative_" .. playername }) local inv = minetest.get_inventory({ type = "detached", name = "creative_" .. playername })
local creative_list = {} local creative_list = {}
local lang = minetest.get_player_information(playername).lang_code local lang = minetest.get_player_information(playername).lang_code
for name, def in pairs(minetest.registered_items) do for _, str in pairs(inventory_lists["all"]) do
if (not def.groups.not_in_creative_inventory or def.groups.not_in_creative_inventory == 0) and def.description and local stack = ItemStack(str)
def.description ~= "" then if filter_item(stack:get_name(), minetest.strip_colors(stack:get_description()), lang, filter)
if filter_item(string.lower(def.name), def.description, lang, filter) then and stack:get_name() ~= "mcl_enchanting:book_enchanted" then
table.insert(creative_list, name) table.insert(creative_list, stack:to_string())
end
end end
end end
for ench, def in pairs(mcl_enchanting.enchantments) do for ench, def in pairs(mcl_enchanting.enchantments) do

@ -8,6 +8,8 @@ local ROCKET_TIMEOUT = 1
local YAW_OFFSET = -math.pi/2 local YAW_OFFSET = -math.pi/2
local particle_explosion = vl_fireworks.particle_explosion
local function damage_explosion(self, damagemulitplier, pos) local function damage_explosion(self, damagemulitplier, pos)
if self._harmless then return end if self._harmless then return end
@ -27,194 +29,6 @@ local function damage_explosion(self, damagemulitplier, pos)
end end
end end
local function particle_explosion(pos)
if pos.object then pos = pos.object:get_pos() end
local particle_pattern = math.random(1, 3)
local fpitch
local type = math.random(1, 2)
local size = math.random(1, 3)
local colors = {"red", "yellow", "blue", "green", "white"}
local this_colors = {colors[math.random(#colors)], colors[math.random(#colors)], colors[math.random(#colors)]}
if size == 1 then
fpitch = math.random(200, 300)
elseif size == 2 then
fpitch = math.random(100, 130)
else
fpitch = math.random(60, 70)
end
if type == 1 then
core.sound_play("mcl_bows_firework", {
pos = pos,
max_hear_distance = 100,
gain = 3.0,
pitch = fpitch/100
}, true)
else
core.sound_play("mcl_bows_firework_soft", {
pos = pos,
max_hear_distance = 100,
gain = 4.0,
pitch = fpitch/100
}, true)
end
if particle_pattern == 1 then
core.add_particlespawner({
amount = 400 * size,
time = 0.0001,
minpos = pos,
maxpos = pos,
minvel = vector.new(-7 * size,-7 * size,-7 * size),
maxvel = vector.new(7 * size,7 * size,7 * size),
minexptime = .6 * size / 2,
maxexptime = .9 * size / 2,
minsize = 2 * size,
maxsize = 3 * size,
collisiondetection = false,
vertical = false,
texture = "mcl_bows_firework_"..this_colors[1]..".png",
glow = 14,
})
core.add_particlespawner({
amount = 400 * size,
time = 0.0001,
minpos = pos,
maxpos = pos,
minvel = vector.new(-2 * size,-2 * size,-2 * size),
maxvel = vector.new(2 * size,2 * size,2 * size),
minexptime = .6 * size / 2,
maxexptime = .9 * size / 2,
minsize = 2 * size,
maxsize = 3 * size,
collisiondetection = false,
vertical = false,
texture = "mcl_bows_firework_"..this_colors[2]..".png",
glow = 14,
})
core.add_particlespawner({
amount = 100 * size,
time = 0.0001,
minpos = pos,
maxpos = pos,
minvel = vector.new(-14 * size,-14 * size,-14 * size),
maxvel = vector.new(14 * size,14 * size,14 * size),
minexptime = .6 * size / 2,
maxexptime = .9 * size / 2,
minsize = 2 * size,
maxsize = 3 * size,
collisiondetection = false,
vertical = false,
texture = "mcl_bows_firework_"..this_colors[3]..".png",
glow = 14,
})
elseif particle_pattern == 2 then
core.add_particlespawner({
amount = 240 * size,
time = 0.0001,
minpos = pos,
maxpos = pos,
minvel = vector.new(-5 * size,-5 * size,-5 * size),
maxvel = vector.new(5 * size,5 * size,5 * size),
minexptime = .6 * size / 2,
maxexptime = .9 * size / 2,
minsize = 2 * size,
maxsize = 3 * size,
collisiondetection = false,
vertical = false,
texture = "mcl_bows_firework_"..this_colors[1]..".png",
glow = 14,
})
core.add_particlespawner({
amount = 500 * size,
time = 0.0001,
minpos = pos,
maxpos = pos,
minvel = vector.new(-2 * size,-2 * size,-2 * size),
maxvel = vector.new(2 * size,2 * size,2 * size),
minexptime = .6 * size / 2,
maxexptime = .9 * size / 2,
minsize = 2 * size,
maxsize = 3 * size,
collisiondetection = false,
vertical = false,
texture = "mcl_bows_firework_"..this_colors[2]..".png",
glow = 14,
})
core.add_particlespawner({
amount = 350 * size,
time = 0.0001,
minpos = pos,
maxpos = pos,
minvel = vector.new(-3 * size,-3 * size,-3 * size),
maxvel = vector.new(3 * size,3 * size,3 * size),
minexptime = .6 * size / 2,
maxexptime = .9 * size / 2,
minsize = 2 * size,
maxsize = 3 * size,
collisiondetection = false,
vertical = false,
texture = "mcl_bows_firework_"..this_colors[3]..".png",
glow = 14,
})
elseif particle_pattern == 3 then
core.add_particlespawner({
amount = 400 * size,
time = 0.0001,
minpos = pos,
maxpos = pos,
minvel = vector.new(-6 * size,-4 * size,-6 * size),
maxvel = vector.new(6 * size,4 * size,6 * size),
minexptime = .6 * size,
maxexptime = .9 * size,
minsize = 2 * size,
maxsize = 3 * size,
collisiondetection = false,
vertical = false,
texture = "mcl_bows_firework_"..this_colors[1]..".png",
glow = 14,
})
core.add_particlespawner({
amount = 120 * size,
time = 0.0001,
minpos = pos,
maxpos = pos,
minvel = vector.new(-8 * size,6 * size,-8 * size),
maxvel = vector.new(8 * size,6 * size,8 * size),
minexptime = .6 * size,
maxexptime = .9 * size,
minsize = 2 * size,
maxsize = 3 * size,
collisiondetection = false,
vertical = false,
texture = "mcl_bows_firework_"..this_colors[2]..".png",
glow = 14,
})
core.add_particlespawner({
amount = 130 * size,
time = 0.0001,
minpos = pos,
maxpos = pos,
minvel = vector.new(-3 * size,3 * size,-3 * size),
maxvel = vector.new(3 * size,3 * size,3 * size),
minexptime = .6 * size,
maxexptime = .9 * size,
minsize = 2 * size,
maxsize = 3 * size,
collisiondetection = false,
vertical = false,
texture = "mcl_bows_firework_"..this_colors[3]..".png",
glow = 14,
})
end
return size
end
core.register_craftitem("mcl_bows:rocket", { core.register_craftitem("mcl_bows:rocket", {
description = S("Arrow"), description = S("Arrow"),
_tt_help = S("Ammunition").."\n"..S("Damage from bow: 1-10").."\n"..S("Damage from dispenser: 3"), _tt_help = S("Ammunition").."\n"..S("Damage from bow: 1-10").."\n"..S("Damage from dispenser: 3"),
@ -283,7 +97,7 @@ if core.get_modpath("mcl_core") and core.get_modpath("mcl_mobitems") then
output = "mcl_bows:rocket 1", output = "mcl_bows:rocket 1",
recipe = { recipe = {
{"mcl_core:paper"}, {"mcl_core:paper"},
{"mcl_fireworks:rocket_2"}, {"vl_fireworks:rocket_2"},
{"mcl_bows:arrow"}, {"mcl_bows:arrow"},
} }
}) })

@ -1,17 +0,0 @@
minetest.register_craft({
type = "shapeless",
output = "mcl_fireworks:rocket_1 3",
recipe = {"mcl_core:paper", "mcl_mobitems:gunpowder"},
})
minetest.register_craft({
type = "shapeless",
output = "mcl_fireworks:rocket_2 3",
recipe = {"mcl_core:paper", "mcl_mobitems:gunpowder", "mcl_mobitems:gunpowder"},
})
minetest.register_craft({
type = "shapeless",
output = "mcl_fireworks:rocket_3 3",
recipe = {"mcl_core:paper", "mcl_mobitems:gunpowder", "mcl_mobitems:gunpowder", "mcl_mobitems:gunpowder"},
})

@ -1,4 +0,0 @@
local path = minetest.get_modpath("mcl_fireworks")
dofile(path .. "/register.lua")
dofile(path .. "/crafting.lua")

@ -1,28 +0,0 @@
local S = minetest.get_translator(minetest.get_current_modname())
local tt_help = S("Flight Duration:")
local description = S("Firework Rocket")
local function register_rocket(n, duration, force)
minetest.register_craftitem("mcl_fireworks:rocket_" .. n, {
description = description,
_tt_help = tt_help .. " " .. duration,
inventory_image = "mcl_fireworks_rocket.png",
stack_max = 64,
on_use = function(itemstack, user, pointed_thing)
local elytra = mcl_playerplus.elytra[user]
if elytra.active and elytra.rocketing <= 0 then
elytra.rocketing = duration
if not minetest.is_creative_enabled(user:get_player_name()) then
itemstack:take_item()
end
minetest.sound_play("mcl_fireworks_rocket", {pos = user:get_pos()})
end
return itemstack
end,
})
end
register_rocket(1, 2.2, 10)
register_rocket(2, 4.5, 20)
register_rocket(3, 6, 30)

@ -313,8 +313,7 @@ minetest.register_craftitem("mcl_mobitems:nether_star", {
_doc_items_longdesc = S("A nether star is dropped when the Wither dies. Place it in an item frame to show the world how hardcore you are! Or just as decoration."), _doc_items_longdesc = S("A nether star is dropped when the Wither dies. Place it in an item frame to show the world how hardcore you are! Or just as decoration."),
wield_image = "mcl_mobitems_nether_star.png", wield_image = "mcl_mobitems_nether_star.png",
inventory_image = "mcl_mobitems_nether_star.png", inventory_image = "mcl_mobitems_nether_star.png",
-- TODO: Reveal item when it's useful groups = { craftitem = 1 },
groups = { craftitem = 1, not_in_creative_inventory = 1 },
stack_max = 64, stack_max = 64,
}) })

@ -1,6 +1,6 @@
Firework mod for VoxeLibre Firework mod for VoxeLibre
by NO11 and and some parts by j45 by Herowl and teknomunk, based on the old version by NO11 and j45
Sound credits: Sound credits:

@ -0,0 +1,95 @@
-- Firework Star
core.register_craft({ -- temporary
type = "shapeless",
output = "vl_fireworks:firework_star",
recipe = {"mcl_mobitems:gunpowder", "mcl_core:clay_lump"}
})
core.register_craft({ -- temporary
type = "shapeless",
output = "vl_fireworks:firework_star",
recipe = {"mcl_mobitems:gunpowder", "mcl_core:clay_lump", "mcl_fire:fire_charge"}
})
core.register_craft({ -- temporary
type = "shapeless",
output = "vl_fireworks:firework_star",
recipe = {"mcl_mobitems:gunpowder", "mcl_core:clay_lump", "mcl_end:crystal"}
})
local function craft_star(itemstack, player, old_grid)
if itemstack:get_name() ~= "vl_fireworks:firework_star" then return end
local size = 1
-- analyze the recipe used
for _, item in pairs(old_grid) do
if item:get_name() == "mcl_fire:fire_charge" then
size = 2
break
end
if item:get_name() == "mcl_end:crystal" then
size = 3
break
end
end
local effect = {
fn = "generic",
size = size
}
itemstack:get_meta():set_string("vl_fireworks:star_effect", core.serialize(effect))
tt.reload_itemstack_description(itemstack)
return itemstack
end
core.register_craft_predict(craft_star)
core.register_on_craft(craft_star)
-- Firework Rocket
local function register_firework_crafts()
local r1 = {"mcl_core:paper"}
local r2 = table.copy(r1)
table.insert(r2, "vl_fireworks:firework_star") -- TODO replace with a loop or such to allow more stars
for i=1, 3 do
table.insert(r1, "mcl_mobitems:gunpowder")
table.insert(r2, "mcl_mobitems:gunpowder")
core.register_craft({
type = "shapeless",
output = "vl_fireworks:rocket 3",
recipe = r1,
})
core.register_craft({
type = "shapeless",
output = "vl_fireworks:rocket 3",
recipe = r2,
})
end
end
register_firework_crafts()
local function craft_firework(itemstack, player, old_grid)
if itemstack:get_name() ~= "vl_fireworks:rocket" then return end
local gp = 0 -- gunpowder
local stars = {}
-- analyze the recipe used
for _, item in pairs(old_grid) do
if item:get_name() == "mcl_mobitems:gunpowder" then gp = gp + 1 end
if item:get_name() == "vl_fireworks:firework_star" then
local effect = item:get_meta():get("vl_fireworks:star_effect")
or core.serialize({fn="generic"})
table.insert(stars, effect)
end
end
-- determine duration and force from the amount of gunpowder used
local tbl = vl_fireworks.firework_def._vl_fireworks_std_durs_forces[gp]
local meta = itemstack:get_meta()
meta:set_float("vl_fireworks:duration", tbl[1])
meta:set_int("vl_fireworks:force", tbl[2])
-- write star effects into metadata
meta:set_string("vl_fireworks:stars", core.serialize(stars))
tt.reload_itemstack_description(itemstack)
return itemstack
end
core.register_craft_predict(craft_firework)
core.register_on_craft(craft_firework)

@ -0,0 +1,196 @@
local path = minetest.get_modpath("vl_fireworks")
vl_fireworks = {}
local colors = {"red", "yellow", "blue", "green", "white"}
function vl_fireworks.generic_particle_explosion(pos, size)
if pos.object then pos = pos.object:get_pos() end
local particle_pattern = math.random(1, 3)
local fpitch
local type = math.random(1, 2)
local size = size or math.random(1, 3)
local this_colors = {colors[math.random(#colors)], colors[math.random(#colors)], colors[math.random(#colors)]}
if size == 1 then
fpitch = math.random(200, 300)
elseif size == 2 then
fpitch = math.random(100, 130)
else
fpitch = math.random(60, 70)
end
if type == 1 then
core.sound_play("mcl_bows_firework", {
pos = pos,
max_hear_distance = 100,
gain = 3.0,
pitch = fpitch/100
}, true)
else
core.sound_play("mcl_bows_firework_soft", {
pos = pos,
max_hear_distance = 100,
gain = 4.0,
pitch = fpitch/100
}, true)
end
if particle_pattern == 1 then
core.add_particlespawner({
amount = 400 * size,
time = 0.0001,
minpos = pos,
maxpos = pos,
minvel = vector.new(-7 * size,-7 * size,-7 * size),
maxvel = vector.new(7 * size,7 * size,7 * size),
minexptime = .6 * size / 2,
maxexptime = .9 * size / 2,
minsize = 2 * size,
maxsize = 3 * size,
collisiondetection = false,
vertical = false,
texture = "mcl_bows_firework_"..this_colors[1]..".png",
glow = 14,
})
core.add_particlespawner({
amount = 400 * size,
time = 0.0001,
minpos = pos,
maxpos = pos,
minvel = vector.new(-2 * size,-2 * size,-2 * size),
maxvel = vector.new(2 * size,2 * size,2 * size),
minexptime = .6 * size / 2,
maxexptime = .9 * size / 2,
minsize = 2 * size,
maxsize = 3 * size,
collisiondetection = false,
vertical = false,
texture = "mcl_bows_firework_"..this_colors[2]..".png",
glow = 14,
})
core.add_particlespawner({
amount = 100 * size,
time = 0.0001,
minpos = pos,
maxpos = pos,
minvel = vector.new(-14 * size,-14 * size,-14 * size),
maxvel = vector.new(14 * size,14 * size,14 * size),
minexptime = .6 * size / 2,
maxexptime = .9 * size / 2,
minsize = 2 * size,
maxsize = 3 * size,
collisiondetection = false,
vertical = false,
texture = "mcl_bows_firework_"..this_colors[3]..".png",
glow = 14,
})
elseif particle_pattern == 2 then
core.add_particlespawner({
amount = 240 * size,
time = 0.0001,
minpos = pos,
maxpos = pos,
minvel = vector.new(-5 * size,-5 * size,-5 * size),
maxvel = vector.new(5 * size,5 * size,5 * size),
minexptime = .6 * size / 2,
maxexptime = .9 * size / 2,
minsize = 2 * size,
maxsize = 3 * size,
collisiondetection = false,
vertical = false,
texture = "mcl_bows_firework_"..this_colors[1]..".png",
glow = 14,
})
core.add_particlespawner({
amount = 500 * size,
time = 0.0001,
minpos = pos,
maxpos = pos,
minvel = vector.new(-2 * size,-2 * size,-2 * size),
maxvel = vector.new(2 * size,2 * size,2 * size),
minexptime = .6 * size / 2,
maxexptime = .9 * size / 2,
minsize = 2 * size,
maxsize = 3 * size,
collisiondetection = false,
vertical = false,
texture = "mcl_bows_firework_"..this_colors[2]..".png",
glow = 14,
})
core.add_particlespawner({
amount = 350 * size,
time = 0.0001,
minpos = pos,
maxpos = pos,
minvel = vector.new(-3 * size,-3 * size,-3 * size),
maxvel = vector.new(3 * size,3 * size,3 * size),
minexptime = .6 * size / 2,
maxexptime = .9 * size / 2,
minsize = 2 * size,
maxsize = 3 * size,
collisiondetection = false,
vertical = false,
texture = "mcl_bows_firework_"..this_colors[3]..".png",
glow = 14,
})
elseif particle_pattern == 3 then
core.add_particlespawner({
amount = 400 * size,
time = 0.0001,
minpos = pos,
maxpos = pos,
minvel = vector.new(-6 * size,-4 * size,-6 * size),
maxvel = vector.new(6 * size,4 * size,6 * size),
minexptime = .6 * size,
maxexptime = .9 * size,
minsize = 2 * size,
maxsize = 3 * size,
collisiondetection = false,
vertical = false,
texture = "mcl_bows_firework_"..this_colors[1]..".png",
glow = 14,
})
core.add_particlespawner({
amount = 120 * size,
time = 0.0001,
minpos = pos,
maxpos = pos,
minvel = vector.new(-8 * size,6 * size,-8 * size),
maxvel = vector.new(8 * size,6 * size,8 * size),
minexptime = .6 * size,
maxexptime = .9 * size,
minsize = 2 * size,
maxsize = 3 * size,
collisiondetection = false,
vertical = false,
texture = "mcl_bows_firework_"..this_colors[2]..".png",
glow = 14,
})
core.add_particlespawner({
amount = 130 * size,
time = 0.0001,
minpos = pos,
maxpos = pos,
minvel = vector.new(-3 * size,3 * size,-3 * size),
maxvel = vector.new(3 * size,3 * size,3 * size),
minexptime = .6 * size,
maxexptime = .9 * size,
minsize = 2 * size,
maxsize = 3 * size,
collisiondetection = false,
vertical = false,
texture = "mcl_bows_firework_"..this_colors[3]..".png",
glow = 14,
})
end
return size
end
dofile(path .. "/star.lua")
dofile(path .. "/rockets.lua")
dofile(path .. "/crafting.lua")

@ -1,2 +1,3 @@
name = mcl_fireworks name = vl_fireworks
description = Adds fun fireworks to the game which players can use. description = Adds fun fireworks to the game which players can use.
depends = vl_projectile

@ -0,0 +1,30 @@
# Blender 3.6.7
# www.blender.org
o Plane
v -0.500000 1.000000 0.500000
v 0.500000 1.000000 0.500000
v -0.500000 1.000000 -0.500000
v 0.500000 1.000000 -0.500000
v -0.300517 0.000000 0.300517
v 0.300517 0.000000 -0.300517
v -0.300517 1.500000 0.300517
v 0.300517 1.500000 -0.300517
v 0.300517 0.000000 0.300517
v -0.300517 0.000000 -0.300517
v 0.300517 1.500000 0.300517
v -0.300517 1.500000 -0.300517
vn -0.0000 1.0000 -0.0000
vn 0.7071 -0.0000 0.7071
vn 0.7071 -0.0000 -0.7071
vt 0.562500 0.562500
vt 1.000000 0.562500
vt 1.000000 1.000000
vt 0.562500 1.000000
vt -0.000000 0.125000
vt 0.437500 0.125000
vt 0.437500 0.937500
vt -0.000000 0.937500
s 0
f 1/1/1 2/2/1 4/3/1 3/4/1
f 5/5/2 6/6/2 8/7/2 7/8/2
f 9/5/3 10/6/3 12/7/3 11/8/3

@ -0,0 +1,201 @@
local S = core.get_translator(core.get_current_modname())
local tt_help = S("Flight Duration:")
local description = S("Firework Rocket")
local TAU = 2*math.pi
local function explode(self, pos, stars)
mcl_mobs.mob_class.safe_boom(self, pos, 1)
if not stars then return end
for _, effect in pairs(stars) do
if type(effect) == "string" then effect = core.deserialize(effect) end
if effect.fn == "generic" then
vl_fireworks.generic_particle_explosion(pos, effect.size or 1)
end
-- TODO implement other handlers
end
end
local firework_entity = {
physical = true,
pointable = false,
visual = "mesh",
visual_size = {x=3, y=3},
mesh = "vl_fireworks_rocket.obj",
textures = {"vl_fireworks_entity.png"},
backface_culling = false,
collisionbox = {-0.1, 0, -0.1, 0.1, 0.5, 0.1},
collide_with_objects = false,
liquid_drag = true,
_fire_damage_resistant = true,
_save_fields = {
"last_pos", "vl_projectile", "dir", "rot_axis", "force", "stars"
},
_vector_save_fields = {
last_pos = true, dir = true, rot_axis = true, force = true
},
_damage=1, -- Damage on impact
_blocked = false,
_viscosity=0, -- Viscosity of node the arrow is currently in
_vl_projectile = {
ignore_gravity = true,
survive_collision = false,
damages_players = true,
maximum_time = 3,
pitch_offset = -math.pi / 2,
damage_groups = function(self)
return { fleshy = vector.length(self.object:get_velocity()) }
end,
tracer_texture = "mobs_mc_arrow_particle.png",
behaviors = {
vl_projectile.burns,
vl_projectile.has_tracer,
function(self, dtime)
if self._vl_projectile.extra then
local e = self._vl_projectile.extra
self._force = e.force/10 + 5
self._vl_projectile.maximum_time = e.dur
self._rot_axis = e.rot_axis
self._dir = self.object:get_velocity():normalize()
self._stars = e.stars
self._vl_projectile.extra = nil
end
if not self._dir then return end
if self._last_pos and (self._last_pos - self.object:get_pos()):length() < (10*dtime) then
self._rot_axis = -self._rot_axis
end
self._dir = self._dir:rotate_around_axis(self._rot_axis, dtime/3)
local obj = self.object
obj:set_velocity((obj:get_velocity():length() + self._force*dtime) * self._dir)
end,
vl_projectile.collides_with_solids,
vl_projectile.raycast_collides_with_entities,
},
allow_punching = function(self, entity_def, projectile_def, object)
local lua = object:get_luaentity()
if lua and lua.name == "mobs_mc:rover" then return false end
-- TODO at some point make it so impact depends on collision speed? (see next line)
--if (self.object:get_velocity() + object:get_velocity()).length() < 5 then return end
return true
end,
},
get_staticdata = function(self)
local out = {}
local save_fields = self._save_fields
for i = 1,#save_fields do
local field = save_fields[i]
out[field] = self["_"..field]
end
out.timer = self.timer
-- Preserve entity properties
out.properties = self.object:get_properties()
return core.serialize(out)
end,
on_activate = function(self, staticdata, dtime_s)
self.object:set_armor_groups({ immortal = 1 })
self._time_in_air = 1.0
local data = core.deserialize(staticdata)
if not data then return end
-- Restore entity properties
if data.properties then
self.object:set_properties(data.properties)
data.properties = nil
end
self.timer = data.timer
-- Restore rocket state
local save_fields = self._save_fields
local vecs = self._vector_save_fields
for i = 1,#save_fields do
local field = save_fields[i]
local d = data[field]
if type(d) == "table" and vecs[field] then
d = vector.new(d.x, d.y, d.z)
end
self["_"..field] = d
end
if not self._vl_projectile then
self._vl_projetile = {}
end
end,
_on_remove = function(self)
explode(self, self.object:get_pos(), self._stars)
end,
}
vl_projectile.register("vl_fireworks:rocket", firework_entity)
function vl_fireworks.shoot_firework(itemstack, pos, dir)
local meta = itemstack:get_meta()
local rot_axis = vector.new(1,0,0)
rot_axis = rot_axis:rotate_around_axis(vector.new(0,1,0), math.random()*TAU)
local stars = meta:get("vl_fireworks:stars") or core.serialize({})
vl_projectile.create("vl_fireworks:rocket", {
pos = pos,
dir = dir or vector.new(0,1,0),
velocity = 1 + meta:get_int("vl_fireworks:force")/10,
extra = {
dur = meta:get_float("vl_fireworks:duration"),
force = meta:get_float("vl_fireworks:force"),
rot_axis = rot_axis,
stars = core.deserialize(stars)
}
})
end
local firework_def = {
description = description,
inventory_image = "vl_fireworks_rocket.png",
stack_max = 64,
on_use = function(itemstack, user, pointed_thing)
local elytra = mcl_playerplus.elytra[user]
if elytra.active and elytra.rocketing <= 0 then
elytra.rocketing = meta:get_float("vl_fireworks:duration")
if not core.is_creative_enabled(user:get_player_name()) then
itemstack:take_item()
end
core.sound_play("vl_fireworks_rocket", {pos = user:get_pos()})
end
return itemstack
end,
on_place = function(itemstack, user, pointed_thing)
local pos = pointed_thing.above
vl_fireworks.shoot_firework(itemstack, pos)
if mcl_gamemode.get_gamemode(user) ~= "creative" then
itemstack:take_item()
return itemstack
end
end,
_on_dispense = function(dropitem, pos, droppos, dropnode, dropdir)
vl_fireworks.shoot_firework(dropitem, pos, dropdir)
end,
_vl_fireworks_std_durs_forces = { {2.2, 10}, {4.5, 20}, {6, 30} },
_vl_fireworks_tt = function(duration, stars)
local retval = tt_help .. " " .. duration
for _, effect in pairs(stars) do
if type(effect) == "string" then effect = core.deserialize(effect) end
retval = retval .. "\n\n" .. vl_fireworks.star_tt(effect)
end
return retval
end,
}
vl_fireworks.firework_def = table.copy(firework_def)
core.register_craftitem("vl_fireworks:rocket", firework_def)

@ -0,0 +1,23 @@
local S = minetest.get_translator(minetest.get_current_modname())
minetest.register_craftitem("vl_fireworks:firework_star", {
description = S("Firework Star"),
_doc_items_longdesc = S("A firework star is the key component of a firework rocket which is responsible for the visible explosion."),
wield_image = "vl_fireworks_star.png",
inventory_image = "vl_fireworks_star.png",
groups = { craftitem = 1, firework_star = 1 },
stack_max = 64,
})
function vl_fireworks.star_tt(effect)
local s = ""
if effect.fn == "generic" then
s = S("Generic Firework Star")
end
if effect.size then
s = s .. "\n" .. S("Size:") .. " " .. effect.size
end
return s:trim()
end
-- TODO image handlers

Binary file not shown.

After

Width:  |  Height:  |  Size: 199 B

Before

Width:  |  Height:  |  Size: 195 B

After

Width:  |  Height:  |  Size: 195 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 241 B