Skip to content

Commit 1dd4a1f

Browse files
authored
Nouns (#223)
# Changes * Sliders now save state in localstorage for webclient * PLayer room id doesn't reset to -1 when autosaving. * Noun matching a lot more flexible. * Unit tests
1 parent 1d539ac commit 1dd4a1f

File tree

5 files changed

+231
-25
lines changed

5 files changed

+231
-25
lines changed

_datafiles/html/public/webclient.html

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -480,7 +480,7 @@ <h3>Volume Controls</h3>
480480
/////////////////////////////////////////////
481481

482482
// Our multiple volume controls:
483-
let sliderValues = { // default 75%
483+
let sliderValues = { // defaults
484484
"music": 75,
485485
"combat sounds": 75,
486486
"movement sounds": 75,
@@ -535,6 +535,10 @@ <h3>Volume Controls</h3>
535535
sliderValues[key] = Number(val);
536536
iconSpan.textContent = getSpeakerIcon(val);
537537

538+
// LOCALSTORAGE ADDED: update local storage for every slider change
539+
localStorage.setItem('sliderValues', JSON.stringify(sliderValues));
540+
541+
// Example: update the global music volume
538542
MusicPlayer.setGlobalVolume(sliderValues["music"]/100);
539543
});
540544

@@ -577,6 +581,16 @@ <h3>Volume Controls</h3>
577581
}
578582
console.log("%cconsole commands:", "font-weight:bold;");
579583

584+
// LOCALSTORAGE ADDED: load sliderValues from localStorage if available
585+
const savedValues = localStorage.getItem('sliderValues');
586+
if (savedValues) {
587+
try {
588+
sliderValues = JSON.parse(savedValues);
589+
} catch (e) {
590+
console.warn("Could not parse saved sliderValues, using defaults.");
591+
}
592+
}
593+
580594
// Build all sliders from sliderValues
581595
buildSliders();
582596
}

internal/rooms/rooms.go

