add choice mechanic

This commit is contained in:
Stanislaw Dzioba
2026-03-23 19:44:41 +01:00
parent 1373e1e128
commit 86edc3bf88
8 changed files with 300 additions and 136 deletions

View File

@@ -1,4 +1,4 @@
import React, { useState } from 'react';
import React, { useState, useRef } from 'react';
import { Statement } from '@/lib/vn-parser';
import { Sprite } from '@/lib/sprite-bank';
@@ -9,6 +9,7 @@ interface VNOutlineProps {
isParsing: boolean;
spriteBank: Record<string, Sprite[]>;
onUpdateSpriteBank: (newBank: Record<string, Sprite[]>) => void;
onAddSpriteToScript: (char: string, id: string, url: string) => void;
}
export const VNOutline: React.FC<VNOutlineProps> = ({
@@ -17,10 +18,13 @@ export const VNOutline: React.FC<VNOutlineProps> = ({
onSelectLine,
isParsing,
spriteBank,
onUpdateSpriteBank
onUpdateSpriteBank,
onAddSpriteToScript
}) => {
const [activeTab, setActiveTab] = useState<'outline' | 'sprites'>('outline');
const [newCharName, setNewCharName] = useState('');
const [uploadingFor, setUploadingFor] = useState<string | null>(null);
const fileInputRef = useRef<HTMLInputElement>(null);
const addCharacter = () => {
if (!newCharName || spriteBank[newCharName]) return;
@@ -33,14 +37,39 @@ export const VNOutline: React.FC<VNOutlineProps> = ({
onUpdateSpriteBank(rest);
};
const addSprite = (charName: string) => {
const addSpriteUrl = (charName: string) => {
const id = prompt('Sprite ID (e.g. happy):');
if (!id) return;
const url = prompt('Sprite Image URL:');
if (!url) return;
const newSprites = [...spriteBank[charName], { id, name: id, url }];
onUpdateSpriteBank({ ...spriteBank, [charName]: newSprites });
onAddSpriteToScript(charName, id, url);
};
const handleFileUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (!file || !uploadingFor) return;
const id = prompt('Sprite ID (e.g. happy):');
if (!id) return;
const reader = new FileReader();
reader.onload = (e) => {
const url = e.target?.result as string;
if (url) {
onAddSpriteToScript(uploadingFor, id, url);
}
};
reader.readAsDataURL(file);
// Reset file input
if (fileInputRef.current) fileInputRef.current.value = '';
setUploadingFor(null);
};
const triggerFileUpload = (charName: string) => {
setUploadingFor(charName);
fileInputRef.current?.click();
};
const removeSprite = (charName: string, spriteId: string) => {
@@ -132,15 +161,16 @@ export const VNOutline: React.FC<VNOutlineProps> = ({
{charName}
</h3>
<div className="flex gap-2">
<button onClick={() => addSprite(charName)} className="text-[8px] text-gray-400 hover:text-cyan-400 uppercase font-bold tracking-tighter">Add Sprite</button>
<button onClick={() => addSpriteUrl(charName)} className="text-[8px] text-gray-400 hover:text-cyan-400 uppercase font-bold tracking-tighter">URL</button>
<button onClick={() => triggerFileUpload(charName)} className="text-[8px] text-gray-400 hover:text-cyan-400 uppercase font-bold tracking-tighter">Upload</button>
<button onClick={() => removeCharacter(charName)} className="text-[8px] text-gray-400 hover:text-red-400 uppercase font-bold tracking-tighter">Delete</button>
</div>
</div>
<div className="grid grid-cols-2 gap-2">
{sprites.map(sprite => (
<div key={sprite.id} className="group relative">
<div className="aspect-[2/3] bg-gray-900 rounded border border-gray-800 overflow-hidden">
<img src={sprite.url} alt={sprite.name} className="w-full h-full object-cover opacity-60 group-hover:opacity-100 transition-opacity" />
<div className="aspect-[2/3] bg-gray-900 rounded border border-gray-800 overflow-hidden p-1">
<img src={sprite.url} alt={sprite.name} className="w-full h-full object-contain opacity-60 group-hover:opacity-100 transition-opacity" />
</div>
<div className="absolute bottom-0 inset-x-0 bg-black/80 p-1 text-[8px] font-bold flex justify-between items-center group-hover:bg-cyan-950/90">
<span className="truncate flex-1">{sprite.id}</span>
@@ -156,6 +186,13 @@ export const VNOutline: React.FC<VNOutlineProps> = ({
</div>
</div>
))}
<input
type="file"
ref={fileInputRef}
className="hidden"
accept="image/*"
onChange={handleFileUpload}
/>
</div>
)}
</div>