mirror of
				https://github.com/luanti-org/luanti.git
				synced 2025-10-28 22:25:20 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			332 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
			
		
		
	
	
			332 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
| local F = minetest.formspec_escape
 | |
| 
 | |
| -- hashed node pos -> sound handle
 | |
| local played_sounds = {}
 | |
| 
 | |
| -- all of these can be set via the formspec
 | |
| local meta_keys = {
 | |
| 	-- SimpleSoundSpec
 | |
| 	"sss.name",
 | |
| 	"sss.gain",
 | |
| 	"sss.pitch",
 | |
| 	"sss.fade",
 | |
| 	-- sound parameters
 | |
| 	"sparam.gain",
 | |
| 	"sparam.pitch",
 | |
| 	"sparam.fade",
 | |
| 	"sparam.start_time",
 | |
| 	"sparam.loop",
 | |
| 	"sparam.pos",
 | |
| 	"sparam.object",
 | |
| 	"sparam.to_player",
 | |
| 	"sparam.exclude_player",
 | |
| 	"sparam.max_hear_distance",
 | |
| 	-- fade
 | |
| 	"fade.step",
 | |
| 	"fade.gain",
 | |
| 	-- other
 | |
| 	"ephemeral",
 | |
| }
 | |
| 
 | |
| local function get_all_metadata(meta)
 | |
| 	return {
 | |
| 		sss = {
 | |
| 			name  = meta:get_string("sss.name"),
 | |
| 			gain  = meta:get_string("sss.gain"),
 | |
| 			pitch = meta:get_string("sss.pitch"),
 | |
| 			fade  = meta:get_string("sss.fade"),
 | |
| 		},
 | |
| 		sparam = {
 | |
| 			gain              = meta:get_string("sparam.gain"),
 | |
| 			pitch             = meta:get_string("sparam.pitch"),
 | |
| 			fade              = meta:get_string("sparam.fade"),
 | |
| 			start_time       = meta:get_string("sparam.start_time"),
 | |
| 			loop              = meta:get_string("sparam.loop"),
 | |
| 			pos               = meta:get_string("sparam.pos"),
 | |
| 			object            = meta:get_string("sparam.object"),
 | |
| 			to_player         = meta:get_string("sparam.to_player"),
 | |
| 			exclude_player    = meta:get_string("sparam.exclude_player"),
 | |
| 			max_hear_distance = meta:get_string("sparam.max_hear_distance"),
 | |
| 		},
 | |
| 		fade = {
 | |
| 			gain = meta:get_string("fade.gain"),
 | |
| 			step = meta:get_string("fade.step"),
 | |
| 		},
 | |
| 		ephemeral = meta:get_string("ephemeral"),
 | |
| 	}
 | |
| end
 | |
| 
 | |
| local function log_msg(msg)
 | |
| 	minetest.log("action", msg)
 | |
| 	minetest.chat_send_all(msg)
 | |
| end
 | |
| 
 | |
| local function try_call(f, ...)
 | |
| 	local function log_on_err(success, errmsg, ...)
 | |
| 		if not success then
 | |
| 			log_msg("[soundstuff:jukebox] Call failed: "..errmsg)
 | |
| 		else
 | |
| 			return errmsg, ...
 | |
| 		end
 | |
| 	end
 | |
| 
 | |
| 	return log_on_err(pcall(f, ...))
 | |
| end
 | |
| 
 | |
| local function show_formspec(pos, player)
 | |
| 	local meta = minetest.get_meta(pos)
 | |
| 
 | |
| 	local md = get_all_metadata(meta)
 | |
| 
 | |
| 	local pos_hash = minetest.hash_node_position(pos)
 | |
| 	local sound_handle = played_sounds[pos_hash]
 | |
| 
 | |
| 	local fs = {}
 | |
| 	local function fs_add(str)
 | |
| 		table.insert(fs, str)
 | |
| 	end
 | |
| 
 | |
| 	fs_add([[
 | |
| 		formspec_version[6]
 | |
| 		size[14,12]
 | |
| 	]])
 | |
| 
 | |
| 	-- SimpleSoundSpec
 | |
