write something there

This commit is contained in:
N-Nachtigal 2025-05-04 16:01:41 +02:00
commit b4b6c08f4f
8546 changed files with 309825 additions and 0 deletions

View file

@ -0,0 +1,69 @@
# music_modpack
![Screenshot](screenshot.png)
## Overview
Music modpack with API for easy in-game music playback and custom track registration.
## Requirements
- Minetest 5.0.0+
- Minetest_game 5.0.0+
- [sfinv_buttons](https://repo.or.cz/minetest_sfinv_buttons.git) (Optional, but highly recommended: easy way to open music settings menu)
## Features
- Time-based music
- Elevation-based music
- Formspec for music settings
## Settings
By default, settings menu is only available with `/musicsettings` command. If you have sfinv_buttons installed, music menu is available in inventory->more->music settings
## Adding your own music
Call music.register_track() with following definition:
```Lua
music.register_track({
name = "my_track",
length = 200,
gain = 1,
day = true,
night = true,
ymin = 0,
ymax = 31000,
})
```
- name - name of the sound file in your mod sounds folder, without extension (.ogg)
- length - length of the track in seconds
- gain - volume, value from 0 to 1
- day - track will be played at day
- night - track will be played at night
- ymin - minimum elevation for track to play
- ymax - maximum elevation for track to play
## Settingtypes
Available settings that you can put in your minetest.conf directly, or access them via "Settings->All Settings->Mods->music_modpack" menu.
```
music_time_interval = integer, Interval between attempts to play music, default is 60
music_cleanup_interval = integer, interval between attempts to clean up player state, default is 5
music_global_gain = float, global music volume, default is 0.3
music_add_random_delay = boolean, if to add a random delay to interval between attempts, default is true
music_maximum_random_delay = - integer, maximum random delay in seconds, default is 30
music_display_playback_messages = boolean, display messages when music starts for a certain player, default is true
```
Music packs also provide settingtypes with corresponding height limits.
## Content
Default pack features 11 tracks from composer Kevin McLeod. Tracks are split into day tracks and night tracks. If music_dfcaverns is not enabled, night tracks also play underground (up to -31000).
Music_dfcaverns is an additional pack of music for underground layers from [dfcaverns](https://github.com/FaceDeer/dfcaverns/), featuring 13 tracks from composer Kevin McLeod. Tracks split into three categories, each for one cavern layer from dfcaverns, and their heights are set accordingly. Dfcaverns modpack is not required, however, and it is recommended to enable this pack even without dfcaverns, unless client connection speed is an issue.
## License
Code is licensed under GPLv3.
All music used in this mod was produced by Kevin McLeod and released under CC BY 4.0. [link to the license](https://creativecommons.org/licenses/by/4.0/).
Original music can be found [here](https://incompetech.com/music/royalty-free/music.html).

View file

@ -0,0 +1 @@
Music modpack with API for easy in-game music playback and custom track registration.

View file

View file

@ -0,0 +1,288 @@
music = {}
local players = {}
local tracks = {}
--Settingtypes
local time_interval = tonumber(minetest.settings:get("music_time_interval")) or 90
local cleanup_interval = tonumber(minetest.settings:get("music_cleanup_interval")) or 5
local global_gain = tonumber(minetest.settings:get("music_global_gain")) or 0.1
local add_random_delay = minetest.settings:get_bool("music_add_random_delay", true)
local maximum_random_delay = tonumber(minetest.settings:get("music_maximum_random_delay")) or 45
local display_playback_messages = minetest.settings:get_bool("music_display_playback_messages", true)
local random_delay = 0
--Initialize random delay on the first run
if add_random_delay then
random_delay = math.random(maximum_random_delay)
end
--Internal functions
local function load_player_settings(name)
local file = io.open(minetest.get_worldpath() .. "/music_settings.mt", "r")
if file then
local rawfile = file:read()
io.close(file)
if rawfile then
local settings = minetest.deserialize(rawfile)
if settings[name] then
players[name].settings = settings[name]
end
else
minetest.log("error", "[Music_api] Unable to read volume settings!")
end
end
end
local function save_player_settings(name)
local path = minetest.get_worldpath() .. "/music_settings.mt"
local file = io.open(path, "r")
local settings = {}
if file then
local rawfile = file:read()
io.close(file)
if rawfile then
settings = minetest.deserialize(rawfile) or {}
end
end
settings[name] = players[name].settings
file = io.open(path, "w")
if file then
local rawfile = minetest.serialize(settings)
file:write(rawfile)
io.close(file)
minetest.log("action", "[Music_api] Saving volume settings for " .. name)
else
minetest.log("error", "[Music_api] Unable to save volume settings!")
end
end
local function play_track(name)
-- Do not play music for dead players or if music is already playing
local p = players[name]
if not p or p.is_dead or p.playing then
return
end
local player = minetest.get_player_by_name(name)
local player_pos = player:get_pos()
local possible_tracks = {}
local time = minetest.get_timeofday()
--Assemble list of fitting tracks
for _,track in pairs(tracks) do
if track.name ~= p.previous and ((track.day and time > 0.25 and time < 0.75) or
(track.night and ((time < 0.25 and time >= 0) or (time > 0.75 and time <= 1)))) and
player_pos.y >= track.ymin and player_pos.y < track.ymax
then
table.insert(possible_tracks, track)
end
end
--Return if no music fits
if #possible_tracks == 0 then
p.previous = nil
return
end
--Select random track from fitting
local track = possible_tracks[math.random(#possible_tracks)]
--Start playback
if display_playback_messages then
minetest.log("action", "[Music_api]: Starting playblack for: " .. name .. " " .. track.name .. " Available tracks for user: " .. #possible_tracks .. " Random delay: " .. random_delay)
end
p.track_handle = minetest.sound_play(track.name, {to_player = name, gain = track.gain * global_gain * p.settings.gain})
p.playing = true
p.previous = track.name
p.playback_started = os.time()
p.track_def = track
end
local function stop_track(name,step)
local p = players[name]
if p and p.playing and p.track_handle then
minetest.sound_fade(p.track_handle,step or 0.01,0)
p.playing = false
p.track_handle = nil
p.playback_started = nil
p.track_def = nil
if display_playback_messages then
minetest.log("action", "[Music_api]: Stopping playback for: " .. name)
end
end
end
local function display_music_settings(name)
local user
if type(name) ~= "string" and name:is_player() then
user = name:get_player_name()
else
user = name
end
local volume = math.floor(players[user].settings.gain * 1000)
local formspec = "size[5,2]" .. default.gui_bg .. default.gui_bg_img ..
"textarea[0.3,0.06;2,1;;Volume:;]" ..
"scrollbar[0,0.6;4.8,0.25;horizontal;volume;" .. tostring(volume) .. "]" ..
"button[0,1.5;1,0.3;play;Play]" ..
"button[0.9,1.5;1,0.3;stop;Stop]" ..
"button_exit[3,1.5;2,0.3;accept;Accept]"
minetest.show_formspec(user, "music_settings", formspec)
end
--Registrations
minetest.register_on_player_receive_fields(function(player, formname, fields)
if formname ~= "music_settings" then return end
local name = player:get_player_name()
local p = players[name]
if fields.volume then
local params = minetest.explode_scrollbar_event(fields.volume)
p.settings.gain = params.value / 1000
end
if fields.play then
if p.playing then
stop_track(name,0.05)
end
play_track(name)
end
if fields.stop then
stop_track(name,0.05)
end
if fields.accept or fields.quit then
save_player_settings(name)
end
end)
minetest.register_on_joinplayer(function(player)
local name = player:get_player_name()
players[name] = {playing = false, playback_started = nil, track_handle = nil, track_def = nil, previous = nil, settings = {gain = 0.5}, is_dead = player:get_hp() <= 0}
load_player_settings(name)
end
)
minetest.register_on_leaveplayer(function(player)
local name = player:get_player_name()
players[name] = nil
end
)
minetest.register_chatcommand("musicsettings",{
params = "",
description = "Displays music settings menu",
privs = {shout = true},
func = display_music_settings
})
if minetest.get_modpath("sfinv_buttons") then
sfinv_buttons.register_button("show_music_settings",
{
title = "Music Settings",
action = display_music_settings,
tooltip = "Show music settings",
image = "music_sfinv_buttons_icon.png",
})
end
local cleanup_timer = 0
minetest.register_globalstep(function(dtime)
cleanup_timer = cleanup_timer + dtime
if cleanup_timer < cleanup_interval then return end
cleanup_timer = 0
for k,v in pairs(players) do
local track = v.track_def
if track then
if v.playing and os.time() > v.playback_started + track.length then
stop_track(k)
end
-- Stop music when it is no longer appropriate for the given conditions
if v.playing then
local time = minetest.get_timeofday()
local player = minetest.get_player_by_name(k)
if player then
local player_pos = player:get_pos()
if not ((track.day and time > 0.205 and time < 0.76) or
(track.night and ((time < 0.205 and time >= 0) or (time > 0.76 and time <= 1)))) or
player_pos.y < track.ymin or player_pos.y > track.ymax
then
stop_track(k)
play_track(k) -- start new track for the appropriate conditions
end
end
end
end
end
end)
local track_timer = 0
minetest.register_globalstep(function(dtime)
--Increment timer, return if it doesn't, reset it if it does and continue with function execution
track_timer = track_timer + dtime
if track_timer < time_interval + random_delay then return end
track_timer = 0
--Return if no tracks are defined
if next(tracks) == nil then return end
--Play music for every player
for k,v in pairs(players) do
play_track(k)
end
--Change random delay if enabled on each play attempt
if add_random_delay then
random_delay = math.random(maximum_random_delay)
end
end)
--API function
function music.register_track(def)
if def.name == nil or def.length == nil then
minetest.log("error", "[Music_api] Missing track definition parameters!")
return
end
local track_def = {
name = def.name,
length = def.length,
gain = def.gain or 1,
day = def.day or false,
night = def.night or false,
ymin = def.ymin or -31000,
ymax = def.ymax or 31000,
}
table.insert(tracks, track_def)
end
-- Don't play music for dead players
minetest.register_on_dieplayer(function(player)
local name = player:get_player_name()
stop_track(name,0.025)
players[name].is_dead = true
end)
-- Enable music for respawned players
minetest.register_on_dieplayer(function(player)
local name = player:get_player_name()
players[name].is_dead = false
end)

View file

@ -0,0 +1 @@
sfinv_buttons?

View file

@ -0,0 +1 @@
An api and manager for in-game music

View file

@ -0,0 +1,2 @@
local path = minetest.get_modpath("music_api")
dofile(path .. "/api.lua")

View file

@ -0,0 +1 @@
name = music_api

View file

@ -0,0 +1,17 @@
#Interval between attempts to play music
music_time_interval (Interval between attempts to play music) int 60
#Interval between attempts to clean up player playback state
music_cleanup_interval (Interval between attempts to clean up player playback state) int 5
#Music volume
music_global_gain (Music volume) float 0.3 0.0 1.0
#Add random delay to music
music_add_random_delay (Add random delay to each music start attempt) bool true
#Maximum random delay (from 0) to add
music_maximum_random_delay (Maximum random delay to add) int 30
#Display info messages in server console
music_display_playback_messages (Display info messages in server console) bool true

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB