import React, { useState } from 'react'; import { Plus, Trash2, MessageCircle } from 'lucide-react'; import type { DialogueConfiguration, DialoguePage, DialogueSpeaker } from '../types/npc'; interface DialogueEditorProps { dialogue: DialogueConfiguration | null; onChange: (dialogue: DialogueConfiguration) => void; } export const DialogueEditor: React.FC = ({ dialogue, onChange }) => { const [selectedPageId, setSelectedPageId] = useState(null); const [speakerEditMode, setSpeakerEditMode] = useState(false); const initializeDialogue = () => { const newDialogue: DialogueConfiguration = { speakers: { npc: { name: { type: 'expression', expression: 'q.npc.name' }, face: 'q.npc.face(false);' }, player: { name: { type: 'expression', expression: 'q.player.username' }, face: 'q.player.face();' } }, pages: [{ id: 'greeting', speaker: 'npc', lines: ['Hello there!'], input: 'q.dialogue.close();' }] }; onChange(newDialogue); setSelectedPageId('greeting'); }; if (!dialogue) { return (

Dialogue Editor

No dialogue configured

); } const addPage = () => { const newPageId = `page_${Date.now()}`; const newPage: DialoguePage = { id: newPageId, speaker: 'npc', lines: ['New dialogue line'], input: 'q.dialogue.close();' }; onChange({ ...dialogue, pages: [...dialogue.pages, newPage] }); setSelectedPageId(newPageId); }; const updatePage = (pageId: string, updatedPage: DialoguePage) => { onChange({ ...dialogue, pages: dialogue.pages.map(page => page.id === pageId ? updatedPage : page ) }); }; const deletePage = (pageId: string) => { onChange({ ...dialogue, pages: dialogue.pages.filter(page => page.id !== pageId) }); if (selectedPageId === pageId) { setSelectedPageId(dialogue.pages.length > 1 ? dialogue.pages[0].id : null); } }; const addSpeaker = () => { const speakerName = `speaker_${Object.keys(dialogue.speakers).length + 1}`; onChange({ ...dialogue, speakers: { ...dialogue.speakers, [speakerName]: { name: { type: 'expression', expression: `'${speakerName}'` }, face: '' } } }); }; const updateSpeaker = (speakerId: string, speaker: DialogueSpeaker) => { onChange({ ...dialogue, speakers: { ...dialogue.speakers, [speakerId]: speaker } }); }; const deleteSpeaker = (speakerId: string) => { const newSpeakers = { ...dialogue.speakers }; delete newSpeakers[speakerId]; onChange({ ...dialogue, speakers: newSpeakers }); }; const selectedPage = dialogue.pages.find(page => page.id === selectedPageId); return (

Dialogue Editor

{/* Global Dialogue Settings */}

Global Settings

onChange({ ...dialogue, initializationAction: e.target.value || undefined })} className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 text-sm" placeholder="q.dialogue.close();" />

MoLang script executed when dialogue starts

onChange({ ...dialogue, escapeAction: e.target.value || undefined })} className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 text-sm" placeholder="q.dialogue.close();" />

MoLang script executed when ESC is pressed

{/* Speakers Management */}

Speakers

{speakerEditMode ? (
{Object.entries(dialogue.speakers).map(([speakerId, speaker]) => (
{speakerId} {!['npc', 'player'].includes(speakerId) && ( )}
updateSpeaker(speakerId, { ...speaker, name: { ...speaker.name, expression: e.target.value } })} className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 text-sm" placeholder="q.npc.name" />
updateSpeaker(speakerId, { ...speaker, face: e.target.value })} className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 text-sm" placeholder="q.npc.face(false);" />
))}
) : (
{Object.keys(dialogue.speakers).map(speakerId => ( {speakerId} ))}
)}
{/* Pages List */}

Pages

{dialogue.pages.map(page => (
setSelectedPageId(page.id)} >
{page.id}
Speaker: {page.speaker}
{Array.isArray(page.lines) ? (typeof page.lines[0] === 'string' ? page.lines[0] : page.lines[0]?.text || 'Expression') : (typeof page.lines === 'string' ? page.lines : page.lines?.text || 'Expression')}
))}
{/* Page Editor */}
{selectedPage ? ( updatePage(selectedPage.id, updatedPage)} /> ) : (
Select a page to edit
)}
); }; interface PageEditorProps { page: DialoguePage; speakers: Record; onChange: (page: DialoguePage) => void; } const PageEditor: React.FC = ({ page, speakers, onChange }) => { const updatePage = (field: keyof DialoguePage, value: any) => { onChange({ ...page, [field]: value }); }; const updateLine = (index: number, value: string) => { const newLines = [...page.lines]; newLines[index] = value; updatePage('lines', newLines); }; const addLine = () => { updatePage('lines', [...page.lines, '']); }; const removeLine = (index: number) => { updatePage('lines', page.lines.filter((_, i) => i !== index)); }; return (

Edit Page: {page.id}

updatePage('id', e.target.value)} className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500" />
{page.lines.map((line, index) => (
updateLine(index, e.target.value)} className="flex-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 text-sm" placeholder="Dialogue text or MoLang expression" />
))}