diff --git a/_datafiles/config.yaml b/_datafiles/config.yaml index c3be12b6..ef610b9f 100755 --- a/_datafiles/config.yaml +++ b/_datafiles/config.yaml @@ -191,6 +191,7 @@ ConsistentAttackMessages: true CorpsesEnabled: true # - CorpseDecayTime - # How long until corpses crumble to dust (Go away). +# See ShopRestockRate comments for time format. CorpseDecayTime: 1 hour # - MaxAltCharacters - # How many characters beyond their original character can they create? Players @@ -217,6 +218,9 @@ NightHours: 8 # - LeaderboardSize - # Maximum size of each leaderboard. 0 to disable. LeaderboardSize: 10 +# - ContainerSizeMax - +# Maximum number of objects a container can hold before stuff overflows +ContainerSizeMax: 10 ################################################################################ # # MEMORY/CPU OPTIMIZATIONS diff --git a/_datafiles/html/public/webclient.html b/_datafiles/html/public/webclient.html index 1220b14d..80964688 100644 --- a/_datafiles/html/public/webclient.html +++ b/_datafiles/html/public/webclient.html @@ -20,7 +20,6 @@ position: relative; /* So absolutely positioned children can be placed relative to */ } - /* Ensure the main container is positioned relatively */ #main-container { flex: 1; position: relative; @@ -91,7 +90,6 @@ padding: 10px; cursor: pointer; z-index: 9999; - /* Optional background to make it stand out: */ background-color: rgba(30,30,30, 0.8); border-radius: 5px; } @@ -133,6 +131,22 @@ font-size: 20px; color: #ccc; } + + /* Mute Checkbox */ + #mute-container { + margin-bottom: 15px; + color: #ccc; + display: flex; + align-items: center; + } + #mute-container label { + cursor: pointer; + } + #mute-container input[type="checkbox"] { + margin-right: 8px; + transform: scale(1.2); + cursor: pointer; + } @@ -148,8 +162,14 @@

Volume Controls

+ +
+ + +
+
- +
@@ -173,11 +193,11 @@

Volume Controls

///////////////////////////////////////////// // - // Terminal emulation setup + // Terminal Setup // ///////////////////////////////////////////// const term = new window.Terminal({ - cols: 80, + cols: 80, rows: 60, cursorBlink: true, fontSize: 20 @@ -186,30 +206,28 @@

Volume Controls

term.loadAddon(fitAddon); term.open(document.getElementById('terminal')); - // Resize the terminal to fit its container - const resizeTerminal = () => { + function resizeTerminal() { fitAddon.fit(); - }; - resizeTerminal(); + } window.addEventListener('resize', resizeTerminal); + resizeTerminal(); ///////////////////////////////////////////// // - // Misc tracking + // Misc // ///////////////////////////////////////////// let payloadsReceived = 0; let totalBytesReceived = 0; let payloadsSent = 0; let totalBytesSent = 0; - // Command history let commandHistory = []; let historyPosition = 0; let commandHistoryMaxLength = 30; ///////////////////////////////////////////// // - // Basic WebSocket code + // WebSocket // ///////////////////////////////////////////// let socket = null; @@ -220,7 +238,6 @@

Volume Controls

const textOutput = document.getElementById('terminal'); const textInput = document.getElementById('command-input'); - // Key shortcuts const codeShortcuts = { "Numpad1": "southwest", "Numpad2": "south", @@ -261,12 +278,11 @@

Volume Controls

