Stand auswürfeln
This commit is contained in:
@@ -751,4 +751,146 @@ a,
|
||||
.character-details {
|
||||
padding-bottom: 120px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Dice Button and Overlay Styles */
|
||||
.input-with-dice {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.input-with-dice select {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.dice-btn {
|
||||
background: #4CAF50;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
font-size: 18px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
.dice-btn:hover:not(:disabled) {
|
||||
background: #45a049;
|
||||
}
|
||||
|
||||
.dice-btn:disabled {
|
||||
background: #ccc;
|
||||
cursor: not-allowed;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.roll-result {
|
||||
margin-top: 8px;
|
||||
padding: 8px;
|
||||
background-color: #e8f5e8;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
color: #2e7d2e;
|
||||
border-left: 3px solid #4CAF50;
|
||||
}
|
||||
|
||||
/* Roll Overlay Styles */
|
||||
.roll-overlay {
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
z-index: 2000;
|
||||
cursor: pointer;
|
||||
animation: slideInRight 0.3s ease-out;
|
||||
}
|
||||
|
||||
.roll-overlay-content {
|
||||
background: linear-gradient(135deg, #4CAF50, #45a049);
|
||||
color: white;
|
||||
padding: 20px;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
|
||||
min-width: 200px;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
border: 2px solid rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
.overlay-close {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 12px;
|
||||
background: none;
|
||||
border: none;
|
||||
color: white;
|
||||
font-size: 20px;
|
||||
cursor: pointer;
|
||||
opacity: 0.7;
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
|
||||
.overlay-close:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.overlay-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 12px;
|
||||
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.overlay-roll {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 8px;
|
||||
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.roll-breakdown {
|
||||
font-size: 14px;
|
||||
opacity: 0.9;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.overlay-result {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 10px;
|
||||
color: #ffeb3b;
|
||||
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.overlay-hint {
|
||||
font-size: 12px;
|
||||
opacity: 0.8;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
@keyframes slideInRight {
|
||||
from {
|
||||
transform: translateX(100%);
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
transform: translateX(0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Responsive design for smaller screens */
|
||||
@media (max-width: 768px) {
|
||||
.roll-overlay {
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
left: 10px;
|
||||
}
|
||||
|
||||
.roll-overlay-content {
|
||||
min-width: unset;
|
||||
}
|
||||
}
|
||||
@@ -65,8 +65,8 @@ export default {
|
||||
ko: 50, // Konstitution
|
||||
in: 50, // Intelligenz
|
||||
zt: 50, // Zaubertalent
|
||||
au: 50, // Ausstrahlung
|
||||
pa: 50, // Psi-Kraft
|
||||
au: 50, // Ausehen
|
||||
pa: 50, // Persönliche Ausstrahlung
|
||||
wk: 50, // Willenskraft
|
||||
},
|
||||
attributes: [
|
||||
@@ -102,13 +102,13 @@ export default {
|
||||
},
|
||||
{
|
||||
key: 'au',
|
||||
name: 'Ausstrahlung',
|
||||
description: 'Charisma and leadership'
|
||||
name: 'Aussehen',
|
||||
description: 'Beautyness'
|
||||
},
|
||||
{
|
||||
key: 'pa',
|
||||
name: 'Psi-Kraft',
|
||||
description: 'Psychic abilities'
|
||||
name: 'Persönliche Ausstrahlung',
|
||||
description: 'Charisma and leadership'
|
||||
},
|
||||
{
|
||||
key: 'wk',
|
||||
|
||||
@@ -34,12 +34,43 @@
|
||||
</div>
|
||||
</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 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>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="stand">{{ $t('characters.basicInfo.socialClass') }} {{ $t('characters.basicInfo.required') }}</label>
|
||||
<div class="input-with-dice">
|
||||
<select id="stand" v-model="formData.stand" required>
|
||||
<option value="">{{ $t('characters.basicInfo.selectSocialClass') }}</option>
|
||||
<option value="Adel">{{ $t('characters.basicInfo.nobility') }}</option>
|
||||
<option value="Mittelschicht">{{ $t('characters.basicInfo.middleClass') }}</option>
|
||||
<option value="Volk">{{ $t('characters.basicInfo.commonFolk') }}</option>
|
||||
<option value="Unfrei">{{ $t('characters.basicInfo.unfree') }}</option>
|
||||
</select>
|
||||
<button
|
||||
type="button"
|
||||
class="dice-btn"
|
||||
@click="rollSocialClass"
|
||||
:disabled="!formData.typ"
|
||||
:title="formData.typ ? $t('characters.basicInfo.rollSocialClass') : $t('characters.basicInfo.selectClassFirst')"
|
||||
>
|
||||
🎲
|
||||
</button>
|
||||
</div>
|
||||
<div v-if="lastSocialClassRoll" class="roll-result">
|
||||
{{ $t('characters.basicInfo.rollResult') }}: {{ lastSocialClassRoll.roll }}
|
||||
<span v-if="lastSocialClassRoll.modifier !== 0">
|
||||
({{ lastSocialClassRoll.baseRoll }}{{ lastSocialClassRoll.modifier >= 0 ? '+' : '' }}{{ lastSocialClassRoll.modifier }})
|
||||
</span>
|
||||
→ {{ lastSocialClassRoll.result }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
@@ -75,6 +106,24 @@
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<!-- Roll Result Overlay -->
|
||||
<div v-if="showOverlay && lastSocialClassRoll" class="roll-overlay" @click="hideOverlay">
|
||||
<div class="roll-overlay-content">
|
||||
<button class="overlay-close" @click="hideOverlay">×</button>
|
||||
<div class="overlay-title">🎲 {{ $t('characters.basicInfo.rollResult') }}</div>
|
||||
<div class="overlay-roll">
|
||||
{{ lastSocialClassRoll.roll }}
|
||||
<span v-if="lastSocialClassRoll.modifier !== 0" class="roll-breakdown">
|
||||
({{ lastSocialClassRoll.baseRoll }}{{ lastSocialClassRoll.modifier >= 0 ? '+' : '' }}{{ lastSocialClassRoll.modifier }})
|
||||
</span>
|
||||
</div>
|
||||
<div class="overlay-result">
|
||||
→ {{ $t('characters.basicInfo.' + lastSocialClassRoll.result.toLowerCase()) || lastSocialClassRoll.result }}
|
||||
</div>
|
||||
<div class="overlay-hint">{{ $t('characters.basicInfo.clickToClose') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -97,6 +146,7 @@ export default {
|
||||
rasse: '',
|
||||
typ: '',
|
||||
herkunft: '',
|
||||
stand: '',
|
||||
glaube: '',
|
||||
},
|
||||
races: [],
|
||||
@@ -105,6 +155,9 @@ export default {
|
||||
beliefSearch: '',
|
||||
beliefResults: [],
|
||||
searchTimeout: null,
|
||||
lastSocialClassRoll: null,
|
||||
showOverlay: false,
|
||||
overlayTimeout: null,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -112,7 +165,14 @@ export default {
|
||||
return this.formData.name.length >= 2 &&
|
||||
this.formData.rasse &&
|
||||
this.formData.typ &&
|
||||
this.formData.herkunft
|
||||
this.formData.herkunft &&
|
||||
this.formData.stand
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'formData.typ'() {
|
||||
// Clear social class roll result when character class changes
|
||||
this.lastSocialClassRoll = null
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
@@ -122,6 +182,7 @@ export default {
|
||||
rasse: this.sessionData.rasse || '',
|
||||
typ: this.sessionData.typ || '',
|
||||
herkunft: this.sessionData.herkunft || '',
|
||||
stand: this.sessionData.stand || '',
|
||||
glaube: this.sessionData.glaube || '',
|
||||
}
|
||||
|
||||
@@ -131,6 +192,15 @@ export default {
|
||||
|
||||
await this.loadReferenceData()
|
||||
},
|
||||
beforeUnmount() {
|
||||
// Clean up timeouts
|
||||
if (this.searchTimeout) {
|
||||
clearTimeout(this.searchTimeout)
|
||||
}
|
||||
if (this.overlayTimeout) {
|
||||
clearTimeout(this.overlayTimeout)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async loadReferenceData() {
|
||||
try {
|
||||
@@ -187,6 +257,86 @@ export default {
|
||||
this.beliefResults = []
|
||||
},
|
||||
|
||||
rollSocialClass() {
|
||||
if (!this.formData.typ) {
|
||||
return
|
||||
}
|
||||
|
||||
// Base 1d100 roll
|
||||
const baseRoll = this.$rollNotation('1d100')
|
||||
let modifier = 0
|
||||
|
||||
// Apply class modifiers
|
||||
switch (this.formData.typ) {
|
||||
case 'Barde':
|
||||
case 'Priester':
|
||||
modifier = 20
|
||||
break
|
||||
case 'Druide':
|
||||
case 'Magier':
|
||||
modifier = 10
|
||||
break
|
||||
case 'Assassine':
|
||||
case 'Händler':
|
||||
case 'Waldläufer':
|
||||
modifier = -10
|
||||
break
|
||||
case 'Spitzbube':
|
||||
modifier = -20
|
||||
break
|
||||
}
|
||||
|
||||
const finalRoll = baseRoll.sum + modifier
|
||||
|
||||
// Determine social class based on final roll
|
||||
let socialClass = ''
|
||||
if (finalRoll <= 10) {
|
||||
socialClass = 'Unfrei'
|
||||
} else if (finalRoll <= 50) {
|
||||
socialClass = 'Volk'
|
||||
} else if (finalRoll <= 90) {
|
||||
socialClass = 'Mittelschicht'
|
||||
} else {
|
||||
socialClass = 'Adel'
|
||||
}
|
||||
|
||||
// Set the form data
|
||||
this.formData.stand = socialClass
|
||||
|
||||
// Store roll information for display
|
||||
this.lastSocialClassRoll = {
|
||||
baseRoll: baseRoll.sum,
|
||||
modifier: modifier,
|
||||
roll: finalRoll,
|
||||
result: socialClass
|
||||
}
|
||||
|
||||
// Show overlay notification
|
||||
this.showRollOverlay()
|
||||
},
|
||||
|
||||
showRollOverlay() {
|
||||
this.showOverlay = true
|
||||
|
||||
// Clear existing timeout if any
|
||||
if (this.overlayTimeout) {
|
||||
clearTimeout(this.overlayTimeout)
|
||||
}
|
||||
|
||||
// Hide overlay after 20 seconds
|
||||
this.overlayTimeout = setTimeout(() => {
|
||||
this.showOverlay = false
|
||||
}, 20000)
|
||||
},
|
||||
|
||||
hideOverlay() {
|
||||
this.showOverlay = false
|
||||
if (this.overlayTimeout) {
|
||||
clearTimeout(this.overlayTimeout)
|
||||
this.overlayTimeout = null
|
||||
}
|
||||
},
|
||||
|
||||
handleSubmit() {
|
||||
if (this.isValid) {
|
||||
this.$emit('next', this.formData)
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<template>
|
||||
<div class="derived-values-form">
|
||||
<h2>Derived Values</h2>
|
||||
<p class="instruction">These values are calculated from your attributes. You can adjust them as needed.</p>
|
||||
<h2>{{ $t('characters.derivedValues.title') }}</h2>
|
||||
<p class="instruction">{{ $t('characters.derivedValues.instruction') }}</p>
|
||||
|
||||
<form @submit.prevent="handleSubmit">
|
||||
<div class="values-grid">
|
||||
<div class="value-group" v-for="value in derivedValues" :key="value.key">
|
||||
<label :for="value.key">{{ value.name }}</label>
|
||||
<label :for="value.key">{{ $t(value.name) }}</label>
|
||||
<div class="value-input-group">
|
||||
<input
|
||||
:id="value.key"
|
||||
@@ -17,40 +17,40 @@
|
||||
required
|
||||
/>
|
||||
<div class="value-info">
|
||||
<span class="calculated-value">Calculated: {{ calculatedValues[value.key] }}</span>
|
||||
<span class="value-description">{{ value.description }}</span>
|
||||
<span class="calculated-value">{{ $t('characters.derivedValues.calculated') }}: {{ calculatedValues[value.key] }}</span>
|
||||
<span class="value-description">{{ $t(value.description) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="calculation-info">
|
||||
<h3>Calculation Rules</h3>
|
||||
<h3>{{ $t('characters.derivedValues.calculationRules') }}</h3>
|
||||
<div class="calculation-rules">
|
||||
<div class="rule">
|
||||
<strong>LP (Life Points):</strong> Base formula: (KO + ST) / 2 + modifier
|
||||
<strong>{{ $t('characters.derivedValues.lpFormula') }}:</strong> {{ $t('characters.derivedValues.lpDescription') }}
|
||||
</div>
|
||||
<div class="rule">
|
||||
<strong>AP (Adventure Points):</strong> Base formula: (AU + WK) / 2 + modifier
|
||||
<strong>{{ $t('characters.derivedValues.apFormula') }}:</strong> {{ $t('characters.derivedValues.apDescription') }}
|
||||
</div>
|
||||
<div class="rule">
|
||||
<strong>B (Burden):</strong> Base formula: ST + modifier
|
||||
<strong>{{ $t('characters.derivedValues.bFormula') }}:</strong> {{ $t('characters.derivedValues.bDescription') }}
|
||||
</div>
|
||||
<div class="rule">
|
||||
<strong>Bennies:</strong> Base values based on character class
|
||||
<strong>{{ $t('characters.derivedValues.benniesFormula') }}:</strong> {{ $t('characters.derivedValues.benniesDescription') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
<button type="button" @click="handlePrevious" class="prev-btn">
|
||||
← Previous: Attributes
|
||||
← {{ $t('characters.derivedValues.previousAttributes') }}
|
||||
</button>
|
||||
<button type="button" @click="recalculate" class="calc-btn">
|
||||
Recalculate from Attributes
|
||||
{{ $t('characters.derivedValues.recalculate') }}
|
||||
</button>
|
||||
<button type="submit" class="next-btn" :disabled="!isValid">
|
||||
Next: Skills & Spells →
|
||||
{{ $t('characters.derivedValues.nextSkills') }} →
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
@@ -80,43 +80,43 @@ export default {
|
||||
derivedValues: [
|
||||
{
|
||||
key: 'lp_max',
|
||||
name: 'Life Points (LP) Maximum',
|
||||
description: 'Maximum life/health points',
|
||||
name: 'characters.derivedValues.lpMax',
|
||||
description: 'characters.derivedValues.lpMaxDescription',
|
||||
min: 1,
|
||||
max: 200
|
||||
},
|
||||
{
|
||||
key: 'ap_max',
|
||||
name: 'Adventure Points (AP) Maximum',
|
||||
description: 'Maximum adventure points for special actions',
|
||||
name: 'characters.derivedValues.apMax',
|
||||
description: 'characters.derivedValues.apMaxDescription',
|
||||
min: 1,
|
||||
max: 200
|
||||
},
|
||||
{
|
||||
key: 'b_max',
|
||||
name: 'Burden (B) Maximum',
|
||||
description: 'Maximum carrying capacity',
|
||||
name: 'characters.derivedValues.bMax',
|
||||
description: 'characters.derivedValues.bMaxDescription',
|
||||
min: 1,
|
||||
max: 500
|
||||
},
|
||||
{
|
||||
key: 'sg',
|
||||
name: 'Schicksalsgunst (SG)',
|
||||
description: 'Fate points for rerolls',
|
||||
name: 'characters.derivedValues.sg',
|
||||
description: 'characters.derivedValues.sgDescription',
|
||||
min: 0,
|
||||
max: 10
|
||||
},
|
||||
{
|
||||
key: 'gg',
|
||||
name: 'Göttliche Gnade (GG)',
|
||||
description: 'Divine grace points',
|
||||
name: 'characters.derivedValues.gg',
|
||||
description: 'characters.derivedValues.ggDescription',
|
||||
min: 0,
|
||||
max: 10
|
||||
},
|
||||
{
|
||||
key: 'gp',
|
||||
name: 'Glückspunkte (GP)',
|
||||
description: 'Luck points',
|
||||
name: 'characters.derivedValues.gp',
|
||||
description: 'characters.derivedValues.gpDescription',
|
||||
min: 0,
|
||||
max: 10
|
||||
},
|
||||
|
||||
+44
-1
@@ -227,11 +227,54 @@ export default {
|
||||
selectClass: 'Klasse auswählen',
|
||||
origin: 'Herkunft',
|
||||
selectOrigin: 'Herkunft auswählen',
|
||||
socialClass: 'Gesellschaftlicher Stand',
|
||||
selectSocialClass: 'Stand auswählen',
|
||||
nobility: 'Adel',
|
||||
middleClass: 'Mittelschicht',
|
||||
commonFolk: 'Volk',
|
||||
unfree: 'Unfrei',
|
||||
religion: 'Religion/Glaube',
|
||||
religionPlaceholder: 'Mindestens 2 Zeichen für die Suche eingeben...',
|
||||
selected: 'Ausgewählt',
|
||||
nextAttributes: 'Weiter: Attribute →',
|
||||
required: '*'
|
||||
required: '*',
|
||||
rollResult: 'Würfelergebnis',
|
||||
rollSocialClass: 'Würfle für gesellschaftlichen Stand',
|
||||
selectClassFirst: 'Zuerst Charakterklasse auswählen',
|
||||
clickToClose: 'Klicken zum Schließen',
|
||||
adel: 'Adel',
|
||||
mittelschicht: 'Mittelschicht',
|
||||
volk: 'Volk',
|
||||
unfrei: 'Unfrei'
|
||||
},
|
||||
derivedValues: {
|
||||
title: 'Abgeleitete Werte',
|
||||
instruction: 'Diese Werte werden aus Ihren Attributen berechnet. Sie können sie nach Bedarf anpassen.',
|
||||
lpMax: 'Lebenspunkte (LP) Maximum',
|
||||
lpMaxDescription: 'Maximale Lebens-/Gesundheitspunkte',
|
||||
apMax: 'Abenteuerpunkte (AP) Maximum',
|
||||
apMaxDescription: 'Maximale Abenteuerpunkte für spezielle Aktionen',
|
||||
bMax: 'Belastung (B) Maximum',
|
||||
bMaxDescription: 'Maximale Tragekapazität',
|
||||
sg: 'Schicksalsgunst (SG)',
|
||||
sgDescription: 'Schicksalspunkte für Wiederholungswürfe',
|
||||
gg: 'Göttliche Gnade (GG)',
|
||||
ggDescription: 'Göttliche Gnadenpunkte',
|
||||
gp: 'Glückspunkte (GP)',
|
||||
gpDescription: 'Glückspunkte',
|
||||
calculated: 'Berechnet',
|
||||
calculationRules: 'Berechnungsregeln',
|
||||
lpFormula: 'LP (Lebenspunkte)',
|
||||
lpDescription: 'Grundformel: (KO + ST) / 2 + Modifikator',
|
||||
apFormula: 'AP (Ausdauerpunkte)',
|
||||
apDescription: 'Grundformel: (AU + WK) / 2 + Modifikator',
|
||||
bFormula: 'B (Bewegungsweite)',
|
||||
bDescription: 'Grundformel: ST + Modifikator',
|
||||
benniesFormula: 'Bennies',
|
||||
benniesDescription: 'Grundwerte basierend auf Charakterklasse',
|
||||
previousAttributes: 'Zurück: Attribute',
|
||||
recalculate: 'Aus Attributen neu berechnen',
|
||||
nextSkills: 'Weiter: Fertigkeiten & Zauber'
|
||||
}
|
||||
}
|
||||
}
|
||||
+44
-1
@@ -133,11 +133,54 @@ export default {
|
||||
selectClass: 'Select Class',
|
||||
origin: 'Origin',
|
||||
selectOrigin: 'Select Origin',
|
||||
socialClass: 'Social Class',
|
||||
selectSocialClass: 'Select Social Class',
|
||||
nobility: 'Nobility',
|
||||
middleClass: 'Middle Class',
|
||||
commonFolk: 'Common Folk',
|
||||
unfree: 'Unfree',
|
||||
religion: 'Religion/Belief',
|
||||
religionPlaceholder: 'Type at least 2 characters to search beliefs...',
|
||||
selected: 'Selected',
|
||||
nextAttributes: 'Next: Attributes →',
|
||||
required: '*'
|
||||
required: '*',
|
||||
rollResult: 'Roll Result',
|
||||
rollSocialClass: 'Roll for social class',
|
||||
selectClassFirst: 'Select character class first',
|
||||
clickToClose: 'Click to close',
|
||||
adel: 'Nobility',
|
||||
mittelschicht: 'Middle Class',
|
||||
volk: 'Common Folk',
|
||||
unfrei: 'Unfree'
|
||||
},
|
||||
derivedValues: {
|
||||
title: 'Derived Values',
|
||||
instruction: 'These values are calculated from your attributes. You can adjust them as needed.',
|
||||
lpMax: 'Life Points (LP) Maximum',
|
||||
lpMaxDescription: 'Maximum life/health points',
|
||||
apMax: 'Adventure Points (AP) Maximum',
|
||||
apMaxDescription: 'Maximum adventure points for special actions',
|
||||
bMax: 'Burden (B) Maximum',
|
||||
bMaxDescription: 'Maximum carrying capacity',
|
||||
sg: 'Fate\'s Favor (SG)',
|
||||
sgDescription: 'Fate points for rerolls',
|
||||
gg: 'Divine Grace (GG)',
|
||||
ggDescription: 'Divine grace points',
|
||||
gp: 'Luck Points (GP)',
|
||||
gpDescription: 'Luck points',
|
||||
calculated: 'Calculated',
|
||||
calculationRules: 'Calculation Rules',
|
||||
lpFormula: 'LP (Life Points)',
|
||||
lpDescription: 'Base formula: (KO + ST) / 2 + modifier',
|
||||
apFormula: 'AP (Adventure Points)',
|
||||
apDescription: 'Base formula: (AU + WK) / 2 + modifier',
|
||||
bFormula: 'B (Movement Range)',
|
||||
bDescription: 'Base formula: ST + modifier',
|
||||
benniesFormula: 'Bennies',
|
||||
benniesDescription: 'Base values based on character class',
|
||||
previousAttributes: 'Previous: Attributes',
|
||||
recalculate: 'Recalculate from Attributes',
|
||||
nextSkills: 'Next: Skills & Spells'
|
||||
}
|
||||
},
|
||||
common: {
|
||||
|
||||
@@ -5,6 +5,7 @@ import { createPinia } from 'pinia'
|
||||
import App from "./App.vue";
|
||||
import router from "./router";
|
||||
import { i18n } from './stores/languageStore'
|
||||
import UtilsPlugin from './utils/utilsPlugin'
|
||||
|
||||
|
||||
const app = createApp(App);
|
||||
@@ -12,4 +13,5 @@ const pinia = createPinia();
|
||||
app.use(pinia);
|
||||
app.use(router);
|
||||
app.use(i18n);
|
||||
app.use(UtilsPlugin);
|
||||
app.mount("#app");
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
/**
|
||||
* Utility-Funktionen für Zufallszahlen
|
||||
*/
|
||||
|
||||
/**
|
||||
* Erzeugt eine einzelne Zufallszahl im Bereich von 1 bis max
|
||||
* @param {number} max - Maximaler Wert (inklusive)
|
||||
* @returns {number} - Zufallszahl zwischen 1 und max
|
||||
*/
|
||||
export function rollDie(max = 6) {
|
||||
if (typeof max !== 'number' || max < 1) {
|
||||
throw new Error('Max value must be a positive number')
|
||||
}
|
||||
return Math.floor(Math.random() * max) + 1
|
||||
}
|
||||
|
||||
/**
|
||||
* Erzeugt mehrere Zufallszahlen im Bereich von 1 bis max
|
||||
* @param {number} count - Anzahl der zu erzeugenden Zufallszahlen
|
||||
* @param {number} max - Maximaler Wert (inklusive, default: 6)
|
||||
* @returns {Array<number>} - Array mit Zufallszahlen
|
||||
*/
|
||||
export function rollDice(count = 1, max = 6) {
|
||||
if (typeof count !== 'number' || count < 1) {
|
||||
throw new Error('Count must be a positive number')
|
||||
}
|
||||
if (typeof max !== 'number' || max < 1) {
|
||||
throw new Error('Max value must be a positive number')
|
||||
}
|
||||
|
||||
const results = []
|
||||
for (let i = 0; i < count; i++) {
|
||||
results.push(rollDie(max))
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
/**
|
||||
* Erzeugt Zufallszahlen und gibt sowohl die einzelnen Ergebnisse als auch die Summe zurück
|
||||
* @param {number} count - Anzahl der Würfel
|
||||
* @param {number} max - Maximaler Wert pro Würfel (default: 6)
|
||||
* @returns {Object} - Objekt mit rolls (Array) und sum (Summe)
|
||||
*/
|
||||
export function rollDiceWithSum(count = 1, max = 6) {
|
||||
const rolls = rollDice(count, max)
|
||||
const sum = rolls.reduce((total, roll) => total + roll, 0)
|
||||
|
||||
return {
|
||||
rolls,
|
||||
sum,
|
||||
count,
|
||||
max
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Simuliert RPG-Würfelnotation (z.B. "3d6", "1d20", "2d10+5", "max(2d20)")
|
||||
* @param {string} notation - Würfelnotation (z.B. "3d6", "1d20+5", "2d8-1", "max(2d20)", "min(3d6)")
|
||||
* @returns {Object} - Objekt mit rolls, sum, modifier und der ursprünglichen notation
|
||||
*/
|
||||
export function rollNotation(notation = '1d6') {
|
||||
if (typeof notation !== 'string') {
|
||||
throw new Error('Notation must be a string')
|
||||
}
|
||||
|
||||
// Entferne Leerzeichen
|
||||
const cleanNotation = notation.replace(/\s+/g, '')
|
||||
|
||||
// Check für max/min Funktionen (z.B. "max(2d20)", "min(3d6)")
|
||||
const functionMatch = cleanNotation.match(/^(max|min)\((\d+)d(\d+)([+-]\d+)?\)$/i)
|
||||
if (functionMatch) {
|
||||
const func = functionMatch[1].toLowerCase()
|
||||
const count = parseInt(functionMatch[2])
|
||||
const sides = parseInt(functionMatch[3])
|
||||
const modifier = functionMatch[4] ? parseInt(functionMatch[4]) : 0
|
||||
|
||||
const rolls = rollDice(count, sides)
|
||||
let selectedValue
|
||||
|
||||
if (func === 'max') {
|
||||
selectedValue = Math.max(...rolls)
|
||||
} else if (func === 'min') {
|
||||
selectedValue = Math.min(...rolls)
|
||||
}
|
||||
|
||||
const finalSum = selectedValue + modifier
|
||||
|
||||
return {
|
||||
notation,
|
||||
rolls,
|
||||
selectedValue,
|
||||
selectedFunction: func,
|
||||
baseSum: selectedValue,
|
||||
modifier,
|
||||
sum: finalSum,
|
||||
count,
|
||||
sides
|
||||
}
|
||||
}
|
||||
|
||||
// Standard-Notation (z.B. "3d6+2" oder "1d20-1")
|
||||
const standardMatch = cleanNotation.match(/^(\d+)d(\d+)([+-]\d+)?$/i)
|
||||
if (!standardMatch) {
|
||||
throw new Error('Invalid dice notation. Use format like "3d6", "1d20+5", "2d8-2", "max(2d20)", or "min(3d6)"')
|
||||
}
|
||||
|
||||
const count = parseInt(standardMatch[1])
|
||||
const sides = parseInt(standardMatch[2])
|
||||
const modifier = standardMatch[3] ? parseInt(standardMatch[3]) : 0
|
||||
|
||||
const rolls = rollDice(count, sides)
|
||||
const baseSum = rolls.reduce((total, roll) => total + roll, 0)
|
||||
const finalSum = baseSum + modifier
|
||||
|
||||
return {
|
||||
notation,
|
||||
rolls,
|
||||
baseSum,
|
||||
modifier,
|
||||
sum: finalSum,
|
||||
count,
|
||||
sides
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Erzeugt eine Zufallszahl in einem bestimmten Bereich (min bis max, inklusive)
|
||||
* @param {number} min - Minimaler Wert (inklusive)
|
||||
* @param {number} max - Maximaler Wert (inklusive)
|
||||
* @returns {number} - Zufallszahl zwischen min und max
|
||||
*/
|
||||
export function randomBetween(min = 1, max = 6) {
|
||||
if (typeof min !== 'number' || typeof max !== 'number') {
|
||||
throw new Error('Min and max must be numbers')
|
||||
}
|
||||
if (min > max) {
|
||||
throw new Error('Min value cannot be greater than max value')
|
||||
}
|
||||
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min
|
||||
}
|
||||
|
||||
/**
|
||||
* Wählt ein zufälliges Element aus einem Array
|
||||
* @param {Array} array - Array mit Elementen zur Auswahl
|
||||
* @returns {*} - Zufällig gewähltes Element
|
||||
*/
|
||||
export function randomChoice(array) {
|
||||
if (!Array.isArray(array) || array.length === 0) {
|
||||
throw new Error('Array must be a non-empty array')
|
||||
}
|
||||
|
||||
const randomIndex = Math.floor(Math.random() * array.length)
|
||||
return array[randomIndex]
|
||||
}
|
||||
|
||||
/**
|
||||
* Mischt ein Array zufällig (Fisher-Yates Shuffle)
|
||||
* @param {Array} array - Array zum Mischen
|
||||
* @returns {Array} - Neues gemischtes Array (Original bleibt unverändert)
|
||||
*/
|
||||
export function shuffleArray(array) {
|
||||
if (!Array.isArray(array)) {
|
||||
throw new Error('Input must be an array')
|
||||
}
|
||||
|
||||
const shuffled = [...array]
|
||||
for (let i = shuffled.length - 1; i > 0; i--) {
|
||||
const j = Math.floor(Math.random() * (i + 1));
|
||||
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]
|
||||
}
|
||||
return shuffled
|
||||
}
|
||||
|
||||
@@ -8,26 +8,55 @@
|
||||
* Usage in components:
|
||||
* this.$formatDate(dateString)
|
||||
* this.$safeValue(value, 'fallback')
|
||||
* this.$rollDice(3, 6) // 3 Würfel mit 6 Seiten
|
||||
* this.$rollNotation('2d10+3') // RPG-Würfelnotation
|
||||
*/
|
||||
|
||||
import { formatDate, formatDateTime, formatRelativeDate, safeValue, capitalize } from './dateUtils'
|
||||
import {
|
||||
rollDie,
|
||||
rollDice,
|
||||
rollDiceWithSum,
|
||||
rollNotation,
|
||||
randomBetween,
|
||||
randomChoice,
|
||||
shuffleArray
|
||||
} from './randomUtils.js'
|
||||
|
||||
export default {
|
||||
install(app) {
|
||||
// Globale Properties für Vue 3
|
||||
// Globale Properties für Vue 3 - Date Utils
|
||||
app.config.globalProperties.$formatDate = formatDate
|
||||
app.config.globalProperties.$formatDateTime = formatDateTime
|
||||
app.config.globalProperties.$formatRelativeDate = formatRelativeDate
|
||||
app.config.globalProperties.$safeValue = safeValue
|
||||
app.config.globalProperties.$capitalize = capitalize
|
||||
|
||||
// Globale Properties für Vue 3 - Random Utils
|
||||
app.config.globalProperties.$rollDie = rollDie
|
||||
app.config.globalProperties.$rollDice = rollDice
|
||||
app.config.globalProperties.$rollDiceWithSum = rollDiceWithSum
|
||||
app.config.globalProperties.$rollNotation = rollNotation
|
||||
app.config.globalProperties.$randomBetween = randomBetween
|
||||
app.config.globalProperties.$randomChoice = randomChoice
|
||||
app.config.globalProperties.$shuffleArray = shuffleArray
|
||||
|
||||
// Provide/Inject für Composition API
|
||||
app.provide('utils', {
|
||||
// Date Utils
|
||||
formatDate,
|
||||
formatDateTime,
|
||||
formatRelativeDate,
|
||||
safeValue,
|
||||
capitalize
|
||||
capitalize,
|
||||
// Random Utils
|
||||
rollDie,
|
||||
rollDice,
|
||||
rollDiceWithSum,
|
||||
rollNotation,
|
||||
randomBetween,
|
||||
randomChoice,
|
||||
shuffleArray
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user