All checks were successful
Deploy to production / deploy (push) Successful in 2m53s
- Introduced FalukantCharacterVisual component to display character portraits and 3D figures based on user settings. - Updated DirectorInfo, ChurchView, PoliticsView, and other components to utilize the new character visual component. - Added visual settings management for users, allowing selection between portrait styles and display modes (portraits, 3D, or both). - Implemented age rules for engagement and marriage in the backend, ensuring compliance with specified age limits. - Created a new settings view for Falukant to manage visual preferences. - Added new images for medieval character portraits. - Updated translations for new settings and features in multiple languages.
190 lines
4.8 KiB
Vue
190 lines
4.8 KiB
Vue
<template>
|
|
<div v-if="shouldRender" class="falukant-character-visual" :class="{ 'is-compact': compact }">
|
|
<div
|
|
v-if="showPortrait"
|
|
class="falukant-character-visual__portrait"
|
|
:style="portraitStyle"
|
|
:aria-label="`${gender || 'male'} portrait`"
|
|
/>
|
|
<div v-if="showFigure" class="falukant-character-visual__figure" :style="figureStyle">
|
|
<Character3D
|
|
:gender="normalizedGender"
|
|
:age="normalizedAge"
|
|
:lightweight="lightweight"
|
|
:noBackground="noBackground"
|
|
:lazy="lazy"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import Character3D from '@/components/Character3D.vue';
|
|
import {
|
|
FALUKANT_VISUAL_DEFAULTS,
|
|
resolveFalukantPortraitAtlas,
|
|
showFalukant3dFigures,
|
|
showFalukantPortraits,
|
|
} from '@/utils/falukantVisualSettings.js';
|
|
|
|
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 {
|
|
name: 'FalukantCharacterVisual',
|
|
components: {
|
|
Character3D,
|
|
},
|
|
props: {
|
|
gender: {
|
|
type: String,
|
|
default: 'male',
|
|
},
|
|
age: {
|
|
type: Number,
|
|
default: null,
|
|
},
|
|
visualSettings: {
|
|
type: Object,
|
|
default: () => ({ ...FALUKANT_VISUAL_DEFAULTS }),
|
|
},
|
|
portraitScale: {
|
|
type: Number,
|
|
default: 0.42,
|
|
},
|
|
figureWidth: {
|
|
type: Number,
|
|
default: 92,
|
|
},
|
|
figureHeight: {
|
|
type: Number,
|
|
default: 118,
|
|
},
|
|
compact: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
lightweight: {
|
|
type: Boolean,
|
|
default: true,
|
|
},
|
|
noBackground: {
|
|
type: Boolean,
|
|
default: true,
|
|
},
|
|
lazy: {
|
|
type: Boolean,
|
|
default: true,
|
|
},
|
|
},
|
|
computed: {
|
|
normalizedGender() {
|
|
return String(this.gender || 'male').toLowerCase() === 'female' ? 'female' : 'male';
|
|
},
|
|
normalizedAge() {
|
|
return Number.isFinite(this.age) ? this.age : null;
|
|
},
|
|
showPortrait() {
|
|
return showFalukantPortraits(this.visualSettings?.figureMode);
|
|
},
|
|
showFigure() {
|
|
return showFalukant3dFigures(this.visualSettings?.figureMode);
|
|
},
|
|
shouldRender() {
|
|
return this.showPortrait || this.showFigure;
|
|
},
|
|
figureStyle() {
|
|
return {
|
|
width: `${this.figureWidth}px`,
|
|
height: `${this.figureHeight}px`,
|
|
};
|
|
},
|
|
portraitStyle() {
|
|
const genderData = AVATAR_POSITIONS[this.normalizedGender] || AVATAR_POSITIONS.male;
|
|
const position = genderData.positions?.[this.ageGroup] || { x: 0, y: 0 };
|
|
const safeScale = Number(this.portraitScale) > 0 ? Number(this.portraitScale) : 1;
|
|
return {
|
|
backgroundImage: `url(${resolveFalukantPortraitAtlas(this.normalizedGender, this.visualSettings?.portraitStyle)})`,
|
|
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`,
|
|
};
|
|
},
|
|
ageGroup() {
|
|
const age = this.normalizedAge;
|
|
if (age == null || 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+';
|
|
},
|
|
},
|
|
};
|
|
</script>
|
|
|
|
<style scoped>
|
|
.falukant-character-visual {
|
|
display: inline-flex;
|
|
align-items: flex-end;
|
|
gap: 0.45rem;
|
|
}
|
|
|
|
.falukant-character-visual__portrait {
|
|
background-repeat: no-repeat;
|
|
background-color: rgba(255, 250, 242, 0.9);
|
|
border: 1px solid rgba(116, 85, 49, 0.22);
|
|
border-radius: 0.7rem;
|
|
box-shadow: 0 8px 18px rgba(46, 28, 14, 0.08);
|
|
overflow: hidden;
|
|
}
|
|
|
|
.falukant-character-visual__figure {
|
|
flex: 0 0 auto;
|
|
}
|
|
|
|
.is-compact {
|
|
gap: 0.35rem;
|
|
}
|
|
</style>
|