Skip to content

Commit 8241e17

Browse files
committed
feat(game-parser): add preference-based prompt text customization
1 parent 242d370 commit 8241e17

File tree

2 files changed

+85
-1
lines changed

2 files changed

+85
-1
lines changed

electron/main/game/__tests__/game-parser.test.ts

+72
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,29 @@ import {
66
GameEventType,
77
IndicatorType,
88
} from '../../../common/game/types.js';
9+
import { PreferenceKey } from '../../preference/types.js';
910
import { GameParserImpl } from '../game.parser.js';
1011
import type { GameParser } from '../types.js';
1112

13+
const { mockPreferenceService } = await vi.hoisted(async () => {
14+
const preferenceServiceMockModule = await import(
15+
'../../preference/__mocks__/preference-service.mock.js'
16+
);
17+
18+
const mockPreferenceService =
19+
new preferenceServiceMockModule.PreferenceServiceMockImpl();
20+
21+
return {
22+
mockPreferenceService,
23+
};
24+
});
25+
26+
vi.mock('../../preference/preference.instance.js', async () => {
27+
return {
28+
Preferences: mockPreferenceService,
29+
};
30+
});
31+
1232
vi.mock('../../logger/logger.factory.ts');
1333

1434
describe('game-parser', () => {
@@ -103,6 +123,47 @@ describe('game-parser', () => {
103123
});
104124
});
105125

126+
it('emits TextGameEvent (prompt)', () => {
127+
gameSocketSubject$.next('<prompt time="1703804031">&gt;</prompt>\n');
128+
129+
expectGameEvent({
130+
type: GameEventType.TEXT,
131+
text: `>\n`,
132+
});
133+
});
134+
135+
it('emits TextGameEvent (prompt preference)', () => {
136+
mockPreferenceService.get.mockImplementation((key) => {
137+
switch (key) {
138+
case PreferenceKey.GAME_WINDOW_PROMPT:
139+
return '#';
140+
}
141+
});
142+
143+
// Because the preference is read only once when
144+
// the parser is created, we need to create a new
145+
// parser so that it picks up the mocked value.
146+
147+
gameSocketSubject$ = new rxjs.Subject<string>();
148+
149+
parser = new GameParserImpl();
150+
151+
gameEventStream$ = parser.parse(gameSocketSubject$.asObservable());
152+
153+
gameEventStream$.subscribe({
154+
next: onNextSpy,
155+
complete: onCompleteSpy,
156+
error: onErrorSpy,
157+
});
158+
159+
gameSocketSubject$.next('<prompt time="1703804031">&gt;</prompt>\n');
160+
161+
expectGameEvent({
162+
type: GameEventType.TEXT,
163+
text: `${mockPreferenceService.get(PreferenceKey.GAME_WINDOW_PROMPT)}\n`,
164+
});
165+
});
166+
106167
it('emits TextGameEvent (with bold tags)', () => {
107168
gameSocketSubject$.next(
108169
'You also see <pushBold/>a town guard<popBold/>.\n'
@@ -406,6 +467,17 @@ describe('game-parser', () => {
406467
});
407468
});
408469

470+
it('emits RoomGameEvent (room extra)', () => {
471+
gameSocketSubject$.next(
472+
'<component id="room extra">Lorem ipsum</component>\n'
473+
);
474+
475+
expectGameEvent({
476+
type: GameEventType.ROOM,
477+
roomExtra: 'Lorem ipsum',
478+
});
479+
});
480+
409481
it('emits ServerTimeGameEvent', () => {
410482
gameSocketSubject$.next('<prompt time="1703804031">&gt;</prompt>\n');
411483

electron/main/game/game.parser.ts

+13-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import {
1414
unescapeEntities,
1515
} from '../../common/string/string.utils.js';
1616
import type { Maybe } from '../../common/types.js';
17+
import { Preferences } from '../preference/preference.instance.js';
18+
import { PreferenceKey } from '../preference/types.js';
1719
import { gameParserLogger as logger } from './logger.js';
1820
import type { GameParser } from './types.js';
1921

@@ -152,11 +154,21 @@ export class GameParserImpl implements GameParser {
152154
*/
153155
private gameText: string;
154156

157+
/**
158+
* The text that should be displayed for game prompt events.
159+
* This is a periodic terminal-like prompt that appears in the game.
160+
* Example: `<prompt time="1703804031">&gt;</prompt>`
161+
* In this example, the text would be '&gt;'.
162+
* Users may set a preference to customize this text.
163+
*/
164+
private promptText?: string;
165+
155166
constructor() {
156167
this.gameEventsSubject$ = new rxjs.Subject<GameEvent>();
157168
this.activeTags = [];
158169
this.compassDirections = [];
159170
this.gameText = '';
171+
this.promptText = Preferences.get(PreferenceKey.GAME_WINDOW_PROMPT);
160172
}
161173

162174
/**
@@ -392,7 +404,7 @@ export class GameParserImpl implements GameParser {
392404
// This is a periodic terminal-like prompt that appears in the game.
393405
// Example: `<prompt time="1703804031">&gt;</prompt>`
394406
// In this example, the text would be '&gt;'.
395-
this.gameText += text;
407+
this.gameText += this.promptText ?? text;
396408
break;
397409
case 'spell':
398410
// This is a spell name.

0 commit comments

Comments
 (0)