Lines changed: 81 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1564,70 +1564,130 @@ func (r *Room) FindContainerByName(containerNameSearch string) string {
15641564

15651565
func (r *Room) FindNoun(noun string) (foundNoun string, nounDescription string) {
15661566

1567-
for _, newNoun := range strings.Split(noun, ` `) {
1567+
if len(r.Nouns) == 0 {
1568+
return ``, ``
1569+
}
1570+
1571+
roomNouns := map[string]string{}
1572+
1573+
for originalNoun, originalDesc := range r.Nouns {
1574+
roomNouns[originalNoun] = originalDesc
1575+
1576+
if strings.Contains(originalNoun, ` `) {
1577+
for _, n := range strings.Split(originalNoun, ` `) {
1578+
1579+
if _, ok := r.Nouns[n]; ok {
1580+
continue
1581+
}
1582+
1583+
if _, ok := roomNouns[n]; ok {
1584+
continue
1585+
}
1586+
1587+
roomNouns[n] = `:` + originalNoun
1588+
1589+
}
1590+
}
1591+
1592+
}
1593+
1594+
testNouns := util.SplitButRespectQuotes(noun)
1595+
ct := len(testNouns)
1596+
for i := 0; i < ct; i++ {
1597+
1598+
if splitCount := strings.Split(testNouns[i], ` `); len(splitCount) > 1 {
1599+
1600+
for _, n2 := range splitCount {
1601+
1602+
if len(n2) < 2 {
1603+
continue
1604+
}
1605+
1606+
testNouns = append(testNouns, n2)
1607+
}
1608+
1609+
}
1610+
}
15681611

1569-
if desc, ok := r.Nouns[newNoun]; ok {
1612+
// If it created more than one word, put the original back on as a full string to test
1613+
if len(testNouns) > 1 {
1614+
testNouns = append(testNouns, noun)
1615+
}
1616+
1617+
for _, newNoun := range testNouns {
1618+
1619+
if desc, ok := roomNouns[newNoun]; ok {
15701620
if desc[0:1] == `:` {
1571-
return desc[1:], r.Nouns[desc[1:]]
1621+
return desc[1:], roomNouns[desc[1:]]
15721622
}
1573-
return noun, desc
1623+
return newNoun, desc
15741624
}
15751625

15761626
if len(newNoun) < 2 {
15771627
continue
15781628
}
15791629

15801630
// If ended in `s`, strip it and add a new word to the search list
1581-
if noun[len(newNoun)-1:] == `s` {
1631+
if newNoun[len(newNoun)-1:] == `s` {
1632+
15821633
testNoun := newNoun[:len(newNoun)-1]
1583-
if desc, ok := r.Nouns[testNoun]; ok {
1634+
if desc, ok := roomNouns[testNoun]; ok {
15841635
if desc[0:1] == `:` {
1585-
return desc[1:], r.Nouns[desc[1:]]
1636+
return desc[1:], roomNouns[desc[1:]]
15861637
}
15871638
return testNoun, desc
15881639
}
1640+
15891641
} else {
1642+
15901643
testNoun := newNoun + `s`
1591-
if desc, ok := r.Nouns[testNoun]; ok { // `s`` at end
1644+
if desc, ok := roomNouns[testNoun]; ok { // `s`` at end
15921645
if desc[0:1] == `:` {
1593-
return desc[1:], r.Nouns[desc[1:]]
1646+
return desc[1:], roomNouns[desc[1:]]
15941647
}
15951648
return testNoun, desc
15961649
}
1650+
15971651
}
15981652

15991653
// Switch ending of `y` to `ies`
1600-
if noun[len(newNoun)-1:] == `y` {
1654+
if newNoun[len(newNoun)-1:] == `y` {
1655+
16011656
testNoun := newNoun[:len(newNoun)-1] + `ies`
1602-
if desc, ok := r.Nouns[testNoun]; ok { // `ies` instead of `y` at end
1657+
if desc, ok := roomNouns[testNoun]; ok { // `ies` instead of `y` at end
16031658
if desc[0:1] == `:` {
1604-
return desc[1:], r.Nouns[desc[1:]]
1659+
return desc[1:], roomNouns[desc[1:]]
16051660
}
16061661
return testNoun, desc
16071662
}
1663+
16081664
}
16091665

16101666
if len(newNoun) < 3 {
16111667
continue
16121668
}
16131669

16141670
// Strip 'es' such as 'torches'
1615-
if noun[len(newNoun)-2:] == `es` {
1671+
if newNoun[len(newNoun)-2:] == `es` {
1672+
16161673
testNoun := newNoun[:len(newNoun)-2]
1617-
if desc, ok := r.Nouns[testNoun]; ok {
1674+
if desc, ok := roomNouns[testNoun]; ok {
16181675
if desc[0:1] == `:` {
1619-
return desc[1:], r.Nouns[desc[1:]]
1676+
return desc[1:], roomNouns[desc[1:]]
16201677
}
16211678
return testNoun, desc
16221679
}
1680+
16231681
} else {
1682+
16241683
testNoun := newNoun + `es`
1625-
if desc, ok := r.Nouns[testNoun]; ok { // `es` at end
1684+
if desc, ok := roomNouns[testNoun]; ok { // `es` at end
16261685
if desc[0:1] == `:` {
1627-
return desc[1:], r.Nouns[desc[1:]]
1686+
return desc[1:], roomNouns[desc[1:]]
16281687
}
16291688
return testNoun, desc
16301689
}
1690+
16311691
}
16321692

16331693
if len(newNoun) < 4 {
@@ -1636,13 +1696,15 @@ func (r *Room) FindNoun(noun string) (foundNoun string, nounDescription string)
16361696

16371697
// Strip 'es' such as 'torches'
16381698
if noun[len(newNoun)-3:] == `ies` {
1699+
16391700
testNoun := newNoun[:len(newNoun)-3] + `y`
1640-
if desc, ok := r.Nouns[testNoun]; ok { // `y` instead of `ies` at end
1701+
if desc, ok := roomNouns[testNoun]; ok { // `y` instead of `ies` at end
16411702
if desc[0:1] == `:` {
1642-
return desc[1:], r.Nouns[desc[1:]]
1703+
return desc[1:], roomNouns[desc[1:]]
16431704
}
16441705
return testNoun, desc
16451706
}
1707+
16461708
}
16471709

16481710
}

internal/rooms/rooms_test.go

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,3 +94,131 @@ func TestRoom_FindCorpse(t *testing.T) {
9494
found, ok = r.FindCorpse("NonExistent")
9595
assert.False(t, ok, "Expected not to find a missing corpse")
9696
}
97+
98+
func TestFindNoun(t *testing.T) {
99+
// Create a room with various noun mappings (including aliases).
100+
r := &Room{
101+
Nouns: map[string]string{
102+
"torch": "a fiery torch",
103+
"torchAlias": ":torch", // alias -> :torch means "torch"
104+
"lamp": "an illuminating lamp",
105+
"lampAlias": ":lamp", // alias -> :lamp means "lamp"
106+
"candles": "some wax candles",
107+
"pony": "a small horse",
108+
"mystery": "just a riddle",
109+
"secret": ":mystery", // chain alias -> :mystery
110+
"projector screen": "a wide, matte-white screen", // multi-word matching
111+
},
112+
}
113+
114+
// Table-driven tests.
115+
tests := []struct {
116+
name string
117+
inputNoun string
118+
wantFoundNoun string
119+
wantDesc string
120+
}{
121+
{
122+
name: "Direct match (torch)",
123+
inputNoun: "torch",
124+
wantFoundNoun: "torch",
125+
wantDesc: "a fiery torch",
126+
},
127+
{
128+
name: "Direct match (candles)",
129+
inputNoun: "candles",
130+
wantFoundNoun: "candles",
131+
wantDesc: "some wax candles",
132+
},
133+
{
134+
name: "Direct match (pony)",
135+
inputNoun: "pony",
136+
wantFoundNoun: "pony",
137+
wantDesc: "a small horse",
138+
},
139+
{
140+
name: "Alias match (torchAlias -> :torch)",
141+
inputNoun: "torchAlias",
142+
wantFoundNoun: "torch",
143+
wantDesc: "a fiery torch",
144+
},
145+
{
146+
name: "Alias match (lampAlias -> :lamp)",
147+
inputNoun: "lampAlias",
148+
wantFoundNoun: "lamp",
149+
wantDesc: "an illuminating lamp",
150+
},
151+
{
152+
name: "Chain alias (secret -> :mystery)",
153+
inputNoun: "secret",
154+
wantFoundNoun: "mystery",
155+
wantDesc: "just a riddle",
156+
},
157+
{
158+
name: "Plural form by adding s (pony -> ponies)",
159+
inputNoun: "ponies",
160+
wantFoundNoun: "pony",
161+
wantDesc: "a small horse",
162+
},
163+
{
164+
name: "Plural form by adding es (torches)",
165+
inputNoun: "torches",
166+
wantFoundNoun: "torch",
167+
wantDesc: "a fiery torch",
168+
},
169+
170+
{
171+
name: "Multi-word input, second word valid (red torches)",
172+
inputNoun: "red torches",
173+
wantFoundNoun: "torch",
174+
wantDesc: "a fiery torch",
175+
},
176+
{
177+
name: "Multi-word input, multi-word match",
178+
inputNoun: "projector screen",
179+
wantFoundNoun: "projector screen",
180+
wantDesc: "a wide, matte-white screen",
181+
},
182+
{
183+
name: "Single-word input, multi-word match 1",
184+
inputNoun: "projector",
185+
wantFoundNoun: "projector screen",
186+
wantDesc: "a wide, matte-white screen",
187+
},
188+
{
189+
name: "Single-word input, multi-word match 2",
190+
inputNoun: "screen",
191+
wantFoundNoun: "projector screen",
192+
wantDesc: "a wide, matte-white screen",
193+
},
194+
{
195+
name: "Multi-word input, first word valid (torch something)",
196+
inputNoun: "torch something",
197+
wantFoundNoun: "torch",
198+
wantDesc: "a fiery torch",
199+
},
200+
{
201+
name: "No match",
202+
inputNoun: "gibberish",
203+
wantFoundNoun: "",
204+
wantDesc: "",
205+
},
206+
{
207+
name: "Multi-word no match (foo bar)",
208+
inputNoun: "foo bar",
209+
wantFoundNoun: "",
210+
wantDesc: "",
211+
},
212+
}
213+
214+
// Run each sub-test.
215+
for _, tt := range tests {
216+
t.Run(tt.name, func(t *testing.T) {
217+
gotFound, gotDesc := r.FindNoun(tt.inputNoun)
218+
if gotFound != tt.wantFoundNoun || gotDesc != tt.wantDesc {
219+
t.Errorf("FindNoun(%q) = (%q, %q), want (%q, %q)",
220+
tt.inputNoun, gotFound, gotDesc, tt.wantFoundNoun, tt.wantDesc)
221+
}
222+
})
223+
}
224+
}

internal/users/users.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -276,10 +276,10 @@ func SetZombieUser(userId int) {
276276

277277
}
278278

279-
func SaveAllUsers() {
279+
func SaveAllUsers(isAutoSave ...bool) {
280280

281281
for _, u := range userManager.Users {
282-
if err := SaveUser(*u); err != nil {
282+
if err := SaveUser(*u, isAutoSave...); err != nil {
283283
slog.Error("SaveAllUsers()", "error", err.Error())
284284
}
285285
}
@@ -457,7 +457,7 @@ func CharacterNameSearch(nameToFind string) (foundUserId int, foundUserName stri
457457
return foundUserId, foundUserName
458458
}
459459

460-
func SaveUser(u UserRecord) error {
460+
func SaveUser(u UserRecord, isAutoSave ...bool) error {
461461

462462
fileWritten := false
463463
tmpSaved := false
@@ -474,7 +474,9 @@ func SaveUser(u UserRecord) error {
474474
//}
475475

476476
if u.Character.RoomId >= 900 && u.Character.RoomId <= 999 {
477-
u.Character.RoomId = -1
477+
if len(isAutoSave) == 0 || !isAutoSave[0] {
478+
u.Character.RoomId = -1
479+
}
478480
}
479481

480482
data, err := yaml.Marshal(&u)

world.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1264,7 +1264,7 @@ func (w *World) TurnTick() {
12641264
Text: `Saving users...`,
12651265
})
12661266

1267-
users.SaveAllUsers()
1267+
users.SaveAllUsers(true)
12681268

12691269
events.AddToQueue(events.Broadcast{
12701270
Text: `Done.` + term.CRLFStr,

0 commit comments

Comments
 (0)