Workflow und Layout repariert.

Übersetzungen hinzugefügt
This commit is contained in:
2025-08-13 09:30:35 +02:00
parent 99dcfb57be
commit 2e32363863
4 changed files with 287 additions and 81 deletions
@@ -1,14 +1,14 @@
<template>
<div class="attributes-form character-creation-container">
<h2>Character Attributes</h2>
<p class="instruction">Set the basic attributes for your character (1-100)</p>
<h2>{{ $t('characters.attributes.title') }}</h2>
<p class="instruction">{{ $t('characters.attributes.instruction') }}</p>
<form @submit.prevent="handleSubmit" class="attributes-form-content">
<div class="attributes-grid">
<div class="attribute-group" v-for="attr in attributes" :key="attr.key">
<div class="attribute-row">
<label :for="attr.key" class="attribute-label">
{{ attr.name }} ({{ attr.key.toUpperCase() }})
{{ $t(`characters.attributes.${attr.key}`) }} ({{ attr.key.toUpperCase() }})
</label>
<div class="input-with-dice">
<input
@@ -19,23 +19,23 @@
max="100"
required
class="attribute-input"
@input="updateTotal"
@input="handleAttributeChange"
/>
<button
type="button"
class="dice-btn"
@click="rollAttribute(attr.key)"
:title="attr.key === 'au' ? 'Roll AU: 1d100 with race restrictions (Elfen ≥81, Gnome/Zwerge ≤80)' :
'Roll max(2d100) for ' + attr.name"
:title="attr.key === 'au' ? $t('characters.attributes.rollTooltipAu') :
$t('characters.attributes.rollTooltipOther') + ' ' + $t(`characters.attributes.${attr.key}`)"
>
🎲
</button>
</div>
</div>
<span class="attribute-description">{{ attr.description }}</span>
<span class="attribute-description">{{ $t(`characters.attributes.${attr.key}Description`) }}</span>
<!-- Race restriction warning for AU -->
<div v-if="attr.key === 'au' && auRaceRestriction" class="race-restriction-warning">
{{ auRaceRestriction.message }}
{{ $t(`characters.attributes.raceRestriction${auRaceRestriction.raceKey}`) }}
</div>
<div v-if="lastAttributeRoll && lastAttributeRoll.attribute === attr.key" class="roll-result">
{{ attr.name }}: {{ lastAttributeRoll.roll }}
@@ -54,22 +54,22 @@
<div class="attribute-summary">
<div class="total-points">
<strong>Total Points: {{ totalPoints }}</strong>
<strong>{{ $t('characters.attributes.totalPoints') }}: {{ totalPoints }}</strong>
</div>
<div class="average-points">
<strong>Average: {{ averagePoints.toFixed(1) }}</strong>
<strong>{{ $t('characters.attributes.averagePoints') }}: {{ averagePoints.toFixed(1) }}</strong>
</div>
<button type="button" @click="rollAllAttributes" class="roll-all-btn">
🎲 Roll All Attributes
🎲 {{ $t('characters.attributes.rollAllAttributes') }}
</button>
</div>
<div class="form-actions">
<button type="button" @click="handlePrevious" class="prev-btn">
Previous: Basic Info
{{ $t('characters.attributes.previousBasicInfo') }}
</button>
<button type="submit" class="next-btn" :disabled="!isValid">
Next: Derived Values
{{ $t('characters.attributes.nextDerivedValues') }}
</button>
</div>
</form>
@@ -93,7 +93,7 @@
<div class="overlay-result">
{{ lastAttributeRoll.attributeName }}: {{ lastAttributeRoll.result }}
</div>
<div class="overlay-hint">Click to close</div>
<div class="overlay-hint">{{ $t('characters.attributes.clickToClose') }}</div>
</div>
</div>
</div>
@@ -112,13 +112,13 @@ export default {
data() {
return {
formData: {
st: 50, // Stärke
gs: 50, // Geschicklichkeit
gw: 50, // Gewandtheit
ko: 50, // Konstitution
in: 50, // Intelligenz
zt: 50, // Zaubertalent
au: 50, // Ausehen
st: 0, // Stärke
gs: 0, // Geschicklichkeit
gw: 0, // Gewandtheit
ko: 0, // Konstitution
in: 0, // Intelligenz
zt: 0, // Zaubertalent
au: 0, // Ausehen
},
attributes: [
{
@@ -165,7 +165,26 @@ export default {
},
computed: {
isValid() {
return Object.values(this.formData).every(val => val >= 1 && val <= 100)
// Check basic value range (1-100) for only the defined attributes
const definedKeys = this.attributes.map(attr => attr.key)
const relevantValues = definedKeys.map(key => this.formData[key])
const basicValid = relevantValues.every(val => val >= 1 && val <= 100)
if (!basicValid) return false
// Check race-specific AU restrictions
const race = this.sessionData.rasse || ''
const auValue = this.formData.au
if (race === 'Elfen' && auValue < 81) {
return false // Elfen must have AU ≥ 81
}
if ((race === 'Gnome' || race === 'Zwerge') && auValue > 80) {
return false // Gnome/Zwerge must have AU ≤ 80
}
return true
},
averagePoints() {
return this.totalPoints / Object.keys(this.formData).length
@@ -173,11 +192,11 @@ export default {
auRaceRestriction() {
const race = this.sessionData.rasse || ''
if (race === 'Elfen') {
return { type: 'minimum', value: 81, message: 'Elfen must have AU ≥ 81' }
return { type: 'minimum', value: 81, raceKey: 'Elves' }
} else if (race === 'Gnome') {
return { type: 'maximum', value: 80, message: 'Gnome must have AU ≤ 80' }
return { type: 'maximum', value: 80, raceKey: 'Gnomes' }
} else if (race === 'Zwerge') {
return { type: 'maximum', value: 80, message: 'Zwerge must have AU ≤ 80' }
return { type: 'maximum', value: 80, raceKey: 'Dwarves' }
}
return null
}
@@ -196,6 +215,11 @@ export default {
}
},
methods: {
handleAttributeChange(event) {
// Simple update - Vue's reactivity should handle the rest
this.updateTotal()
},
updateTotal() {
this.totalPoints = Object.values(this.formData).reduce((sum, val) => sum + (val || 0), 0)
},
@@ -317,7 +341,7 @@ export default {
}
</script>
<style scoped>
.attributes-form {
max-width: 800px;
margin: 0 auto;
@@ -506,4 +530,5 @@ export default {
padding: 10px;
}
}
</style>
@@ -3,6 +3,8 @@
<h2>{{ $t('characters.basicInfo.title') }}</h2>
<form @submit.prevent="handleSubmit">
<div class="form-row">
<!-- 1. Name -->
<div class="form-group">
<label for="name">{{ $t('characters.basicInfo.characterName') }} {{ $t('characters.basicInfo.required') }}</label>
<input
@@ -16,7 +18,46 @@
/>
</div>
<!-- 2. Herkunft -->
<div class="form-group">
<label for="herkunft">{{ $t('characters.basicInfo.origin') }} {{ $t('characters.basicInfo.required') }}</label>
<select id="herkunft" v-model="formData.herkunft" required>
<option value="">{{ $t('characters.basicInfo.selectOrigin') }}</option>
<option v-for="origin in origins" :key="origin" :value="origin">{{ origin }}</option>
</select>
</div>
</div>
<!-- 3. Glaube -->
<div class="form-group">
<label for="glaube">{{ $t('characters.basicInfo.religion') }}</label>
<div class="belief-search">
<input
id="glaube"
v-model="beliefSearch"
type="text"
:placeholder="$t('characters.basicInfo.religionPlaceholder')"
@input="searchBeliefs"
/>
<div v-if="beliefResults.length > 0" class="belief-dropdown">
<div
v-for="belief in beliefResults"
:key="belief"
class="belief-option"
@click="selectBelief(belief)"
>
{{ belief }}
</div>
</div>
</div>
<div v-if="formData.glaube" class="selected-belief">
{{ $t('characters.basicInfo.selected') }}: {{ formData.glaube }}
<button type="button" @click="clearBelief" class="clear-btn">×</button>
</div>
</div>
<div class="form-row">
<!-- 4. Geschlecht -->
<div class="form-group">
<label for="geschlecht">{{ $t('characters.basicInfo.gender') }} {{ $t('characters.basicInfo.required') }}</label>
<select id="geschlecht" v-model="formData.geschlecht" required>
@@ -26,6 +67,7 @@
</select>
</div>
<!-- 5. Rasse -->
<div class="form-group">
<label for="rasse">{{ $t('characters.basicInfo.race') }} {{ $t('characters.basicInfo.required') }}</label>
<select id="rasse" v-model="formData.rasse" required>
@@ -36,6 +78,7 @@
</div>
<div class="form-row">
<!-- 6. Charakterklasse -->
<div class="form-group">
<label for="typ">{{ $t('characters.basicInfo.characterClass') }} {{ $t('characters.basicInfo.required') }}</label>
<select id="typ" v-model="formData.typ" required>
@@ -44,24 +87,7 @@
</select>
</div>
<div class="form-group">
<label for="herkunft">{{ $t('characters.basicInfo.origin') }} {{ $t('characters.basicInfo.required') }}</label>
<select id="herkunft" v-model="formData.herkunft" required>
<option value="">{{ $t('characters.basicInfo.selectOrigin') }}</option>
<option v-for="origin in origins" :key="origin" :value="origin">{{ origin }}</option>
</select>
</div>
</div>
<div class="form-row">
<div class="form-group">
<label for="herkunft">{{ $t('characters.basicInfo.origin') }} {{ $t('characters.basicInfo.required') }}</label>
<select id="herkunft" v-model="formData.herkunft" required>
<option value="">{{ $t('characters.basicInfo.selectOrigin') }}</option>
<option v-for="origin in origins" :key="origin" :value="origin">{{ origin }}</option>
</select>
</div>
<!-- 7. Sozialschicht -->
<div class="form-group">
<label for="stand">{{ $t('characters.basicInfo.socialClass') }} {{ $t('characters.basicInfo.required') }}</label>
<div class="input-with-dice">
@@ -92,33 +118,6 @@
</div>
</div>
<div class="form-group">
<label for="glaube">{{ $t('characters.basicInfo.religion') }}</label>
<div class="belief-search">
<input
id="glaube"
v-model="beliefSearch"
type="text"
:placeholder="$t('characters.basicInfo.religionPlaceholder')"
@input="searchBeliefs"
/>
<div v-if="beliefResults.length > 0" class="belief-dropdown">
<div
v-for="belief in beliefResults"
:key="belief"
class="belief-option"
@click="selectBelief(belief)"
>
{{ belief }}
</div>
</div>
</div>
<div v-if="formData.glaube" class="selected-belief">
{{ $t('characters.basicInfo.selected') }}: {{ formData.glaube }}
<button type="button" @click="clearBelief" class="clear-btn">×</button>
</div>
</div>
<div class="form-actions">
<button type="submit" class="next-btn" :disabled="!isValid">
{{ $t('characters.basicInfo.nextAttributes') }}
@@ -493,4 +492,118 @@ input:focus, select:focus {
background-color: #ccc;
cursor: not-allowed;
}
/* Social class roll styles */
.input-with-dice {
display: flex;
gap: 8px;
align-items: center;
}
.input-with-dice select {
flex: 1;
}
.dice-btn {
padding: 10px 12px;
background-color: #4caf50;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
transition: background-color 0.3s;
flex-shrink: 0;
}
.dice-btn:hover:not(:disabled) {
background-color: #45a049;
}
.dice-btn:disabled {
background-color: #ccc;
cursor: not-allowed;
}
.roll-result {
margin-top: 8px;
padding: 8px;
background-color: #e8f5e8;
border-radius: 4px;
font-size: 14px;
color: #2e7d32;
}
/* Roll overlay styles */
.roll-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.7);
display: flex;
justify-content: center;
align-items: center;
z-index: 10000;
cursor: pointer;
}
.roll-overlay-content {
background: white;
padding: 30px;
border-radius: 10px;
text-align: center;
position: relative;
cursor: default;
min-width: 300px;
}
.overlay-close {
position: absolute;
top: 10px;
right: 15px;
background: none;
border: none;
font-size: 24px;
cursor: pointer;
color: #666;
}
.overlay-close:hover {
color: #000;
}
.overlay-title {
font-size: 20px;
font-weight: bold;
margin-bottom: 15px;
color: #333;
}
.overlay-roll {
font-size: 36px;
font-weight: bold;
color: #4caf50;
margin: 20px 0;
}
.roll-breakdown {
font-size: 18px;
color: #666;
margin-left: 10px;
}
.overlay-result {
font-size: 24px;
font-weight: bold;
color: #2196f3;
margin: 20px 0;
}
.overlay-hint {
font-size: 14px;
color: #666;
margin-top: 15px;
}
</style>
+34
View File
@@ -251,6 +251,40 @@ export default {
volk: 'Volk',
unfrei: 'Unfrei'
},
attributes: {
title: 'Charakterattribute',
instruction: 'Setzen Sie die Grundattribute für Ihren Charakter (1-100)',
totalPoints: 'Gesamtpunkte',
averagePoints: 'Durchschnitt',
rollAllAttributes: 'Alle Attribute würfeln',
previousBasicInfo: 'Zurück: Grundinformationen',
nextDerivedValues: 'Weiter: Abgeleitete Werte',
enabled: 'AKTIVIERT',
disabled: 'DEAKTIVIERT',
clickToClose: 'Klicken zum Schließen',
// Einzelne Attribute
st: 'Stärke',
stDescription: 'Körperliche Kraft und Stärke',
gs: 'Geschicklichkeit',
gsDescription: 'Geschicklichkeit und manuelle Fertigkeiten',
gw: 'Gewandtheit',
gwDescription: 'Beweglichkeit und schnelle Reaktionen',
ko: 'Konstitution',
koDescription: 'Gesundheit und Ausdauer',
in: 'Intelligenz',
inDescription: 'Lernfähigkeit und Logik',
zt: 'Zaubertalent',
ztDescription: 'Magisches Talent und Mana',
au: 'Aussehen',
auDescription: 'Schönheit und Aussehen (Rassenbeschränkungen: Elfen ≥81, Gnome/Zwerge ≤80)',
// Rassenbeschränkungen
raceRestrictionElves: 'Elfen müssen Aussehen ≥ 81 haben',
raceRestrictionGnomes: 'Gnome müssen Aussehen ≤ 80 haben',
raceRestrictionDwarves: 'Zwerge müssen Aussehen ≤ 80 haben',
// Würfel-Tooltips
rollTooltipAu: 'Würfle Aussehen: 1d100 mit Rassenbeschränkungen (Elfen ≥81, Gnome/Zwerge ≤80)',
rollTooltipOther: 'Würfle max(2d100) für'
},
derivedValues: {
title: 'Abgeleitete Werte',
instruction: 'Diese Werte werden aus Ihren Attributen berechnet. Sie können sie nach Bedarf anpassen.',
+34
View File
@@ -157,6 +157,40 @@ export default {
volk: 'Common Folk',
unfrei: 'Unfree'
},
attributes: {
title: 'Character Attributes',
instruction: 'Set the basic attributes for your character (1-100)',
totalPoints: 'Total Points',
averagePoints: 'Average',
rollAllAttributes: 'Roll All Attributes',
previousBasicInfo: 'Previous: Basic Info',
nextDerivedValues: 'Next: Derived Values',
enabled: 'ENABLED',
disabled: 'DISABLED',
clickToClose: 'Click to close',
// Individual attributes
st: 'Strength',
stDescription: 'Physical strength and power',
gs: 'Dexterity',
gsDescription: 'Dexterity and manual skill',
gw: 'Agility',
gwDescription: 'Agility and quick reactions',
ko: 'Constitution',
koDescription: 'Health and endurance',
in: 'Intelligence',
inDescription: 'Learning ability and logic',
zt: 'Spell Talent',
ztDescription: 'Magical talent and mana',
au: 'Beauty',
auDescription: 'Beauty and appearance (Race restrictions: Elves ≥81, Gnomes/Dwarves ≤80)',
// Race restrictions
raceRestrictionElves: 'Elves must have Beauty ≥ 81',
raceRestrictionGnomes: 'Gnomes must have Beauty ≤ 80',
raceRestrictionDwarves: 'Dwarves must have Beauty ≤ 80',
// Roll tooltips
rollTooltipAu: 'Roll Beauty: 1d100 with race restrictions (Elves ≥81, Gnomes/Dwarves ≤80)',
rollTooltipOther: 'Roll max(2d100) for'
},
derivedValues: {
title: 'Derived Values',
instruction: 'These values are calculated from your attributes. You can adjust them as needed.',