Implemented houses
This commit is contained in:
@@ -3,31 +3,50 @@
|
||||
<StatusBar />
|
||||
<div class="contentscroll">
|
||||
|
||||
<!-- Titel -->
|
||||
<h2>{{ $t('falukant.family.title') }}</h2>
|
||||
|
||||
<!-- Ehepartner -->
|
||||
<div class="spouse-section">
|
||||
<h3>{{ $t('falukant.family.spouse.title') }}</h3>
|
||||
<div v-if="relationships.length > 0">
|
||||
<table>
|
||||
<tr>
|
||||
<td>{{ $t('falukant.family.relationships.name') }}</td>
|
||||
<td>
|
||||
{{ $t('falukant.titles.' + relationships[0].character2.gender + '.' +
|
||||
relationships[0].character2.nobleTitle) }}
|
||||
{{ relationships[0].character2.firstName }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('falukant.family.spouse.age') }}</td>
|
||||
<td>{{ relationships[0].character2.age }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('falukant.family.spouse.status') }}</td>
|
||||
<td>{{ $t('falukant.family.statuses.' + relationships[0].relationshipType) }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="relationship">
|
||||
<table>
|
||||
<tr>
|
||||
<td>{{ $t('falukant.family.relationships.name') }}</td>
|
||||
<td>
|
||||
{{ $t('falukant.titles.' + relationships[0].character2.gender + '.' +
|
||||
relationships[0].character2.nobleTitle) }}
|
||||
{{ relationships[0].character2.firstName }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('falukant.family.spouse.age') }}</td>
|
||||
<td>{{ relationships[0].character2.age }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('falukant.family.spouse.mood') }}</td>
|
||||
<td>{{ $t(`falukant.mood.${relationships[0].character2.mood.tr}`) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('falukant.family.spouse.status') }}</td>
|
||||
<td>{{ $t('falukant.family.statuses.' + relationships[0].relationshipType) }}</td>
|
||||
</tr>
|
||||
<tr v-if="relationships[0].relationshipType === 'wooing'">
|
||||
<td>{{ $t('falukant.family.spouse.progress') }}</td>
|
||||
<td>
|
||||
<div class="progress">
|
||||
<div class="progress-inner" :style="{
|
||||
width: relationships[0].progress + '%',
|
||||
backgroundColor: progressColor(relationships[0].progress)
|
||||
}"></div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<ul>
|
||||
<li v-for="characteristic in relationships[0].character2.characterTrait"
|
||||
:key="characteristic.id">{{ $t(`falukant.character.${characteristic.tr}`) }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div v-if="relationships[0].relationshipType === 'wooing'">
|
||||
<h3>{{ $t('falukant.family.spouse.wooing.gifts') }}</h3>
|
||||
<table class="spouse-table">
|
||||
@@ -35,6 +54,7 @@
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>{{ $t('falukant.family.spouse.wooing.gift') }}</th>
|
||||
<th>{{ $t('falukant.family.spouse.wooing.effect') }}</th>
|
||||
<th>{{ $t('falukant.family.spouse.wooing.value') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -42,12 +62,14 @@
|
||||
<tr v-for="gift in gifts" :key="gift.id">
|
||||
<td><input type="radio" name="gift" :value="gift.id" v-model="selectedGiftId"></td>
|
||||
<td>{{ $t(`falukant.gifts.${gift.name}`) }}</td>
|
||||
<td>{{ $t(`falukant.family.spouse.giftAffect.${getEffect(gift)}`) }}</td>
|
||||
<td>{{ formatCost(gift.cost) }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div>
|
||||
<button @click="sendGift" class="button">{{ $t('falukant.family.spouse.wooing.sendGift') }}</button>
|
||||
<button @click="sendGift" class="button">{{ $t('falukant.family.spouse.wooing.sendGift')
|
||||
}}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -67,7 +89,7 @@
|
||||
v-model="selectedProposalId"></td>
|
||||
<td>{{
|
||||
$t(`falukant.titles.${proposal.proposedCharacterGender}.${proposal.proposedCharacterNobleTitle}`)
|
||||
}} {{ proposal.proposedCharacterName }}</td>
|
||||
}} {{ proposal.proposedCharacterName }}</td>
|
||||
<td>{{ proposal.proposedCharacterAge }}</td>
|
||||
<td>{{ formatCost(proposal.cost) }}</td>
|
||||
</tr>
|
||||
@@ -80,7 +102,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Kinder -->
|
||||
<div class="children-section">
|
||||
<h3>{{ $t('falukant.family.children.title') }}</h3>
|
||||
<div v-if="children && children.length > 0">
|
||||
@@ -131,9 +152,6 @@
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<!-- Dialog-Beispiele oder ähnliche Komponenten -->
|
||||
<MessageDialog ref="messageDialog" />
|
||||
<ErrorDialog ref="errorDialog" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -160,7 +178,9 @@ export default {
|
||||
proposals: [],
|
||||
selectedProposalId: null,
|
||||
gifts: [],
|
||||
selectedGiftId: null
|
||||
selectedGiftId: null,
|
||||
moodAffects: [],
|
||||
characterAffects: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -169,12 +189,13 @@ export default {
|
||||
async mounted() {
|
||||
await this.loadFamilyData();
|
||||
await this.loadGifts();
|
||||
await this.loadMoodAffects();
|
||||
await this.loadCharacterAffects();
|
||||
},
|
||||
methods: {
|
||||
async loadFamilyData() {
|
||||
try {
|
||||
const response = await apiClient.get('/api/falukant/family');
|
||||
console.log(response.data);
|
||||
this.relationships = response.data.relationships;
|
||||
this.children = response.data.children;
|
||||
this.lovers = response.data.lovers;
|
||||
@@ -193,6 +214,22 @@ export default {
|
||||
return new Intl.NumberFormat(navigator.language, { minimumFractionDigits: 2, maximumFractionDigits: 2 }).format(value);
|
||||
},
|
||||
|
||||
getEffect(gift) {
|
||||
const relationship = this.relationships[0];
|
||||
const partner = relationship.character2;
|
||||
const currentMoodId = partner.mood?.id ?? partner.mood_id;
|
||||
const moodEntry = gift.moodsAffects.find(ma => ma.mood_id === currentMoodId);
|
||||
const moodValue = moodEntry ? moodEntry.suitability : 0;
|
||||
let highestCharacterValue = 0;
|
||||
for (const trait of partner.characterTrait) {
|
||||
const charEntry = gift.charactersAffects.find(ca => ca.trait_id === trait.id);
|
||||
if (charEntry && charEntry.suitability > highestCharacterValue) {
|
||||
highestCharacterValue = charEntry.suitability;
|
||||
}
|
||||
}
|
||||
return Math.round((moodValue + highestCharacterValue) / 2);
|
||||
},
|
||||
|
||||
async acceptProposal() {
|
||||
const response = await apiClient.post('/api/falukant/family/acceptmarriageproposal'
|
||||
, { proposalId: this.selectedProposalId });
|
||||
@@ -200,19 +237,54 @@ export default {
|
||||
},
|
||||
|
||||
async loadGifts() {
|
||||
const response = await apiClient.get('/api/falukant/family/gifts');
|
||||
this.gifts = response.data;
|
||||
const response = await apiClient.get('/api/falukant/family/gifts');
|
||||
this.gifts = response.data;
|
||||
},
|
||||
|
||||
async sendGift() {
|
||||
if (!this.selectedGiftId) {
|
||||
alert('Please select a gift');
|
||||
this.$root.$refs.errorDialog.open(`tr:falukant.family.sendgift.error.nogiftselected`);
|
||||
return;
|
||||
}
|
||||
const response = await apiClient.post('/api/falukant/family/gift'
|
||||
, { giftId: this.selectedGiftId });
|
||||
this.loadFamilyData();
|
||||
}
|
||||
try {
|
||||
await apiClient.post('/api/falukant/family/gift'
|
||||
, { giftId: this.selectedGiftId });
|
||||
this.loadFamilyData();
|
||||
this.$root.$refs.messageDialog.open('tr:falukant.family.sendgift.success');
|
||||
} catch (error) {
|
||||
console.log(error.response);
|
||||
if (error.response.status === 412) {
|
||||
this.$root.$refs.errorDialog.open(`tr:falukant.family.sendgift.error.${error.response.data.error}`);
|
||||
} else {
|
||||
this.$root.$refs.errorDialog.open(`tr:falukant.family.sendgift.error.generic`);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
async loadMoodAffects() {
|
||||
try {
|
||||
const response = await apiClient.get('/api/falukant/mood/affect');
|
||||
this.moodAffects = response.data;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
},
|
||||
|
||||
async loadCharacterAffects() {
|
||||
try {
|
||||
const response = await apiClient.get('/api/falukant/character/affect');
|
||||
this.characterAffects = response.data;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
},
|
||||
|
||||
progressColor(p) {
|
||||
const pct = Math.max(0, Math.min(100, p)) / 100;
|
||||
const red = Math.round(255 * (1 - pct));
|
||||
const green = Math.round(255 * pct);
|
||||
return `rgb(${red}, ${green}, 0)`;
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -284,4 +356,28 @@ export default {
|
||||
h2 {
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
.relationship>table,
|
||||
.relationship>ul {
|
||||
display: inline-block;
|
||||
margin-right: 1em;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.relationship>ul {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.progress {
|
||||
width: 100%;
|
||||
background-color: #e5e7eb;
|
||||
border-radius: 0.25rem;
|
||||
overflow: hidden;
|
||||
height: 1rem;
|
||||
}
|
||||
|
||||
.progress-inner {
|
||||
height: 100%;
|
||||
transition: width 0.3s ease, background-color 0.3s ease;
|
||||
}
|
||||
</style>
|
||||
259
frontend/src/views/falukant/HouseView.vue
Normal file
259
frontend/src/views/falukant/HouseView.vue
Normal file
@@ -0,0 +1,259 @@
|
||||
<template>
|
||||
<div class="houseView">
|
||||
<StatusBar />
|
||||
<h2>{{ $t('falukant.house.title') }}</h2>
|
||||
<div class="existingHouse">
|
||||
<div :style="houseStyle(picturePosition)" class="house"></div>
|
||||
<div class="statusreport">
|
||||
<h3>{{ $t('falukant.house.statusreport') }}</h3>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ $t('falukant.house.element') }}</th>
|
||||
<th>{{ $t('falukant.house.state') }}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="status, index in status">
|
||||
<td>{{ $t(`falukant.house.status.${index}`) }}</td>
|
||||
<td>{{ status }} %</td>
|
||||
<td><button v-if="status < 100">{{ $t('falukant.house.renovate') }} ({{
|
||||
$t('falukant.house.cost') }}: {{ getRenovationCost(index, status) }}</button></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('falukant.house.worth') }}</td>
|
||||
<td>{{ getWorth(status) }}</td>
|
||||
<td><button @click="sellHouse">{{ $t('falukant.house.sell') }}</button></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="buyablehouses">
|
||||
<h3>{{ $t('falukant.house.buyablehouses') }}</h3>
|
||||
<div style="overflow:auto">
|
||||
<div style="display: flex; flex-direction: row" v-for="house in buyableHouses">
|
||||
<div style="width:100px; height:100px; display: hidden;">
|
||||
<div :style="houseStyle(house.houseType.position)" class="housePreview buyableHouseInfo"></div>
|
||||
</div>
|
||||
<div class="buyableHouseInfo">
|
||||
<h4 style="display: inline;">{{ $t('falukant.house.statusreport') }}</h4>
|
||||
<table>
|
||||
<tbody>
|
||||
<template v-for="value, key in house">
|
||||
<tr v-if="key != 'houseType' && key != 'id'">
|
||||
<td>{{ $t(`falukant.house.status.${key}`) }}</td>
|
||||
<td>{{ value }} %</td>
|
||||
</tr>
|
||||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div>
|
||||
{{ $t('falukant.house.price') }}: {{ buyCost(house) }}
|
||||
</div>
|
||||
<div>
|
||||
<button @click="buyHouse(house.id)">{{ $t('falukant.house.buy') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import StatusBar from '@/components/falukant/StatusBar.vue';
|
||||
import apiClient from '@/utils/axios.js';
|
||||
import { mapState } from "vuex";
|
||||
|
||||
export default {
|
||||
name: 'HouseView',
|
||||
components: {
|
||||
StatusBar
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
houseTypes: [],
|
||||
userHouse: {},
|
||||
houseType: {},
|
||||
status: {},
|
||||
buyableHouses: [],
|
||||
picturePosition: 0,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async loadHouseTypes() {
|
||||
try {
|
||||
const houseTypesResult = await apiClient.get('/api/falukant/houses/types');
|
||||
this.houseTypes = houseTypesResult.data;
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
},
|
||||
async loadUserHouse() {
|
||||
try {
|
||||
const userHouseResult = await apiClient.get('/api/falukant/houses');
|
||||
Object.assign(this.userHouse, userHouseResult.data);
|
||||
const { houseType, ...houseStatus } = this.userHouse;
|
||||
this.status = houseStatus;
|
||||
this.picturePosition = parseInt(houseType.position);
|
||||
this.houseType = houseType;
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Laden des Hauses:', error);
|
||||
this.userHouse = null;
|
||||
this.status = null;
|
||||
}
|
||||
},
|
||||
async loadBuyableHouses() {
|
||||
try {
|
||||
const buyableHousesResult = await apiClient.get('/api/falukant/houses/buyable');
|
||||
this.buyableHouses = buyableHousesResult.data;
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Laden der kaufbaren Häuser:', error);
|
||||
}
|
||||
},
|
||||
houseStyle(housePosition) {
|
||||
const columns = 3;
|
||||
const spriteSize = 341; // Breite & Höhe eines einzelnen Hauses
|
||||
let calculatePosition = Math.max(housePosition - 1, 0);
|
||||
const x = (calculatePosition % columns) * spriteSize;
|
||||
const y = Math.floor(calculatePosition / columns) * spriteSize;
|
||||
|
||||
return {
|
||||
backgroundImage: 'url("/images/falukant/houses.png")',
|
||||
backgroundPosition: `-${x}px -${y}px`,
|
||||
backgroundSize: `${columns * spriteSize}px auto`, // z.B. 1023px auto
|
||||
};
|
||||
},
|
||||
buyCost(house) {
|
||||
const houseQuality = (house.roofCondition + house.windowCondition + house.floorCondition + house.wallCondition) / 4;
|
||||
return (house.houseType.cost / 100 * houseQuality).toFixed(2);
|
||||
},
|
||||
getWorth() {
|
||||
const house = {...this.userHouse, houseType: this.houseType};
|
||||
const buyWorth = this.buyCost(house);
|
||||
return (buyWorth * 0.8).toFixed(2);
|
||||
},
|
||||
async buyHouse(houseId) {
|
||||
try {
|
||||
const response = await apiClient.post('/api/falukant/houses',
|
||||
{
|
||||
houseId: houseId,
|
||||
}
|
||||
);
|
||||
this.$router.push({ name: 'HouseView' });
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Kaufen des Hauses:', error);
|
||||
}
|
||||
},
|
||||
async getHouseData() {
|
||||
await this.loadUserHouse();
|
||||
await this.loadBuyableHouses();
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(['socket', 'daemonSocket']),
|
||||
getHouseStyle() {
|
||||
if (!this.userHouse || this.userHouse.position === undefined || this.userHouse.position === null) {
|
||||
return {};
|
||||
}
|
||||
return this.houseStyle(this.userHouse.position);
|
||||
},
|
||||
|
||||
getHouseType(position) {
|
||||
const houseType = this.houseTypes[position];
|
||||
return houseType;
|
||||
},
|
||||
getHouseStatus(position) {
|
||||
const houseStatus = this.houseStatuses[position];
|
||||
return houseStatus;
|
||||
},
|
||||
getRenovationCost(index, status) {
|
||||
const houseType = this.houseTypes[position];
|
||||
const renovationCost = houseType.renovationCosts[status];
|
||||
return renovationCost;
|
||||
},
|
||||
},
|
||||
created() {
|
||||
},
|
||||
async mounted() {
|
||||
this.loadHouseTypes();
|
||||
await this.getHouseData();
|
||||
if (this.socket) {
|
||||
this.socket.on("falukantHouseUpdate", this.getHouseData);
|
||||
}
|
||||
if (this.daemonSocket) {
|
||||
this.daemonSocket.addEventListener("message", this.handleDaemonSocketMessage);
|
||||
}
|
||||
},
|
||||
beforeUnmount() {
|
||||
if (this.socket) {
|
||||
this.socket.off("falukantHouseUpdate", this.fetchStatus);
|
||||
}
|
||||
if (this.daemonSocket) {
|
||||
this.daemonSocket.removeEventListener("message", this.handleDaemonSocketMessage);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
h2 {
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
.existingHouse {
|
||||
display: block;
|
||||
width: auto;
|
||||
height: 255px;
|
||||
}
|
||||
|
||||
Element {
|
||||
background-position: 71px 54px;
|
||||
}
|
||||
|
||||
.house {
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
background-repeat: no-repeat;
|
||||
image-rendering: crisp-edges;
|
||||
transform: scale(0.7);
|
||||
transform-origin: top left;
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
width: 341px;
|
||||
height: 341px;
|
||||
}
|
||||
|
||||
.statusreport {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
height: 250px;
|
||||
}
|
||||
|
||||
.buyableHouseInfo {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.housePreview {
|
||||
transform: scale(0.2);
|
||||
width: 341px;
|
||||
height: 341px;
|
||||
transform-origin: top left;
|
||||
}
|
||||
|
||||
.houseView {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.buyablehouses {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
@@ -91,6 +91,7 @@
|
||||
</div>
|
||||
<div class="imagecontainer">
|
||||
<div :style="getAvatarStyle" class="avatar"></div>
|
||||
<div :style="getHouseStyle" class="house"></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -168,6 +169,24 @@ export default {
|
||||
height: `${height}px`,
|
||||
};
|
||||
},
|
||||
getHouseStyle() {
|
||||
if (!this.falukantUser) return {};
|
||||
const imageUrl = '/images/falukant/houses.png';
|
||||
const housePosition = this.falukantUser.house ? this.falukantUser.house.type.position : 0;
|
||||
const x = housePosition % 3;
|
||||
const y = Math.floor(housePosition / 3);
|
||||
return {
|
||||
backgroundImage: `url(${imageUrl})`,
|
||||
backgroundPosition: `-${x * 341}px -${y * 341}px`,
|
||||
backgroundSize: "341px 341px",
|
||||
width: "114px",
|
||||
height: "114px",
|
||||
};
|
||||
},
|
||||
getAgeColor(age) {
|
||||
const ageGroup = this.getAgeGroup(age);
|
||||
return ageGroup === 'child' ? 'blue' : ageGroup === 'teen' ? 'green' : ageGroup === 'adult' ? 'red' : 'gray';
|
||||
},
|
||||
moneyValue() {
|
||||
const m = this.falukantUser?.money;
|
||||
return typeof m === 'string' ? parseFloat(m) : m;
|
||||
@@ -298,7 +317,16 @@ export default {
|
||||
image-rendering: crisp-edges;
|
||||
}
|
||||
|
||||
.house {
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
background-repeat: no-repeat;
|
||||
background-size: cover;
|
||||
image-rendering: crisp-edges;
|
||||
}
|
||||
|
||||
h2 {
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user