diff --git a/public/icon64.png b/public/icon64.png new file mode 100644 index 0000000..e7ce54b Binary files /dev/null and b/public/icon64.png differ diff --git a/public/manifest.json b/public/manifest.json index bbb9836..512e29d 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -14,9 +14,10 @@ "initTimeout": 10 }, "icons": { - "64x64": "/icon.png" + "64x64": "/icon64.png" }, "environment": { + "webViewBackgroundColor": "#1a1a1a", "capabilities": ["chat", "dice"], "extras": ["colorStyles"] } diff --git a/src/components/CharacterSheet.css b/src/components/CharacterSheet.css index 236c21c..5613270 100644 --- a/src/components/CharacterSheet.css +++ b/src/components/CharacterSheet.css @@ -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; +} \ No newline at end of file diff --git a/src/components/CharacterSheet.tsx b/src/components/CharacterSheet.tsx index 4a0ee9a..b10232f 100644 --- a/src/components/CharacterSheet.tsx +++ b/src/components/CharacterSheet.tsx @@ -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(initialCharacter); + // Hilfsfunktion um zu prüfen ob Charakter Zauberer ist + const isSpellcaster = character.astralEnergy && character.astralEnergy.max > 0; + return (
+
- DSA 5e Logo + DSA 5e Character Sheet

DSA 5 Character Sheet

+ + {/* Grundinformationen */} + + {/* Eigenschaften */} - + + {/* Kampfwerte */} + + {/* Fertigkeiten */} + + + {/* Zauber - nur anzeigen wenn Astralenergie > 0 oder explizit gewünscht */} + {(isSpellcaster || Object.keys(character.spells).length > 0) && ( + + )} + + {/* Button zum Hinzufügen von Zaubern falls noch kein Zauberer */} + {!isSpellcaster && Object.keys(character.spells).length === 0 && ( +
+ +
+ )}
); }; diff --git a/src/components/Spells.tsx b/src/components/Spells.tsx new file mode 100644 index 0000000..37620b2 --- /dev/null +++ b/src/components/Spells.tsx @@ -0,0 +1,235 @@ +import React, { useState } from 'react'; +import type { DSACharacter, DSASpell, DSAAttributes } from '../types/character'; + +interface SpellsProps { + character: DSACharacter; + setCharacter: React.Dispatch>; +} + +const Spells: React.FC = ({ character, setCharacter }) => { + const [newSpell, setNewSpell] = useState>({ + 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[] = [ + { + 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) => { + 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 ( +
+

Zauber & Astralenergie

+ + {/* Astralenergie Tracking */} + {character.astralEnergy && ( +
+

Astralenergie (AsP)

+
+
+ + updateAstralEnergy('current', parseInt(e.target.value) || 0)} + min="0" + /> +
+
+ + updateAstralEnergy('max', parseInt(e.target.value) || 0)} + min="0" + /> +
+
+
+ )} + + {/* Häufige Zauber hinzufügen */} +
+

Häufige Zauber hinzufügen

+
+ {commonSpells.map((spell, index) => ( + + ))} +
+
+ + {/* Zauberliste */} +
+

Beherrschte Zauber

+ {Object.keys(character.spells).length === 0 ? ( +

Noch keine Zauber erlernt. Füge welche hinzu!

+ ) : ( +
+ {Object.entries(character.spells).map(([spellId, spell]) => ( +
+
+

{spell.name}

+ +
+ +
+
+ {spell.attributes.map(attr => attr.substring(0, 2).toUpperCase()).join('/')} +
+ +
+
AsP: {spell.aspCost}
+
Dauer: {spell.castingTime}
+
Reichweite: {spell.range}
+
+
+ +
+ + updateSpell(spellId, 'skillValue', parseInt(e.target.value) || 0)} + min="0" + max="20" + /> +
+ +
+ Gesamt: {calculateSpellCheck(spell)} +
+ + {spell.description && ( +
+ {spell.description} +
+ )} +
+ ))} +
+ )} +
+
+ ); +}; + +export default Spells; \ No newline at end of file diff --git a/src/styles/talespire-variables.css b/src/styles/talespire-variables.css index 81b8083..437a6d5 100644 --- a/src/styles/talespire-variables.css +++ b/src/styles/talespire-variables.css @@ -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, } diff --git a/src/types/character.ts b/src/types/character.ts index ce3b1e1..e0fbcdb 100644 --- a/src/types/character.ts +++ b/src/types/character.ts @@ -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; + magicalTraditions: string[]; }