add choice mechanic
This commit is contained in:
41
app/page.tsx
41
app/page.tsx
@@ -7,6 +7,9 @@ import { VNEditor } from '@/components/VNEditor';
|
||||
import { VNPreview } from '@/components/VNPreview';
|
||||
import { INITIAL_SPRITE_BANK, Sprite } from '@/lib/sprite-bank';
|
||||
|
||||
const STORAGE_KEY_SCRIPT = 'vn_editor_script';
|
||||
const STORAGE_KEY_FILENAME = 'vn_editor_filename';
|
||||
|
||||
export default function VisualNovelEditorPage() {
|
||||
const [scriptText, setScriptText] = useState(DEFAULT_SCRIPT);
|
||||
const [fileName, setFileName] = useState("scene_01.vns");
|
||||
@@ -16,10 +19,29 @@ export default function VisualNovelEditorPage() {
|
||||
const [isParsing, setIsParsing] = useState(false);
|
||||
const editorRef = useRef<HTMLTextAreaElement>(null);
|
||||
|
||||
// Load from LocalStorage
|
||||
useEffect(() => {
|
||||
const savedScript = localStorage.getItem(STORAGE_KEY_SCRIPT);
|
||||
const savedFileName = localStorage.getItem(STORAGE_KEY_FILENAME);
|
||||
if (savedScript) setScriptText(savedScript);
|
||||
if (savedFileName) setFileName(savedFileName);
|
||||
}, []);
|
||||
|
||||
// Save to LocalStorage
|
||||
useEffect(() => {
|
||||
localStorage.setItem(STORAGE_KEY_SCRIPT, scriptText);
|
||||
localStorage.setItem(STORAGE_KEY_FILENAME, fileName);
|
||||
}, [scriptText, fileName]);
|
||||
|
||||
const updateSpriteBank = (newBank: Record<string, Sprite[]>) => {
|
||||
setSpriteBank(newBank);
|
||||
};
|
||||
|
||||
const addSpriteToScript = (char: string, id: string, url: string) => {
|
||||
// Add to the top of the script or after existing defines
|
||||
setScriptText(prev => `define_sprite ${char} ${id} "${url}"\n${prev}`);
|
||||
};
|
||||
|
||||
// Parse Debounce
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(() => {
|
||||
@@ -34,7 +56,7 @@ export default function VisualNovelEditorPage() {
|
||||
const parts = s.text.split(' ');
|
||||
const char = parts[1];
|
||||
const id = parts[2];
|
||||
const url = parts[3];
|
||||
const url = parts[3]?.replace(/^"(.*)"$/, '$1');
|
||||
if (char && id && url) {
|
||||
if (!newScriptBank[char]) newScriptBank[char] = [];
|
||||
if (!newScriptBank[char].some(sp => sp.id === id)) {
|
||||
@@ -68,12 +90,23 @@ export default function VisualNovelEditorPage() {
|
||||
}, [scriptText]);
|
||||
|
||||
const previewStatements = useMemo(() =>
|
||||
statements.filter(s => s.type === 'say'),
|
||||
statements.filter(s => s.type === 'say' || s.type === 'choice'),
|
||||
[statements]
|
||||
);
|
||||
|
||||
const currentStatement = previewStatements[currentSayIndex] || null;
|
||||
|
||||
const handleChoiceSelect = (jumpLabel: string) => {
|
||||
const labelIdx = statements.findIndex(s => s.type === 'label' && s.label === jumpLabel);
|
||||
if (labelIdx !== -1) {
|
||||
// Find the first 'previewable' statement at or after the label
|
||||
const nextPreviewIdx = previewStatements.findIndex(s => s.lineNumber >= statements[labelIdx].lineNumber);
|
||||
if (nextPreviewIdx !== -1) {
|
||||
setCurrentSayIndex(nextPreviewIdx);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleSave = () => {
|
||||
const blob = new Blob([scriptText], { type: 'text/plain' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
@@ -125,6 +158,7 @@ export default function VisualNovelEditorPage() {
|
||||
isParsing={isParsing}
|
||||
spriteBank={spriteBank}
|
||||
onUpdateSpriteBank={updateSpriteBank}
|
||||
onAddSpriteToScript={addSpriteToScript}
|
||||
/>
|
||||
|
||||
<div className="flex-1 flex flex-col">
|
||||
@@ -166,12 +200,13 @@ export default function VisualNovelEditorPage() {
|
||||
onNext={() => setCurrentSayIndex(prev => Math.min(previewStatements.length - 1, prev + 1))}
|
||||
statements={statements}
|
||||
spriteBank={spriteBank}
|
||||
onChoiceSelect={handleChoiceSelect}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style jsx global>{`
|
||||
@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&family=Noto+Serif:ital,wght@0,400;0,700;1,400&display=swap');
|
||||
@import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&family=Noto+Serif:ital,wght@0,400;0,700;1,400;1,700&display=swap');
|
||||
|
||||
.custom-scrollbar::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
|
||||
Reference in New Issue
Block a user