return false; } - connectButton.addEventListener('click', function() { - if ( socket && socket.readyState === WebSocket.OPEN ) { + connectButton.addEventListener('click', () => { + if (socket && socket.readyState === WebSocket.OPEN) { socket.close(); return; } - debugLog("Connecting to: " + 'ws://'+location.host +':80/ws'); socket = new WebSocket('ws://'+location.host +':80/ws'); @@ -282,29 +298,28 @@

Volume Controls

totalBytesReceived += event.data.length; // Check if it's a webclient command - if ( event.data.length > 9 && event.data.substring(8, 9) === ':' ) { - if ( handleWebclientCommand(event.data) ) { + if (event.data.length > 9 && event.data.substring(8, 9) === ':') { + if (handleWebclientCommand(event.data)) { return; } } // MSP commands - if ( event.data.length > 9 && event.data.slice(0, 2) === "!!") { - if ( event.data.slice(0, 8) === "!!MUSIC(" ) { - const parts = event.data.slice(8, event.data.length-1).split(" "); + if (event.data.length > 9 && event.data.slice(0, 2) === "!!") { + if (event.data.slice(0, 8) === "!!MUSIC(") { + const parts = event.data.slice(8, event.data.length - 1).split(" "); let fileName = parts[0]; let obj = {}; - for ( let i=1; iVolume Controls let soundLevel = 1.0; let loopMusic = true; - if ( obj.L && obj.L !== "-1" ) { + if (obj.L && obj.L !== "-1") { loopMusic = false; } - if ( obj.V ) { + if (obj.V) { soundLevel = Number(obj.V)/100; } - if ( !MusicPlayer.isPlaying(baseMp3Url+fileName) ) { - MusicPlayer.play(baseMp3Url+fileName, loopMusic, (sliderValues["music"]/100)); + if (!MusicPlayer.isPlaying(baseMp3Url + fileName)) { + MusicPlayer.play(baseMp3Url + fileName, loopMusic, soundLevel*(sliderValues["music"] / 100)); } } } - else if ( event.data.slice(0, 8) === "!!SOUND(" ) { - const parts = event.data.slice(8, event.data.length-1).split(" "); + else if (event.data.slice(0, 8) === "!!SOUND(") { + const parts = event.data.slice(8, event.data.length - 1).split(" "); let fileName = parts[0]; let obj = {}; - for ( let i=1; iVolume Controls socket.onclose = function(event) { if (event.wasClean) { - term.writeln("Connection closed cleanly, code=${event.code}, reason=${event.reason}"); + term.writeln(`Connection closed cleanly, code=${event.code}, reason=${event.reason}`); } else { term.writeln("Connection died"); } connectButton.disabled = false; - if ( textInput.type === "password" ) { + if (textInput.type === "password") { textInput.value = ''; textInput.type = "text"; } @@ -377,22 +403,22 @@

Volume Controls

textInput.addEventListener('keydown', function(event) { // Macro support: F-keys - if ( event.key.substring(0, 1) === "F" && event.key.length === 2 ) { + if (event.key.substring(0, 1) === "F" && event.key.length === 2) { let macroNum = event.key.substring(1); - SendData("="+macroNum); + SendData("=" + macroNum); if (event.preventDefault) event.preventDefault(); return false; } // ArrowUp / ArrowDown for command history - if ( event.key === 'ArrowUp' || event.key === 'ArrowDown' ) { - if ( event.key === 'ArrowUp' ) { + if (event.key === 'ArrowUp' || event.key === 'ArrowDown') { + if (event.key === 'ArrowUp') { historyPosition += 1; } else { historyPosition -= 1; } - if ( historyPosition < 1 ) historyPosition = 1; - if ( historyPosition > commandHistory.length ) historyPosition = commandHistory.length; + if (historyPosition < 1) historyPosition = 1; + if (historyPosition > commandHistory.length) historyPosition = commandHistory.length; event.target.value = commandHistory[ commandHistory.length - historyPosition ]; } @@ -404,12 +430,12 @@

Volume Controls

} // ENTER key - if ( event.key === 'Enter' ) { - if ( event.target.value !== '' ) { - if ( textInput.type !== "password" ) { + if (event.key === 'Enter') { + if (event.target.value !== '') { + if (textInput.type !== "password") { commandHistory.push(event.target.value); historyPosition = 0; - if ( commandHistory.length > commandHistoryMaxLength ) { + if (commandHistory.length > commandHistoryMaxLength) { commandHistory = commandHistory.slice(commandHistory.length - commandHistoryMaxLength); } } @@ -454,18 +480,10 @@

Volume Controls

} } - ///////////////////////////////////////////// - // - // Keep terminal focus on mouse events - // - ///////////////////////////////////////////// - textOutput.addEventListener('mousedown', function() { - isDragging = false; - }); - textOutput.addEventListener('mousemove', function() { - isDragging = true; - }); - textOutput.addEventListener('mouseup', function() { + // Keep focus on terminal if user clicks inside + textOutput.addEventListener('mousedown', () => { isDragging = false; }); + textOutput.addEventListener('mousemove', () => { isDragging = true; }); + textOutput.addEventListener('mouseup', () => { const selectedText = window.getSelection().toString(); if (!isDragging && !selectedText) { textInput.focus(); @@ -475,82 +493,133 @@

Volume Controls

///////////////////////////////////////////// // - // Volume sliders with dynamic speaker icons + // Volume Sliders + Mute // ///////////////////////////////////////////// - - // Our multiple volume controls: - let sliderValues = { // defaults + + // 1) Define default sliders (and new ones if needed!) + const defaultSliders = { "music": 75, "combat sounds": 75, "movement sounds": 75, - "other sounds": 75 + "environment sounds": 75, + "other sounds": 75, }; - // Returns an appropriate speaker icon based on value + // 2) We'll store final slider values + let sliderValues = { ...defaultSliders }; + + // 3) We'll also store unmuted slider values if user checks "Mute" + let unmutedSliderValues = null; + + // Return speaker icon function getSpeakerIcon(value) { value = Number(value); if (value === 0) { - return "🔇"; // muted + return "🔇"; } else if (value < 33) { - return "🔈"; // low + return "🔈"; } else if (value < 66) { - return "🔉"; // medium + return "🔉"; } else { - return "🔊"; // high + return "🔊"; } } + // Build the sliders from sliderValues function buildSliders() { const container = document.getElementById('sliders-container'); - container.innerHTML = ""; // Clear any old content + container.innerHTML = ""; Object.keys(sliderValues).forEach(function(key) { - // Create a container for each slider row const wrapper = document.createElement('div'); wrapper.className = "slider-container"; - // Label for the name (Music, Sound FX, etc.) const label = document.createElement('label'); label.textContent = key.toLowerCase().split(' ').map(function(word) { return word.charAt(0).toUpperCase() + word.slice(1); }).join(' '); - // The slider const slider = document.createElement('input'); slider.type = "range"; slider.min = 0; slider.max = 100; slider.value = sliderValues[key]; - // The speaker icon const iconSpan = document.createElement('span'); iconSpan.className = "slider-icon"; - // Initial icon based on current value iconSpan.textContent = getSpeakerIcon(sliderValues[key]); - // On slider change, update the map + icon + // On slider change slider.addEventListener('input', function(e) { const val = e.target.value; sliderValues[key] = Number(val); iconSpan.textContent = getSpeakerIcon(val); - // LOCALSTORAGE ADDED: update local storage for every slider change + // Update local storage localStorage.setItem('sliderValues', JSON.stringify(sliderValues)); - // Example: update the global music volume - MusicPlayer.setGlobalVolume(sliderValues["music"]/100); + // Update music volume + MusicPlayer.setGlobalVolume(sliderValues["music"] / 100); + + // If user moves any slider up from 0, uncheck Mute + const muteCheckbox = document.getElementById('mute-checkbox'); + if (muteCheckbox.checked && val > 0) { + muteCheckbox.checked = false; + localStorage.setItem('muteAllSound', JSON.stringify(false)); + document.getElementById('mute-icon').textContent = "🔊"; + } }); - // Put them all together wrapper.appendChild(label); wrapper.appendChild(slider); wrapper.appendChild(iconSpan); - container.appendChild(wrapper); }); } + // Handle Mute + function toggleMuteAll() { + const muteCheckbox = document.getElementById('mute-checkbox'); + const isChecked = muteCheckbox.checked; + const muteIcon = document.getElementById('mute-icon'); + + if (isChecked) { + // Save the unmuted state + unmutedSliderValues = { ...sliderValues }; + localStorage.setItem('unmutedSliderValues', JSON.stringify(unmutedSliderValues)); + + // Set all to 0 + Object.keys(sliderValues).forEach(key => { + sliderValues[key] = 0; + }); + localStorage.setItem('sliderValues', JSON.stringify(sliderValues)); + + buildSliders(); + + muteIcon.textContent = "🔇"; + MusicPlayer.setGlobalVolume(0); + localStorage.setItem('muteAllSound', JSON.stringify(true)); + } else { + // Restore from unmuted + const savedUnmuted = localStorage.getItem('unmutedSliderValues'); + if (savedUnmuted) { + // Merge defaultSliders just in case new keys appear + let loadedUnmuted = JSON.parse(savedUnmuted) || {}; + loadedUnmuted = { ...defaultSliders, ...loadedUnmuted }; + unmutedSliderValues = { ...loadedUnmuted }; + + sliderValues = { ...unmutedSliderValues }; + localStorage.setItem('sliderValues', JSON.stringify(sliderValues)); + } + buildSliders(); + + muteIcon.textContent = "🔊"; + MusicPlayer.setGlobalVolume(sliderValues["music"] / 100); + localStorage.setItem('muteAllSound', JSON.stringify(false)); + } + } + // Show/hide the floating menu function toggleMenu() { const menu = document.getElementById('floating-menu'); @@ -573,26 +642,73 @@

Volume Controls

function init() { console.log("%cterminal commands:", "font-weight:bold;"); let longest = 0; - for ( let i in specialCommands ) { - longest = i.length > longest ? i.length : longest; + for (let i in specialCommands) { + if (i.length > longest) longest = i.length; } - for ( let i in specialCommands ) { - console.log(' '+i.padEnd(longest)+" - " + specialCommands[i]); + for (let i in specialCommands) { + console.log(" " + i.padEnd(longest) + " - " + specialCommands[i]); } console.log("%cconsole commands:", "font-weight:bold;"); - // LOCALSTORAGE ADDED: load sliderValues from localStorage if available + // 1) Load from localStorage, if available const savedValues = localStorage.getItem('sliderValues'); if (savedValues) { try { - sliderValues = JSON.parse(savedValues); + // Merge existing local storage with defaultSliders + const loaded = JSON.parse(savedValues); + sliderValues = { ...defaultSliders, ...loaded }; } catch (e) { console.warn("Could not parse saved sliderValues, using defaults."); } + } else { + // If no localStorage, ensure we set the defaults + localStorage.setItem('sliderValues', JSON.stringify(sliderValues)); + } + + // 2) Check if we have a "muteAllSound" property + const savedMute = localStorage.getItem('muteAllSound'); + if (savedMute) { + try { + const isMuted = JSON.parse(savedMute); + document.getElementById('mute-checkbox').checked = isMuted; + } catch (e) { + console.warn("Could not parse muteAllSound, ignoring."); + } } - // Build all sliders from sliderValues + // 3) Build the sliders buildSliders(); + + // 4) If currently muted, re-apply zero volumes + const muteCheckbox = document.getElementById('mute-checkbox'); + const muteIcon = document.getElementById('mute-icon'); + + if (muteCheckbox.checked) { + // Possibly retrieve unmuted values from storage + const savedUnmuted = localStorage.getItem('unmutedSliderValues'); + if (savedUnmuted) { + // Merge defaultSliders here, too + try { + let loadedUnmuted = JSON.parse(savedUnmuted) || {}; + unmutedSliderValues = { ...defaultSliders, ...loadedUnmuted }; + } catch(e) { + console.warn("Could not parse unmutedSliderValues."); + } + } + // Force all current sliders to 0 + Object.keys(sliderValues).forEach(key => { + sliderValues[key] = 0; + }); + localStorage.setItem('sliderValues', JSON.stringify(sliderValues)); + buildSliders(); + + muteIcon.textContent = "🔇"; + MusicPlayer.setGlobalVolume(0); + } else { + // If not muted, set music volume from slider + MusicPlayer.setGlobalVolume(sliderValues["music"] / 100); + muteIcon.textContent = "🔊"; + } } diff --git a/_datafiles/html/static/public/audio/sound/environment/wind.mp3 b/_datafiles/html/static/public/audio/sound/environment/wind.mp3 new file mode 100644 index 00000000..deabc3a5 Binary files /dev/null and b/_datafiles/html/static/public/audio/sound/environment/wind.mp3 differ diff --git a/_datafiles/world/default/templates/descriptions/insidecontainer.template b/_datafiles/world/default/templates/descriptions/insidecontainer.template index f87804e8..ef140eea 100644 --- a/_datafiles/world/default/templates/descriptions/insidecontainer.template +++ b/_datafiles/world/default/templates/descriptions/insidecontainer.template @@ -1,13 +1,11 @@ {{- $displayed := 0 -}} -{{- $itemCt := len . -}} +{{ $itemCt := len .ItemNames -}} ┌─────────────────────────────────────────────────────────────────────────┐ - Inside you see: + Inside: {{- if ne $itemCt 0 -}} - {{- range $index, $itemName := . -}} - {{- $displayed = add $displayed 1 -}} - {{ $itemName }}{{- if ne $displayed $itemCt }}{{- if ne $displayed (sub $itemCt 1) }}, {{ else }} and {{ end }}{{ end -}} - {{- end }} + {{ $formattedNames := .ItemNamesFormatted -}}{{- $strlen := 0 -}}{{- $lineCt := 1 -}} + {{ range $index, $name := .ItemNames -}}{{ $proposedLength := (add 2 (add $strlen (len $name))) }}{{- if gt $proposedLength 66 -}}{{- $strlen = 0 -}}{{- $lineCt = (add 1 $lineCt) -}}{{- printf "\n " -}}{{- end -}}{{ index $formattedNames $index }}{{- if ne $index (sub $itemCt 1) }}, {{ $strlen = (add 2 (add $strlen (len $name))) }}{{ end }}{{ end }} {{ else -}} Nothing {{ end }} diff --git a/_datafiles/world/empty/templates/descriptions/insidecontainer.template b/_datafiles/world/empty/templates/descriptions/insidecontainer.template index 7d201aeb..ef140eea 100644 --- a/_datafiles/world/empty/templates/descriptions/insidecontainer.template +++ b/_datafiles/world/empty/templates/descriptions/insidecontainer.template @@ -1,13 +1,11 @@ {{- $displayed := 0 -}} -{{- $itemCt := len . -}} +{{ $itemCt := len .ItemNames -}} ┌─────────────────────────────────────────────────────────────────────────┐ -Inside you see: + Inside: {{- if ne $itemCt 0 -}} - {{- range $index, $itemName := . -}} - {{- $displayed = add $displayed 1 -}} - {{ $itemName }}{{- if ne $displayed $itemCt }}{{- if ne $displayed (sub $itemCt 1) }}, {{ else }} and {{ end }}{{ end -}} - {{- end }} + {{ $formattedNames := .ItemNamesFormatted -}}{{- $strlen := 0 -}}{{- $lineCt := 1 -}} + {{ range $index, $name := .ItemNames -}}{{ $proposedLength := (add 2 (add $strlen (len $name))) }}{{- if gt $proposedLength 66 -}}{{- $strlen = 0 -}}{{- $lineCt = (add 1 $lineCt) -}}{{- printf "\n " -}}{{- end -}}{{ index $formattedNames $index }}{{- if ne $index (sub $itemCt 1) }}, {{ $strlen = (add 2 (add $strlen (len $name))) }}{{ end }}{{ end }} {{ else -}} Nothing {{ end }} diff --git a/internal/configs/configs.go b/internal/configs/configs.go index 29b372c7..62b02ee9 100644 --- a/internal/configs/configs.go +++ b/internal/configs/configs.go @@ -99,7 +99,8 @@ type Config struct { CorpsesEnabled ConfigBool `yaml:"CorpsesEnabled"` // Whether corpses are left behind after mob/player deaths CorpseDecayTime ConfigString `yaml:"CorpseDecayTime"` // How long until corpses decay to dust (go away) - LeaderboardSize ConfigInt `yaml:"LeaderboardSize"` // Maximum size of leaderboard + LeaderboardSize ConfigInt `yaml:"LeaderboardSize"` // Maximum size of leaderboard + ContainerSizeMax ConfigInt `yaml:"ContainerSizeMax"` // How many objects containers can hold before overflowing SeedInt int64 `yaml:"-"` diff --git a/internal/events/eventtypes.go b/internal/events/eventtypes.go index 314f1545..dd1a9ad1 100644 --- a/internal/events/eventtypes.go +++ b/internal/events/eventtypes.go @@ -96,6 +96,7 @@ type MSP struct { UserId int SoundType string // SOUND or MUSIC SoundFile string + Volume int // 1-100 Category string // special category/type for MSP string } diff --git a/internal/mobcommands/go.go b/internal/mobcommands/go.go index 2318bf66..2e7239f8 100644 --- a/internal/mobcommands/go.go +++ b/internal/mobcommands/go.go @@ -138,8 +138,8 @@ func Go(rest string, mob *mobs.Mob, room *rooms.Room) (bool, error) { destRoom.SendTextToExits(`You hear someone moving around.`, true, room.GetPlayers(rooms.FindAll)...) - room.PlaySound(`sound/movement/room-exit.mp3`, `movement`) - destRoom.PlaySound(`sound/movement/room-enter.mp3`, `movement`) + room.PlaySound(`sound/movement/room-exit.mp3`, `movement`, 100) + destRoom.PlaySound(`sound/movement/room-enter.mp3`, `movement`, 100) return true, nil } diff --git a/internal/mobcommands/put.go b/internal/mobcommands/put.go index 08109e38..a19f67b3 100644 --- a/internal/mobcommands/put.go +++ b/internal/mobcommands/put.go @@ -5,6 +5,7 @@ import ( "strconv" "strings" + "github.com/volte6/gomud/internal/configs" "github.com/volte6/gomud/internal/items" "github.com/volte6/gomud/internal/mobs" "github.com/volte6/gomud/internal/rooms" @@ -79,8 +80,8 @@ func Put(rest string, mob *mobs.Mob, room *rooms.Room) (bool, error) { room.SendText(fmt.Sprintf(`%s places their %s into the %s`, mob.Character.Name, item.DisplayName(), containerName)) - // current hard limit of 10 max items. - if len(container.Items) > 10 { + // Enforce container size limits + if len(container.Items) > int(configs.GetConfig().ContainerSizeMax) { randItemToRemove := util.Rand(len(container.Items)) oopsItem := container.Items[randItemToRemove] diff --git a/internal/rooms/rooms.go b/internal/rooms/rooms.go index 399ad95a..a273bec4 100644 --- a/internal/rooms/rooms.go +++ b/internal/rooms/rooms.go @@ -248,7 +248,13 @@ func (r *Room) SendText(txt string, excludeUserIds ...int) { } -func (r *Room) PlaySound(soundFile string, category string, excludeUserIds ...int) { +func (r *Room) PlaySound(soundFile string, category string, volume int, excludeUserIds ...int) { + + if volume < 1 { + volume = 1 + } else if volume > 100 { + volume = 100 + } for _, userId := range r.players { @@ -272,6 +278,7 @@ func (r *Room) PlaySound(soundFile string, category string, excludeUserIds ...in UserId: userId, SoundType: `SOUND`, SoundFile: soundFile, + Volume: volume, Category: category, }) } diff --git a/internal/usercommands/look.go b/internal/usercommands/look.go index 04588aeb..fd9d2b74 100644 --- a/internal/usercommands/look.go +++ b/internal/usercommands/look.go @@ -143,6 +143,9 @@ func Look(rest string, user *users.UserRecord, room *rooms.Room) (bool, error) { containerName := room.FindContainerByName(lookAt) if containerName != `` { + itemNames := []string{} + itemNamesFormatted := []string{} + container := room.Containers[containerName] if container.Lock.IsLocked() { @@ -152,10 +155,9 @@ func Look(rest string, user *users.UserRecord, room *rooms.Room) (bool, error) { return true, nil } - chestStuff := []string{} - if container.Gold > 0 { - chestStuff = append(chestStuff, fmt.Sprintf(`%d gold`, container.Gold)) + itemNames = append(itemNames, fmt.Sprintf(`%d gold`, container.Gold)) + itemNamesFormatted = append(itemNamesFormatted, fmt.Sprintf(`%d gold`, container.Gold)) } for _, item := range container.Items { @@ -163,7 +165,9 @@ func Look(rest string, user *users.UserRecord, room *rooms.Room) (bool, error) { room.RemoveItem(item, false) continue } - chestStuff = append(chestStuff, item.DisplayName()) + + itemNames = append(itemNames, item.Name()) + itemNamesFormatted = append(itemNamesFormatted, fmt.Sprintf(`%s`, item.DisplayName())) } if len(container.Recipes) > 0 { @@ -182,14 +186,16 @@ func Look(rest string, user *users.UserRecord, room *rooms.Room) (bool, error) { user.SendText(``) finalItem := items.New(finalItemId) - user.SendText(fmt.Sprintf(` To receive 1 %s: `, finalItem.DisplayName())) + user.SendText(fmt.Sprintf(` To receive 1 %s: `, finalItem.DisplayName())) for inputItemId, qtyNeeded := range neededItems { tmpItem := items.New(inputItemId) totalContained := container.Count(inputItemId) - colorClass := "9" + colorClass := "8" // None fulfilled if totalContained == qtyNeeded { - colorClass = "14" + colorClass = "10" + } else if totalContained > 0 { + colorClass = "3" } user.SendText(fmt.Sprintf(` [%d/%d] %s`, colorClass, totalContained, qtyNeeded, tmpItem.DisplayName())) } @@ -198,6 +204,11 @@ func Look(rest string, user *users.UserRecord, room *rooms.Room) (bool, error) { } + chestStuff := map[string]any{ + `ItemNames`: itemNames, + `ItemNamesFormatted`: itemNamesFormatted, + } + textOut, _ := templates.Process("descriptions/insidecontainer", chestStuff) user.SendText(``) diff --git a/internal/usercommands/put.go b/internal/usercommands/put.go index b1b1620b..16740a56 100644 --- a/internal/usercommands/put.go +++ b/internal/usercommands/put.go @@ -5,6 +5,7 @@ import ( "strconv" "strings" + "github.com/volte6/gomud/internal/configs" "github.com/volte6/gomud/internal/items" "github.com/volte6/gomud/internal/rooms" "github.com/volte6/gomud/internal/users" @@ -93,14 +94,15 @@ func Put(rest string, user *users.UserRecord, room *rooms.Room) (bool, error) { } if itemFound { + container.AddItem(item) user.Character.RemoveItem(item) user.SendText(fmt.Sprintf(`You place your %s into the %s`, item.DisplayName(), containerName)) room.SendText(fmt.Sprintf(`%s places their %s into the %s`, user.Character.Name, item.DisplayName(), containerName), user.UserId) - // current hard limit of 10 max items. - if len(container.Items) > 10 { + // Enforce container size limits + if len(container.Items) > int(configs.GetConfig().ContainerSizeMax) { randItemToRemove := util.Rand(len(container.Items)) oopsItem := container.Items[randItemToRemove] diff --git a/internal/usercommands/use.go b/internal/usercommands/use.go index 53b82f34..1c664853 100644 --- a/internal/usercommands/use.go +++ b/internal/usercommands/use.go @@ -47,7 +47,7 @@ func Use(rest string, user *users.UserRecord, room *rooms.Room) (bool, error) { container.AddItem(newItem) room.Containers[containerName] = container - room.PlaySound(`sound/other/change.mp3`, `other`) + room.PlaySound(`sound/other/change.mp3`, `other`, 100) user.SendText(``) user.SendText(fmt.Sprintf(`The %s produces a %s!`, containerName, newItem.DisplayName())) diff --git a/internal/users/userrecord.go b/internal/users/userrecord.go index 41c3d61b..b9eadd4f 100644 --- a/internal/users/userrecord.go +++ b/internal/users/userrecord.go @@ -151,16 +151,28 @@ func (u *UserRecord) PlayMusic(musicFile string) { UserId: u.UserId, SoundType: `MUSIC`, SoundFile: musicFile, + Volume: 100, }) } -func (u *UserRecord) PlaySound(soundFile string, category string) { +func (u *UserRecord) PlaySound(soundFile string, category string, volume ...int) { + + v := 100 + if len(volume) > 0 { + v = volume[0] + if v < 1 { + v = 1 + } else if v > 100 { + v = 100 + } + } events.AddToQueue(events.MSP{ UserId: u.UserId, SoundType: `SOUND`, SoundFile: soundFile, + Volume: v, Category: category, }) diff --git a/world.go b/world.go index e7de5061..068b4c26 100644 --- a/world.go +++ b/world.go @@ -1007,7 +1007,7 @@ func (w *World) MessageTick() { user.LastMusic = msp.SoundFile - msg := []byte("!!MUSIC(" + msp.SoundFile + " V=80 L=-1 C=1)") + msg := []byte("!!MUSIC(" + msp.SoundFile + " V=" + strconv.Itoa(msp.Volume) + " L=-1 C=1)") if connections.IsWebsocket(user.ConnectionId()) { @@ -1026,7 +1026,7 @@ func (w *World) MessageTick() { } } else { - msg := []byte("!!SOUND(" + msp.SoundFile + " T=" + msp.Category + ")") + msg := []byte("!!SOUND(" + msp.SoundFile + " T=" + msp.Category + " V=" + strconv.Itoa(msp.Volume) + ")") if connections.IsWebsocket(user.ConnectionId()) {