feat(FamilyView): enhance character display and avatar handling
All checks were successful
Deploy to production / deploy (push) Successful in 1m54s
All checks were successful
Deploy to production / deploy (push) Successful in 1m54s
- Refactored character display for spouses, children, and lovers to improve UI consistency and visual appeal. - Introduced a new method for calculating character avatar styles based on age and gender, enhancing the representation of characters. - Updated the layout to include compact character media for children and lovers, improving space utilization and readability.
This commit is contained in:
@@ -147,11 +147,17 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</details>
|
</details>
|
||||||
</div>
|
</div>
|
||||||
<div class="partner-character-3d">
|
<div class="character-media character-media--spouse">
|
||||||
<Character3D
|
<div
|
||||||
:gender="relationships[0].character2.gender"
|
class="character-avatar character-avatar--spouse"
|
||||||
:age="relationships[0].character2.age"
|
:style="getCharacterAvatarStyle(relationships[0].character2, 0.72)"
|
||||||
/>
|
></div>
|
||||||
|
<div class="character-3d-frame character-3d-frame--spouse">
|
||||||
|
<Character3D
|
||||||
|
:gender="relationships[0].character2.gender"
|
||||||
|
:age="relationships[0].character2.age"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="relationships[0].relationshipType === 'wooing'" class="gifts-section">
|
<div v-if="relationships[0].relationshipType === 'wooing'" class="gifts-section">
|
||||||
@@ -296,10 +302,26 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
<tr v-for="(child, index) in children" :key="index">
|
<tr v-for="(child, index) in children" :key="index">
|
||||||
<td v-if="child.hasName">
|
<td v-if="child.hasName">
|
||||||
{{ child.name }}
|
<div class="child-name-cell">
|
||||||
<span v-if="child.legitimacy && child.legitimacy !== 'legitimate'" class="child-origin-badge">
|
<div>
|
||||||
{{ $t('falukant.family.children.legitimacy.' + child.legitimacy) }}
|
{{ child.name }}
|
||||||
</span>
|
<span v-if="child.legitimacy && child.legitimacy !== 'legitimate'" class="child-origin-badge">
|
||||||
|
{{ $t('falukant.family.children.legitimacy.' + child.legitimacy) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="character-media character-media--compact child-character-media">
|
||||||
|
<div
|
||||||
|
class="character-avatar character-avatar--compact"
|
||||||
|
:style="getCharacterAvatarStyle(child, 0.5)"
|
||||||
|
></div>
|
||||||
|
<div class="character-3d-frame character-3d-frame--compact">
|
||||||
|
<Character3D
|
||||||
|
:gender="child.gender"
|
||||||
|
:age="child.age"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td v-else>
|
<td v-else>
|
||||||
<button @click="jumpToChurchForm">{{ $t('falukant.family.children.baptism')
|
<button @click="jumpToChurchForm">{{ $t('falukant.family.children.baptism')
|
||||||
@@ -330,12 +352,6 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<div class="child-character-3d" v-if="selectedChild">
|
|
||||||
<Character3D
|
|
||||||
:gender="selectedChild.gender"
|
|
||||||
:age="selectedChild.age"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<p>{{ $t('falukant.family.children.none') }}</p>
|
<p>{{ $t('falukant.family.children.none') }}</p>
|
||||||
@@ -367,6 +383,18 @@
|
|||||||
<div v-if="lovers && lovers.length > 0" class="lovers-grid">
|
<div v-if="lovers && lovers.length > 0" class="lovers-grid">
|
||||||
<article v-for="lover in lovers" :key="lover.relationshipId" class="lover-card surface-card">
|
<article v-for="lover in lovers" :key="lover.relationshipId" class="lover-card surface-card">
|
||||||
<div class="lover-card__header">
|
<div class="lover-card__header">
|
||||||
|
<div class="character-media character-media--compact lover-character-media">
|
||||||
|
<div
|
||||||
|
class="character-avatar character-avatar--compact"
|
||||||
|
:style="getCharacterAvatarStyle(lover, 0.5)"
|
||||||
|
></div>
|
||||||
|
<div class="character-3d-frame character-3d-frame--compact">
|
||||||
|
<Character3D
|
||||||
|
:gender="lover.gender"
|
||||||
|
:age="lover.age"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<strong>{{ $t('falukant.titles.' + lover.gender + '.' + lover.title) }} {{ lover.name }}</strong>
|
<strong>{{ $t('falukant.titles.' + lover.gender + '.' + lover.title) }} {{ lover.name }}</strong>
|
||||||
<div v-if="lover.age != null" class="lover-card__age">
|
<div v-if="lover.age != null" class="lover-card__age">
|
||||||
@@ -512,6 +540,42 @@ const MARRIAGE_GIFT_COSTS = {
|
|||||||
lavish: 180
|
lavish: 180
|
||||||
}
|
}
|
||||||
const LOVER_AFFECTION_ACTION_COST = 60
|
const LOVER_AFFECTION_ACTION_COST = 60
|
||||||
|
const AVATAR_POSITIONS = {
|
||||||
|
male: {
|
||||||
|
width: 195,
|
||||||
|
height: 300,
|
||||||
|
positions: {
|
||||||
|
'0-1': { x: 161, y: 28 },
|
||||||
|
'2-3': { x: 802, y: 28 },
|
||||||
|
'4-6': { x: 1014, y: 28 },
|
||||||
|
'7-10': { x: 800, y: 368 },
|
||||||
|
'11-13': { x: 373, y: 368 },
|
||||||
|
'14-16': { x: 1441, y: 28 },
|
||||||
|
'17-20': { x: 1441, y: 368 },
|
||||||
|
'21-30': { x: 1014, y: 368 },
|
||||||
|
'31-45': { x: 1227, y: 368 },
|
||||||
|
'45-55': { x: 803, y: 687 },
|
||||||
|
'55+': { x: 1441, y: 687 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
female: {
|
||||||
|
width: 223,
|
||||||
|
height: 298,
|
||||||
|
positions: {
|
||||||
|
'0-1': { x: 302, y: 66 },
|
||||||
|
'2-3': { x: 792, y: 66 },
|
||||||
|
'4-6': { x: 62, y: 66 },
|
||||||
|
'7-10': { x: 1034, y: 66 },
|
||||||
|
'11-13': { x: 1278, y: 66 },
|
||||||
|
'14-16': { x: 303, y: 392 },
|
||||||
|
'17-20': { x: 1525, y: 392 },
|
||||||
|
'21-30': { x: 1278, y: 392 },
|
||||||
|
'31-45': { x: 547, y: 718 },
|
||||||
|
'45-55': { x: 1034, y: 718 },
|
||||||
|
'55+': { x: 1525, y: 718 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'FamilyView',
|
name: 'FamilyView',
|
||||||
@@ -920,6 +984,36 @@ export default {
|
|||||||
return this.candidateRoles[characterId] || 'secret_affair';
|
return this.candidateRoles[characterId] || 'secret_affair';
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getAgeGroup(ageRaw) {
|
||||||
|
const age = Number(ageRaw || 0);
|
||||||
|
if (age <= 1) return '0-1';
|
||||||
|
if (age <= 3) return '2-3';
|
||||||
|
if (age <= 6) return '4-6';
|
||||||
|
if (age <= 10) return '7-10';
|
||||||
|
if (age <= 13) return '11-13';
|
||||||
|
if (age <= 16) return '14-16';
|
||||||
|
if (age <= 20) return '17-20';
|
||||||
|
if (age <= 30) return '21-30';
|
||||||
|
if (age <= 45) return '31-45';
|
||||||
|
if (age <= 55) return '45-55';
|
||||||
|
return '55+';
|
||||||
|
},
|
||||||
|
|
||||||
|
getCharacterAvatarStyle(character, scale = 1) {
|
||||||
|
const gender = String(character?.gender || 'male');
|
||||||
|
const ageGroup = this.getAgeGroup(character?.age);
|
||||||
|
const genderData = AVATAR_POSITIONS[gender] || AVATAR_POSITIONS.male;
|
||||||
|
const position = genderData.positions?.[ageGroup] || { x: 0, y: 0 };
|
||||||
|
const safeScale = Number(scale) > 0 ? Number(scale) : 1;
|
||||||
|
return {
|
||||||
|
backgroundImage: `url(/images/falukant/avatar/${gender}.png)`,
|
||||||
|
backgroundPosition: `-${Math.round(position.x * safeScale)}px -${Math.round(position.y * safeScale)}px`,
|
||||||
|
backgroundSize: `${Math.round(1792 * safeScale)}px ${Math.round(1024 * safeScale)}px`,
|
||||||
|
width: `${Math.round(genderData.width * safeScale)}px`,
|
||||||
|
height: `${Math.round(genderData.height * safeScale)}px`,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
formatCost(value) {
|
formatCost(value) {
|
||||||
return new Intl.NumberFormat(navigator.language, { minimumFractionDigits: 2, maximumFractionDigits: 2 }).format(value);
|
return new Intl.NumberFormat(navigator.language, { minimumFractionDigits: 2, maximumFractionDigits: 2 }).format(value);
|
||||||
},
|
},
|
||||||
@@ -1377,11 +1471,16 @@ export default {
|
|||||||
.lover-card__header {
|
.lover-card__header {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
justify-content: space-between;
|
justify-content: flex-start;
|
||||||
|
flex-wrap: wrap;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.lover-character-media {
|
||||||
|
margin-right: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
.lover-card__age {
|
.lover-card__age {
|
||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
color: rgba(0, 0, 0, 0.62);
|
color: rgba(0, 0, 0, 0.62);
|
||||||
@@ -1572,15 +1671,6 @@ export default {
|
|||||||
flex: 0 1 auto;
|
flex: 0 1 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.partner-character-3d {
|
|
||||||
width: 200px;
|
|
||||||
height: 280px;
|
|
||||||
border: 1px solid var(--color-border);
|
|
||||||
border-radius: var(--radius-lg);
|
|
||||||
background-color: #fdf1db;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.gifts-section {
|
.gifts-section {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
@@ -1596,15 +1686,60 @@ export default {
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.child-character-3d {
|
.character-media {
|
||||||
width: 200px;
|
display: flex;
|
||||||
height: 280px;
|
gap: 10px;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.character-media--spouse {
|
||||||
|
min-width: 260px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.character-media--compact {
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.character-avatar {
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-color: #fdf1db;
|
||||||
|
box-shadow: var(--shadow-soft);
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.character-avatar--compact {
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.character-3d-frame {
|
||||||
border: 1px solid var(--color-border);
|
border: 1px solid var(--color-border);
|
||||||
border-radius: var(--radius-lg);
|
border-radius: var(--radius-lg);
|
||||||
background-color: #fdf1db;
|
background-color: #fdf1db;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.character-3d-frame--spouse {
|
||||||
|
width: 150px;
|
||||||
|
height: 220px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.character-3d-frame--compact {
|
||||||
|
width: 95px;
|
||||||
|
height: 140px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.child-name-cell {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.child-character-media {
|
||||||
|
width: fit-content;
|
||||||
|
}
|
||||||
|
|
||||||
.spouse-section table,
|
.spouse-section table,
|
||||||
.children-section table {
|
.children-section table {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
|
|||||||
Reference in New Issue
Block a user