added spells
This commit is contained in:
BIN
public/icon64.png
Normal file
BIN
public/icon64.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.1 KiB |
@@ -14,9 +14,10 @@
|
||||
"initTimeout": 10
|
||||
},
|
||||
"icons": {
|
||||
"64x64": "/icon.png"
|
||||
"64x64": "/icon64.png"
|
||||
},
|
||||
"environment": {
|
||||
"webViewBackgroundColor": "#1a1a1a",
|
||||
"capabilities": ["chat", "dice"],
|
||||
"extras": ["colorStyles"]
|
||||
}
|
||||
|
@@ -541,3 +541,8 @@ button:active {
|
||||
transform: scale(1.1);
|
||||
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
body {
|
||||
background: var(--ts-background-primary, var(--background-color));
|
||||
transition: background-color 0.3s ease;
|
||||
}
|
@@ -1,13 +1,16 @@
|
||||
// src/components/CharacterSheet.tsx
|
||||
import React, { useState } from 'react';
|
||||
import type {DSACharacter} from '../types/character';
|
||||
import type { DSACharacter } from '../types/character';
|
||||
import BasicInfo from './BasicInfo';
|
||||
import Attributes from './Attributes';
|
||||
import Skills from './Skills';
|
||||
import Spells from './Spells';
|
||||
import CombatValues from './CombatValues';
|
||||
import './CharacterSheet.css';
|
||||
import ThemeToggle from "./ThemeToggle.tsx";
|
||||
import ThemeToggle from './ThemeToggle';
|
||||
import iconUrl from '/icon.png';
|
||||
import './CharacterSheet.css';
|
||||
|
||||
// Erweiterte Initial-Daten mit Astralenergie und Zaubern
|
||||
const initialCharacter: DSACharacter = {
|
||||
id: crypto.randomUUID(),
|
||||
name: '',
|
||||
@@ -26,12 +29,18 @@ const initialCharacter: DSACharacter = {
|
||||
strength: 8
|
||||
},
|
||||
skills: {},
|
||||
spells: {}, // ← Neu hinzugefügt
|
||||
combat: {
|
||||
lifePoints: { max: 30, current: 30 },
|
||||
stamina: { max: 30, current: 30 },
|
||||
initiative: 10,
|
||||
speed: 8
|
||||
},
|
||||
astralEnergy: { // ← Neu hinzugefügt für Zauberer
|
||||
max: 0,
|
||||
current: 0
|
||||
},
|
||||
magicalTraditions: [], // ← Neu hinzugefügt
|
||||
advantages: [],
|
||||
disadvantages: [],
|
||||
equipment: []
|
||||
@@ -40,17 +49,53 @@ const initialCharacter: DSACharacter = {
|
||||
const CharacterSheet: React.FC = () => {
|
||||
const [character, setCharacter] = useState<DSACharacter>(initialCharacter);
|
||||
|
||||
// Hilfsfunktion um zu prüfen ob Charakter Zauberer ist
|
||||
const isSpellcaster = character.astralEnergy && character.astralEnergy.max > 0;
|
||||
|
||||
return (
|
||||
<div className="character-sheet">
|
||||
<ThemeToggle />
|
||||
|
||||
<header className="character-sheet-header">
|
||||
<img src={iconUrl} alt="DSA 5e Logo" className="character-sheet-icon" />
|
||||
<img
|
||||
src={iconUrl}
|
||||
alt="DSA 5e Character Sheet"
|
||||
className="character-sheet-icon"
|
||||
/>
|
||||
<h1>DSA 5 Character Sheet</h1>
|
||||
</header>
|
||||
|
||||
{/* Grundinformationen */}
|
||||
<BasicInfo character={character} setCharacter={setCharacter} />
|
||||
|
||||
{/* Eigenschaften */}
|
||||
<Attributes character={character} setCharacter={setCharacter} />
|
||||
<Skills character={character} setCharacter={setCharacter} />
|
||||
|
||||
{/* Kampfwerte */}
|
||||
<CombatValues character={character} setCharacter={setCharacter} />
|
||||
|
||||
{/* Fertigkeiten */}
|
||||
<Skills character={character} setCharacter={setCharacter} />
|
||||
|
||||
{/* Zauber - nur anzeigen wenn Astralenergie > 0 oder explizit gewünscht */}
|
||||
{(isSpellcaster || Object.keys(character.spells).length > 0) && (
|
||||
<Spells character={character} setCharacter={setCharacter} />
|
||||
)}
|
||||
|
||||
{/* Button zum Hinzufügen von Zaubern falls noch kein Zauberer */}
|
||||
{!isSpellcaster && Object.keys(character.spells).length === 0 && (
|
||||
<div className="add-magic-section">
|
||||
<button
|
||||
className="add-magic-button"
|
||||
onClick={() => setCharacter(prev => ({
|
||||
...prev,
|
||||
astralEnergy: { max: 20, current: 20 }
|
||||
}))}
|
||||
>
|
||||
🪄 Zauberfähigkeiten hinzufügen
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
235
src/components/Spells.tsx
Normal file
235
src/components/Spells.tsx
Normal file
@@ -0,0 +1,235 @@
|
||||
import React, { useState } from 'react';
|
||||
import type { DSACharacter, DSASpell, DSAAttributes } from '../types/character';
|
||||
|
||||
interface SpellsProps {
|
||||
character: DSACharacter;
|
||||
setCharacter: React.Dispatch<React.SetStateAction<DSACharacter>>;
|
||||
}
|
||||
|
||||
const Spells: React.FC<SpellsProps> = ({ character, setCharacter }) => {
|
||||
const [newSpell, setNewSpell] = useState<Partial<DSASpell>>({
|
||||
name: '',
|
||||
tradition: [],
|
||||
attributes: ['cleverness', 'intuition', 'charisma'],
|
||||
skillValue: 0,
|
||||
aspCost: '4 AsP',
|
||||
castingTime: '2 Aktionen',
|
||||
range: 'Berührung',
|
||||
duration: 'Sofort',
|
||||
difficulty: 0,
|
||||
description: '',
|
||||
effect: ''
|
||||
});
|
||||
|
||||
// Häufige DSA 5e Zauber für Quick-Add
|
||||
const commonSpells: Partial<DSASpell>[] = [
|
||||
{
|
||||
name: 'Ignifaxius',
|
||||
tradition: ['Gildenmagier'],
|
||||
attributes: ['cleverness', 'dexterity', 'constitution'],
|
||||
aspCost: '2 AsP',
|
||||
castingTime: '2 Aktionen',
|
||||
range: '7 Meter',
|
||||
duration: 'Sofort',
|
||||
difficulty: 0,
|
||||
description: 'Entzündet brennbares Material'
|
||||
},
|
||||
{
|
||||
name: 'Odem Arcanum',
|
||||
tradition: ['Gildenmagier'],
|
||||
attributes: ['cleverness', 'intuition', 'intuition'],
|
||||
aspCost: '1 AsP',
|
||||
castingTime: '2 Aktionen',
|
||||
range: 'Berührung',
|
||||
duration: 'Sofort',
|
||||
difficulty: 0,
|
||||
description: 'Erkennt magische Objekte und Wesen'
|
||||
},
|
||||
{
|
||||
name: 'Balsam Salabunde',
|
||||
tradition: ['Gildenmagier', 'Druide'],
|
||||
attributes: ['cleverness', 'intuition', 'dexterity'],
|
||||
aspCost: '4 AsP (A), 2 AsP (B), 8 AsP (C)',
|
||||
castingTime: '8 Aktionen',
|
||||
range: 'Berührung',
|
||||
duration: 'Sofort',
|
||||
difficulty: 0,
|
||||
description: 'Heilt Wunden'
|
||||
}
|
||||
];
|
||||
|
||||
const addSpell = (spellData: Partial<DSASpell>) => {
|
||||
const spell: DSASpell = {
|
||||
id: crypto.randomUUID(),
|
||||
name: spellData.name || '',
|
||||
tradition: spellData.tradition || [],
|
||||
attributes: spellData.attributes || ['cleverness', 'intuition', 'charisma'],
|
||||
skillValue: spellData.skillValue || 0,
|
||||
aspCost: spellData.aspCost || '4 AsP',
|
||||
castingTime: spellData.castingTime || '2 Aktionen',
|
||||
range: spellData.range || 'Berührung',
|
||||
duration: spellData.duration || 'Sofort',
|
||||
difficulty: spellData.difficulty || 0,
|
||||
description: spellData.description || '',
|
||||
effect: spellData.effect || ''
|
||||
};
|
||||
|
||||
setCharacter(prev => ({
|
||||
...prev,
|
||||
spells: {
|
||||
...prev.spells,
|
||||
[spell.id]: spell
|
||||
}
|
||||
}));
|
||||
};
|
||||
|
||||
const updateSpell = (spellId: string, field: keyof DSASpell, value: any) => {
|
||||
setCharacter(prev => ({
|
||||
...prev,
|
||||
spells: {
|
||||
...prev.spells,
|
||||
[spellId]: {
|
||||
...prev.spells[spellId],
|
||||
[field]: value
|
||||
}
|
||||
}
|
||||
}));
|
||||
};
|
||||
|
||||
const removeSpell = (spellId: string) => {
|
||||
setCharacter(prev => {
|
||||
const { [spellId]: removed, ...remainingSpells } = prev.spells;
|
||||
return {
|
||||
...prev,
|
||||
spells: remainingSpells
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const calculateSpellCheck = (spell: DSASpell): number => {
|
||||
const [attr1, attr2, attr3] = spell.attributes;
|
||||
return character.attributes[attr1] + character.attributes[attr2] + character.attributes[attr3] + spell.skillValue;
|
||||
};
|
||||
|
||||
const updateAstralEnergy = (field: 'max' | 'current', value: number) => {
|
||||
setCharacter(prev => ({
|
||||
...prev,
|
||||
astralEnergy: {
|
||||
...prev.astralEnergy!,
|
||||
[field]: Math.max(0, value)
|
||||
}
|
||||
}));
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="spells section">
|
||||
<h2>Zauber & Astralenergie</h2>
|
||||
|
||||
{/* Astralenergie Tracking */}
|
||||
{character.astralEnergy && (
|
||||
<div className="astral-energy">
|
||||
<h3>Astralenergie (AsP)</h3>
|
||||
<div className="value-pair">
|
||||
<div>
|
||||
<label htmlFor="asp-current">Aktuell</label>
|
||||
<input
|
||||
id="asp-current"
|
||||
type="number"
|
||||
value={character.astralEnergy.current}
|
||||
onChange={(e) => updateAstralEnergy('current', parseInt(e.target.value) || 0)}
|
||||
min="0"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="asp-max">Maximum</label>
|
||||
<input
|
||||
id="asp-max"
|
||||
type="number"
|
||||
value={character.astralEnergy.max}
|
||||
onChange={(e) => updateAstralEnergy('max', parseInt(e.target.value) || 0)}
|
||||
min="0"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Häufige Zauber hinzufügen */}
|
||||
<div className="common-spells">
|
||||
<h3>Häufige Zauber hinzufügen</h3>
|
||||
<div className="spell-buttons">
|
||||
{commonSpells.map((spell, index) => (
|
||||
<button
|
||||
key={index}
|
||||
onClick={() => addSpell(spell)}
|
||||
className="spell-add-button"
|
||||
disabled={Object.values(character.spells).some(s => s.name === spell.name)}
|
||||
>
|
||||
{spell.name}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Zauberliste */}
|
||||
<div className="spells-list">
|
||||
<h3>Beherrschte Zauber</h3>
|
||||
{Object.keys(character.spells).length === 0 ? (
|
||||
<p className="no-spells">Noch keine Zauber erlernt. Füge welche hinzu!</p>
|
||||
) : (
|
||||
<div className="spells-grid">
|
||||
{Object.entries(character.spells).map(([spellId, spell]) => (
|
||||
<div key={spellId} className="spell-item">
|
||||
<div className="spell-header">
|
||||
<h4>{spell.name}</h4>
|
||||
<button
|
||||
onClick={() => removeSpell(spellId)}
|
||||
className="remove-button"
|
||||
title="Zauber entfernen"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="spell-info">
|
||||
<div className="spell-attributes">
|
||||
<span>{spell.attributes.map(attr => attr.substring(0, 2).toUpperCase()).join('/')}</span>
|
||||
</div>
|
||||
|
||||
<div className="spell-details">
|
||||
<div><strong>AsP:</strong> {spell.aspCost}</div>
|
||||
<div><strong>Dauer:</strong> {spell.castingTime}</div>
|
||||
<div><strong>Reichweite:</strong> {spell.range}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="spell-value">
|
||||
<label>Fertigkeitswert:</label>
|
||||
<input
|
||||
type="number"
|
||||
value={spell.skillValue}
|
||||
onChange={(e) => updateSpell(spellId, 'skillValue', parseInt(e.target.value) || 0)}
|
||||
min="0"
|
||||
max="20"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="spell-total">
|
||||
<strong>Gesamt: {calculateSpellCheck(spell)}</strong>
|
||||
</div>
|
||||
|
||||
{spell.description && (
|
||||
<div className="spell-description">
|
||||
<em>{spell.description}</em>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Spells;
|
@@ -12,4 +12,5 @@
|
||||
/* Add other TaleSpire variables as you discover them */
|
||||
--ts-color-background: #f5f5f5;
|
||||
--ts-color-on-background: #222222;
|
||||
--ts-background-primary: #1a1a1a,
|
||||
}
|
||||
|
@@ -32,6 +32,21 @@ export interface DSACombatValues {
|
||||
speed: number;
|
||||
}
|
||||
|
||||
export interface DSASpell {
|
||||
id: string;
|
||||
name: string;
|
||||
tradition: string[]; // Gildenmagier, Hexe, Druide, etc.
|
||||
attributes: [keyof DSAAttributes, keyof DSAAttributes, keyof DSAAttributes];
|
||||
skillValue: number; // FW - Fertigkeitswert
|
||||
aspCost: string; // "4 AsP", "2 AsP pro Stufe"
|
||||
castingTime: string; // "2 Aktionen", "5 Minuten"
|
||||
range: string; // "Berührung", "7 Meter"
|
||||
duration: string; // "Sofort", "5 Minuten"
|
||||
difficulty: number; // Erschwernis/Erleichterung
|
||||
description: string;
|
||||
effect: string;
|
||||
}
|
||||
|
||||
export interface DSACharacter {
|
||||
id: string;
|
||||
name: string;
|
||||
@@ -45,4 +60,10 @@ export interface DSACharacter {
|
||||
advantages: string[]; // Vorteile
|
||||
disadvantages: string[]; // Nachteile
|
||||
equipment: string[]; // Ausrüstung
|
||||
astralEnergy?: {
|
||||
max: number;
|
||||
current: number;
|
||||
};
|
||||
spells: Record<string, DSASpell>;
|
||||
magicalTraditions: string[];
|
||||
}
|
||||
|
Reference in New Issue
Block a user