Spiel erweitert
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<div class="houseView">
|
||||
<div class="house-view">
|
||||
<StatusBar />
|
||||
<h2>{{ $t('falukant.house.title') }}</h2>
|
||||
<div class="existingHouse">
|
||||
<div :style="houseStyle(picturePosition)" class="house"></div>
|
||||
<div class="statusreport">
|
||||
<div class="existing-house">
|
||||
<div :style="houseType ? houseStyle(houseType.position, 341) : {}" class="house"></div>
|
||||
<div class="status-panel">
|
||||
<h3>{{ $t('falukant.house.statusreport') }}</h3>
|
||||
<table>
|
||||
<thead>
|
||||
@@ -15,46 +15,54 @@
|
||||
</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 v-for="(value, key) in status" :key="key">
|
||||
<td>{{ $t(`falukant.house.status.${key}`) }}</td>
|
||||
<td>{{ value }}%</td>
|
||||
<td>
|
||||
<button v-if="value < 100" @click="renovate(key)">
|
||||
{{ $t('falukant.house.renovate') }} ({{ getRenovationCost(key, value) }})
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ $t('falukant.house.worth') }}</td>
|
||||
<td>{{ getWorth(status) }}</td>
|
||||
<td><button @click="sellHouse">{{ $t('falukant.house.sell') }}</button></td>
|
||||
<td>{{ getWorth() }} {{ currency }}</td>
|
||||
<td>
|
||||
<button @click="renovateAll" :disabled="allRenovated">
|
||||
{{ $t('falukant.house.renovateAll') }} ({{ getAllRenovationCost() }})
|
||||
</button>
|
||||
<button @click="sellHouse">
|
||||
{{ $t('falukant.house.sell') }}
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="buyablehouses">
|
||||
|
||||
<div class="buyable-houses">
|
||||
<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>
|
||||
<div class="houses-list">
|
||||
<div v-for="house in buyableHouses" :key="house.id" class="house-item">
|
||||
<div :style="house.houseType ? houseStyle(house.houseType.position, 114) : {}" class="house-preview"></div>
|
||||
<div class="house-info">
|
||||
<h4>{{ $t(`falukant.house.type.${house.houseType.labelTr}`) }}</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>
|
||||
<tr v-for="(val, prop) in house" :key="prop"
|
||||
v-if="['roofCondition','wallCondition','floorCondition','windowCondition'].includes(prop)">
|
||||
<td>{{ $t(`falukant.house.status.${prop}`) }}</td>
|
||||
<td>{{ val }}%</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div>
|
||||
{{ $t('falukant.house.price') }}: {{ buyCost(house) }}
|
||||
</div>
|
||||
<div>
|
||||
<button @click="buyHouse(house.id)">{{ $t('falukant.house.buy') }}</button>
|
||||
<div>
|
||||
{{ $t('falukant.house.price') }}: {{ buyCost(house) }}
|
||||
</div>
|
||||
<button @click="buyHouse(house.id)">
|
||||
{{ $t('falukant.house.buy') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -65,195 +73,209 @@
|
||||
<script>
|
||||
import StatusBar from '@/components/falukant/StatusBar.vue';
|
||||
import apiClient from '@/utils/axios.js';
|
||||
import { mapState } from "vuex";
|
||||
import { mapState } from 'vuex';
|
||||
|
||||
export default {
|
||||
name: 'HouseView',
|
||||
components: {
|
||||
StatusBar
|
||||
},
|
||||
components: { StatusBar },
|
||||
data() {
|
||||
return {
|
||||
houseTypes: [],
|
||||
userHouse: {},
|
||||
userHouse: null,
|
||||
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();
|
||||
}
|
||||
currency: '€'
|
||||
};
|
||||
},
|
||||
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;
|
||||
},
|
||||
allRenovated() {
|
||||
return Object.values(this.status).every(v => v >= 100);
|
||||
}
|
||||
},
|
||||
created() {
|
||||
methods: {
|
||||
async loadData() {
|
||||
try {
|
||||
const userRes = await apiClient.get('/api/falukant/houses');
|
||||
this.userHouse = userRes.data;
|
||||
this.houseType = this.userHouse.houseType;
|
||||
const { roofCondition, wallCondition, floorCondition, windowCondition } = this.userHouse;
|
||||
this.status = { roofCondition, wallCondition, floorCondition, windowCondition };
|
||||
|
||||
const buyRes = await apiClient.get('/api/falukant/houses/buyable');
|
||||
this.buyableHouses = buyRes.data;
|
||||
} catch (err) {
|
||||
console.error('Error loading house data', err);
|
||||
}
|
||||
},
|
||||
houseStyle(position, picSize) {
|
||||
const columns = 3;
|
||||
const size = picSize;
|
||||
const index = position - 1;
|
||||
const x = (index % columns) * size;
|
||||
const y = Math.floor(index / columns) * size;
|
||||
return {
|
||||
backgroundImage: 'url("/images/falukant/houses.png")',
|
||||
backgroundPosition: `-${x}px -${y}px`,
|
||||
backgroundSize: `${columns * size}px auto`
|
||||
};
|
||||
},
|
||||
formatPrice(value) {
|
||||
return new Intl.NumberFormat('de-DE', { minimumFractionDigits: 2, maximumFractionDigits: 2 }).format(value);
|
||||
},
|
||||
getRenovationCost(key, value) {
|
||||
const base = this.userHouse.houseType.cost || 0;
|
||||
const weights = { roofCondition: 0.25, wallCondition: 0.25, floorCondition: 0.25, windowCondition: 0.25 };
|
||||
const weight = weights[key] || 0;
|
||||
const missing = 100 - value;
|
||||
const cost = (missing / 100) * base * weight;
|
||||
return this.formatPrice(cost);
|
||||
},
|
||||
getAllRenovationCost() {
|
||||
const total = Object.keys(this.status).reduce((sum, k) => {
|
||||
const raw = parseFloat(this.getRenovationCost(k, this.status[k]).replace(/\./g, '').replace(',', '.'));
|
||||
return sum + (isNaN(raw) ? 0 : raw);
|
||||
}, 0);
|
||||
return this.formatPrice(total * 0.8);
|
||||
},
|
||||
getWorth() {
|
||||
const vals = Object.values(this.status);
|
||||
if (!vals.length) return this.formatPrice(0);
|
||||
const avg = vals.reduce((s, v) => s + v, 0) / vals.length;
|
||||
const price = this.houseType.cost || 0;
|
||||
return this.formatPrice(price * avg / 100 * 0.8);
|
||||
},
|
||||
buyCost(house) {
|
||||
const avg = (house.roofCondition + house.wallCondition + house.floorCondition + house.windowCondition) / 4;
|
||||
return this.formatPrice(house.houseType.cost * avg / 100);
|
||||
},
|
||||
async renovate(key) {
|
||||
try {
|
||||
await apiClient.post('/api/falukant/houses/renovate', { element: key });
|
||||
await this.loadData();
|
||||
} catch (err) {
|
||||
console.error('Error renovating', err);
|
||||
}
|
||||
},
|
||||
async renovateAll() {
|
||||
try {
|
||||
await apiClient.post('/api/falukant/houses/renovate-all');
|
||||
await this.loadData();
|
||||
} catch (err) {
|
||||
console.error('Error renovating all', err);
|
||||
}
|
||||
},
|
||||
async sellHouse() {
|
||||
try {
|
||||
await apiClient.post('/api/falukant/houses/sell');
|
||||
await this.loadData();
|
||||
} catch (err) {
|
||||
console.error('Error selling house', err);
|
||||
}
|
||||
},
|
||||
async buyHouse(id) {
|
||||
try {
|
||||
await apiClient.post('/api/falukant/houses', { houseId: id });
|
||||
await this.loadData();
|
||||
} catch (err) {
|
||||
console.error('Error buying house', err);
|
||||
}
|
||||
},
|
||||
handleDaemonMessage(evt) {
|
||||
try {
|
||||
const msg = JSON.parse(evt.data);
|
||||
if (msg.event === 'houseupdated') this.loadData();
|
||||
} catch {}
|
||||
}
|
||||
},
|
||||
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);
|
||||
}
|
||||
await this.loadData();
|
||||
if (this.socket) this.socket.on('falukantHouseUpdate', this.loadData);
|
||||
if (this.daemonSocket) this.daemonSocket.addEventListener('message', this.handleDaemonMessage);
|
||||
},
|
||||
beforeUnmount() {
|
||||
if (this.socket) {
|
||||
this.socket.off("falukantHouseUpdate", this.fetchStatus);
|
||||
}
|
||||
if (this.daemonSocket) {
|
||||
this.daemonSocket.removeEventListener("message", this.handleDaemonSocketMessage);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
if (this.socket) this.socket.off('falukantHouseUpdate', this.loadData);
|
||||
if (this.daemonSocket) this.daemonSocket.removeEventListener('message', this.handleDaemonMessage);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
|
||||
<style scoped>
|
||||
.house-view {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
padding-top: 20px;
|
||||
margin: 0 0 10px;
|
||||
}
|
||||
|
||||
.existingHouse {
|
||||
display: block;
|
||||
width: auto;
|
||||
height: 255px;
|
||||
}
|
||||
|
||||
Element {
|
||||
background-position: 71px 54px;
|
||||
.existing-house {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.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;
|
||||
background-repeat: no-repeat;
|
||||
image-rendering: crisp-edges;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.houseView {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
.status-panel {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.buyablehouses {
|
||||
.buyable-houses {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.houses-list {
|
||||
display: flex;
|
||||
flex-direction: column; /* vertical list */
|
||||
gap: 20px;
|
||||
max-height: 400px;
|
||||
overflow-y: auto; /* vertical scroll if needed */
|
||||
}
|
||||
|
||||
.house-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.house-preview {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background-repeat: no-repeat;
|
||||
image-rendering: crisp-edges;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
background-size: contain; /* scale image to container */
|
||||
background-position: center; /* center sprite */
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
table th,
|
||||
table td {
|
||||
border: 1px solid #ddd;
|
||||
padding: 8px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 6px 12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user