(dialog: string, clearDialog: string, character: string, animation: boolean)
| 286 | } |
| 287 | |
| 288 | displayNvlDialog(dialog: string, clearDialog: string, character: string, animation: boolean): void { |
| 289 | const textBox = this.engine.element().find('[data-component="text-box"]').get(0) as any; |
| 290 | |
| 291 | if (!textBox) { |
| 292 | this.engine.debug.error('Text box component not found'); |
| 293 | return; |
| 294 | } |
| 295 | |
| 296 | if (textBox.props?.mode !== 'nvl') { |
| 297 | Dialog.reset(); |
| 298 | // NOTE: setProps does NOT trigger a re-render — it only updates the |
| 299 | // mode attribute so CSS (text-box[mode="nvl"]) applies. The ADV-mode |
| 300 | // type-writer element persists and serves as the container for NVL |
| 301 | // dialog entries appended to [data-ui="say"]. |
| 302 | textBox.setProps({ mode: 'nvl' }); |
| 303 | |
| 304 | // We need to re-apply any custom classes here because the reset clears them |
| 305 | this._handleCustomClasses(textBox); |
| 306 | } |
| 307 | |
| 308 | // Remove contents from the dialog area. |
| 309 | const previous = this.engine.element().find('[data-component="text-box"]').data('speaking'); |
| 310 | this.engine.element().find('[data-component="text-box"]').data('speaking', character); |
| 311 | |
| 312 | // Determine if we should animate (respects NVLTypeAnimation setting) |
| 313 | const shouldAnimate = animation && this.engine.setting('NVLTypeAnimation') === true; |
| 314 | |
| 315 | // Build the dialog entry HTML |
| 316 | if (character !== '_narrator') { |
| 317 | const charData = this.engine.character(character); |
| 318 | if (previous !== character) { |
| 319 | this.engine.element().find('[data-ui="say"] [data-spoke]').last().addClass('nvl-dialog-footer'); |
| 320 | this.engine.element().find('[data-ui="say"]').append(`<div data-spoke="${character}" class='named'><span style='color:${charData?.color ?? ''};'>${this.engine.replaceVariables(charData?.name ?? '')}: </span><type-writer></type-writer></div>`); |
| 321 | } else { |
| 322 | this.engine.element().find('[data-ui="say"]').append(`<div data-spoke="${character}"><type-writer></type-writer></div>`); |
| 323 | } |
| 324 | } else { |
| 325 | if (previous !== character) { |
| 326 | this.engine.element().find('[data-ui="say"] [data-spoke]').last().addClass('nvl-dialog-footer'); |
| 327 | } |
| 328 | this.engine.element().find('[data-ui="say"]').append(`<div data-spoke="${character}" class='unnamed'><type-writer></type-writer></div>`); |
| 329 | } |
| 330 | |
| 331 | // Wait for the new type-writer component to be fully mounted before |
| 332 | // setting its content. Using ready() is more reliable than |
| 333 | // requestAnimationFrame since Pandora's connectedCallback is async. |
| 334 | const elements = $_('[data-ui="say"] [data-spoke] type-writer'); |
| 335 | const last = elements.last().get(0) as TypeWriter | undefined; |
| 336 | |
| 337 | if (last && typeof (last as any).ready === 'function') { |
| 338 | (last as any).ready(() => { |
| 339 | last.setContent(dialog, shouldAnimate); |
| 340 | }); |
| 341 | } else if (last) { |
| 342 | last.setContent(dialog, shouldAnimate); |
| 343 | } |
| 344 | |
| 345 | const text_box = this.engine.element().find('[data-component="text-box"]'); |
no test coverage detected