| 	fs_add(string.format([[
 | |
| 		container[0.5,0.5]
 | |
| 		box[-0.1,-0.1;4.2,3.2;#EBEBEB20]
 | |
| 		style[*;font=mono,bold]
 | |
| 		label[0,0.25;SimpleSoundSpec]
 | |
| 		style[*;font=mono]
 | |
| 		field[0.00,1.00;4,0.75;sss.name;name;%s]
 | |
| 		field[0.00,2.25;1,0.75;sss.gain;gain;%s]
 | |
| 		field[1.25,2.25;1,0.75;sss.pitch;pitch;%s]
 | |
| 		field[2.50,2.25;1,0.75;sss.fade;fade;%s]
 | |
| 		container_end[]
 | |
| 		field_close_on_enter[sss.name;false]
 | |
| 		field_close_on_enter[sss.gain;false]
 | |
| 		field_close_on_enter[sss.pitch;false]
 | |
| 		field_close_on_enter[sss.fade;false]
 | |
| 	]], F(md.sss.name), F(md.sss.gain), F(md.sss.pitch), F(md.sss.fade)))
 | |
| 
 | |
| 	-- sound parameter table
 | |
| 	fs_add(string.format([[
 | |
| 		container[5.5,0.5]
 | |
| 		box[-0.1,-0.1;4.2,10.7;#EBEBEB20]
 | |
| 		style[*;font=mono,bold]
 | |
| 		label[0,0.25;sound parameter table]
 | |
| 		style[*;font=mono]
 | |
| 		field[0.00,1;1,0.75;sparam.gain;gain;%s]
 | |
| 		field[1.25,1;1,0.75;sparam.pitch;pitch;%s]
 | |
| 		field[2.50,1;1,0.75;sparam.fade;fade;%s]
 | |
| 		field[0,2.25;4,0.75;sparam.start_time;start_time;%s]
 | |
| 		field[0,3.50;4,0.75;sparam.loop;loop;%s]
 | |
| 		field[0,4.75;4,0.75;sparam.pos;pos;%s]
 | |
| 		field[0,6.00;4,0.75;sparam.object;object;%s]
 | |
| 		field[0,7.25;4,0.75;sparam.to_player;to_player;%s]
 | |
| 		field[0,8.50;4,0.75;sparam.exclude_player;exclude_player;%s]
 | |
| 		field[0,9.75;4,0.75;sparam.max_hear_distance;max_hear_distance;%s]
 | |
| 		container_end[]
 | |
| 		field_close_on_enter[sparam.gain;false]
 | |
| 		field_close_on_enter[sparam.pitch;false]
 | |
| 		field_close_on_enter[sparam.fade;false]
 | |
| 		field_close_on_enter[sparam.start_time;false]
 | |
| 		field_close_on_enter[sparam.loop;false]
 | |
| 		field_close_on_enter[sparam.pos;false]
 | |
| 		field_close_on_enter[sparam.object;false]
 | |
| 		field_close_on_enter[sparam.to_player;false]
 | |
| 		field_close_on_enter[sparam.exclude_player;false]
 | |
| 		field_close_on_enter[sparam.max_hear_distance;false]
 | |
| 		tooltip[sparam.object;Get a name with the Branding Iron.]
 | |
| 	]], F(md.sparam.gain), F(md.sparam.pitch), F(md.sparam.fade),
 | |
| 			F(md.sparam.start_time), F(md.sparam.loop), F(md.sparam.pos),
 | |
| 			F(md.sparam.object), F(md.sparam.to_player), F(md.sparam.exclude_player),
 | |
| 			F(md.sparam.max_hear_distance)))
 | |
| 
 | |
| 	-- fade
 | |
| 	fs_add(string.format([[
 | |
| 		container[10.75,3]
 | |
| 		box[-0.1,-0.1;3.2,3.2;#EBEBEB20]
 | |
| 		style[*;font=mono,bold]
 | |
| 		label[0,0.25;fade]
 | |
| 		style[*;font=mono]
 | |
| 		field[0.00,1;1,0.75;fade.step;step;%s]
 | |
| 		field[1.25,1;1,0.75;fade.gain;gain;%s]
 | |
| 	]], F(md.fade.step), F(md.fade.gain)))
 | |
| 	if not sound_handle then
 | |
| 		fs_add([[
 | |
| 			box[0,2;3,0.75;#363636FF]
 | |
| 			label[0.25,2.375;no sound]
 | |
| 		]])
 | |
| 	else
 | |
| 		fs_add([[
 | |
| 			button[0,2;3,0.75;btn_fade;Fade]
 | |
| 		]])
 | |
| 	end
 | |
| 	fs_add([[
 | |
| 		container_end[]
 | |
| 		field_close_on_enter[fade.step;false]
 | |
| 		field_close_on_enter[fade.gain;false]
 | |
| 	]])
 | |
| 
 | |
| 	-- ephemeral
 | |
| 	fs_add(string.format([[
 | |
| 		checkbox[0.5,5;ephemeral;ephemeral;%s]
 | |
| 	]], md.ephemeral))
 | |
| 
 | |
| 	-- play/stop and release buttons
 | |
| 	if not sound_handle then
 | |
| 		fs_add([[
 | |
| 			container[10.75,0.5]
 | |
| 			button[0,0;3,0.75;btn_play;Play]
 | |
| 			container_end[]
 | |
| 		]])
 | |
| 	else
 | |
| 		fs_add([[
 | |
| 			container[10.75,0.5]
 | |
| 			button[0,0;3,0.75;btn_stop;Stop]
 | |
| 			button[0,1;3,0.75;btn_release;Release]
 | |
| 			container_end[]
 | |
| 		]])
 | |
| 	end
 | |
| 
 | |
| 	-- save and quit button
 | |
| 	fs_add([[
 | |
| 		button_exit[10.75,11;3,0.75;btn_save_quit;Save & Quit]
 | |
| 	]])
 | |
| 
 | |
| 	minetest.show_formspec(player:get_player_name(), "soundstuff:jukebox@"..pos:to_string(),
 | |
| 			table.concat(fs))
 | |
| end
 | |
| 
 | |
| minetest.register_node("soundstuff:jukebox", {
 | |
| 	description = "Jukebox\nAllows to play arbitrary sounds.",
 | |
| 	tiles = {"soundstuff_jukebox.png"},
 | |
| 	groups = {dig_immediate = 2},
 | |
| 
 | |
| 	on_construct = function(pos)
 | |
| 		local meta = minetest.get_meta(pos)
 | |
| 		-- SimpleSoundSpec
 | |
| 		meta:set_string("sss.name", "")
 | |
| 		meta:set_string("sss.gain", "")
 | |
| 		meta:set_string("sss.pitch", "")
 | |
| 		meta:set_string("sss.fade", "")
 | |
| 		-- sound parameters
 | |
| 		meta:set_string("sparam.gain", "")
 | |
| 		meta:set_string("sparam.pitch", "")
 | |
| 		meta:set_string("sparam.fade", "")
 | |
| 		meta:set_string("sparam.start_time", "")
 | |
| 		meta:set_string("sparam.loop", "")
 | |
| 		meta:set_string("sparam.pos", pos:to_string())
 | |
| 		meta:set_string("sparam.object", "")
 | |
| 		meta:set_string("sparam.to_player", "")
 | |
| 		meta:set_string("sparam.exclude_player", "")
 | |
| 		meta:set_string("sparam.max_hear_distance", "")
 | |
| 		-- fade
 | |
| 		meta:set_string("fade.gain", "")
 | |
| 		meta:set_string("fade.step", "")
 | |
| 		-- other
 | |
| 		meta:set_string("ephemeral", "")
 | |
| 
 | |
| 		meta:mark_as_private(meta_keys)
 | |
| 	end,
 | |
| 
 | |
| 	on_rightclick = function(pos, _node, clicker, _itemstack, _pointed_thing)
 | |
| 		show_formspec(pos, clicker)
 | |
| 	end,
 | |
| })
 | |
| 
 | |
| minetest.register_on_player_receive_fields(function(player, formname, fields)
 | |
| 	if formname:sub(1, 19) ~= "soundstuff:jukebox@" then
 | |
| 		return false
 | |
| 	end
 | |
| 
 | |
| 	local pos = vector.from_string(formname, 20)
 | |
| 	if not pos or pos ~= pos:round() then
 | |
| 		minetest.log("error", "[soundstuff:jukebox] Invalid formname.")
 | |
| 		return true
 | |
| 	end
 | |
| 
 | |
| 	local meta = minetest.get_meta(pos)
 | |
| 
 | |
| 	for _, k in ipairs(meta_keys) do
 | |
| 		if fields[k] then
 | |
| 			meta:set_string(k, fields[k])
 | |
| 		end
 | |
| 	end
 | |
| 	meta:mark_as_private(meta_keys)
 | |
| 
 | |
| 	local pos_hash = minetest.hash_node_position(pos)
 | |
| 	local sound_handle = played_sounds[pos_hash]
 | |
| 
 | |
| 	if not sound_handle then
 | |
| 		if fields.btn_play then
 | |
| 			local md = get_all_metadata(meta)
 | |
| 
 | |
| 			local sss = {
 | |
| 				name = md.sss.name,
 | |
| 				gain  = tonumber(md.sss.gain),
 | |
| 				pitch = tonumber(md.sss.pitch),
 | |
| 				fade  = tonumber(md.sss.fade),
 | |
| 			}
 | |
| 			local sparam = {
 | |
| 				gain  = tonumber(md.sparam.gain),
 | |
| 				pitch = tonumber(md.sparam.pitch),
 | |
| 				fade  = tonumber(md.sparam.fade),
 | |
| 				start_time = tonumber(md.sparam.start_time),
 | |
| 				loop = minetest.is_yes(md.sparam.loop),
 | |
| 				pos = vector.from_string(md.sparam.pos),
 | |
| 				object = testtools.get_branded_object(md.sparam.object),
 | |
| 				to_player = md.sparam.to_player,
 | |
| 				exclude_player = md.sparam.exclude_player,
 | |
| 				max_hear_distance = tonumber(md.sparam.max_hear_distance),
 | |
| 			}
 | |
| 			local ephemeral = minetest.is_yes(md.ephemeral)
 | |
| 
 | |
| 			log_msg(string.format(
 | |
| 					"[soundstuff:jukebox] Playing sound: minetest.sound_play(%s, %s, %s)",
 | |
| 					string.format("{name=\"%s\", gain=%s, pitch=%s, fade=%s}",
 | |
| 							sss.name, sss.gain, sss.pitch, sss.fade),
 | |
| 					string.format("{gain=%s, pitch=%s, fade=%s, start_time=%s, loop=%s, pos=%s, "
 | |
| 						.."object=%s, to_player=\"%s\", exclude_player=\"%s\", max_hear_distance=%s}",
 | |
| 							sparam.gain, sparam.pitch, sparam.fade, sparam.start_time,
 | |
| 							sparam.loop, sparam.pos, sparam.object and "<objref>",
 | |
| 							sparam.to_player, sparam.exclude_player,
 | |
| 							sparam.max_hear_distance),
 | |
| 					tostring(ephemeral)))
 | |
| 
 | |
| 			sound_handle = try_call(minetest.sound_play, sss, sparam, ephemeral)
 | |
| 
 | |
| 			played_sounds[pos_hash] = sound_handle
 | |
| 			show_formspec(pos, player)
 | |
| 		end
 | |
| 
 | |
| 	else
 | |
| 		if fields.btn_stop then
 | |
| 			log_msg("[soundstuff:jukebox] Stopping sound: minetest.sound_stop(<handle>)")
 | |
| 
 | |
| 			try_call(minetest.sound_stop, sound_handle)
 | |
| 
 | |
| 		elseif fields.btn_release then
 | |
| 			log_msg("[soundstuff:jukebox] Releasing handle.")
 | |
| 
 | |
| 			played_sounds[pos_hash] = nil
 | |
| 			show_formspec(pos, player)
 | |
| 
 | |
| 		elseif fields.btn_fade then
 | |
| 			local md = get_all_metadata(meta)
 | |
| 
 | |
| 			local step = tonumber(md.fade.step)
 | |
| 			local gain = tonumber(md.fade.gain)
 | |
| 
 | |
| 			log_msg(string.format(
 | |
| 					"[soundstuff:jukebox] Fading sound: minetest.sound_fade(<handle>, %s, %s)",
 | |
| 					step, gain))
 | |
| 
 | |
| 			try_call(minetest.sound_fade, sound_handle, step, gain)
 | |
| 		end
 | |
| 	end
 | |
| 
 | |
| 	return true
 | |
| end)
 |