Implemented houses
This commit is contained in:
@@ -25,6 +25,13 @@ class FalukantController {
|
|||||||
this.acceptMarriageProposal = this.acceptMarriageProposal.bind(this);
|
this.acceptMarriageProposal = this.acceptMarriageProposal.bind(this);
|
||||||
this.getGifts = this.getGifts.bind(this);
|
this.getGifts = this.getGifts.bind(this);
|
||||||
this.sendGift = this.sendGift.bind(this);
|
this.sendGift = this.sendGift.bind(this);
|
||||||
|
this.getHouseTypes = this.getHouseTypes.bind(this);
|
||||||
|
this.getTitelsOfNobility = this.getTitelsOfNobility.bind(this);
|
||||||
|
this.getMoodAffect = this.getMoodAffect.bind(this);
|
||||||
|
this.getCharacterAffect = this.getCharacterAffect.bind(this);
|
||||||
|
this.getUserHouse = this.getUserHouse.bind(this);
|
||||||
|
this.getBuyableHouses = this.getBuyableHouses.bind(this);
|
||||||
|
this.buyUserHouse = this.buyUserHouse.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getUser(req, res) {
|
async getUser(req, res) {
|
||||||
@@ -368,15 +375,16 @@ class FalukantController {
|
|||||||
|
|
||||||
async sendGift(req, res) {
|
async sendGift(req, res) {
|
||||||
try {
|
try {
|
||||||
const { userid: hashedUserId } = req.headers;
|
const { userid: hashedUserId } = req.headers;
|
||||||
const { giftId} = req.body;
|
const { giftId } = req.body;
|
||||||
const result = await FalukantService.sendGift(hashedUserId, giftId);
|
const result = await FalukantService.sendGift(hashedUserId, giftId);
|
||||||
res.status(200).json(result);
|
res.status(200).json(result);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(500).json({ error: error.message });
|
const status = error.status === 412 ? 412 : 500;
|
||||||
console.log(error);
|
res.status(status).json({ error: error.message });
|
||||||
|
console.error(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async getTitelsOfNobility(req, res) {
|
async getTitelsOfNobility(req, res) {
|
||||||
try {
|
try {
|
||||||
@@ -399,6 +407,63 @@ class FalukantController {
|
|||||||
console.log(error);
|
console.log(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getMoodAffect(req, res) {
|
||||||
|
try {
|
||||||
|
const { userid: hashedUserId } = req.headers;
|
||||||
|
const result = await FalukantService.getMoodAffect(hashedUserId);
|
||||||
|
res.status(200).json(result);
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ error: error.message });
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getCharacterAffect(req, res) {
|
||||||
|
try {
|
||||||
|
const { userid: hashedUserId } = req.headers;
|
||||||
|
const result = await FalukantService.getCharacterAffect(hashedUserId);
|
||||||
|
res.status(200).json(result);
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ error: error.message });
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getUserHouse(req, res) {
|
||||||
|
try {
|
||||||
|
const { userid: hashedUserId } = req.headers;
|
||||||
|
const result = await FalukantService.getUserHouse(hashedUserId);
|
||||||
|
console.log(result);
|
||||||
|
res.status(200).json(result);
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ error: error.message });
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getBuyableHouses(req, res) {
|
||||||
|
try {
|
||||||
|
const { userid: hashedUserId } = req.headers;
|
||||||
|
const result = await FalukantService.getBuyableHouses(hashedUserId);
|
||||||
|
res.status(200).json(result);
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ error: error.message });
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async buyUserHouse(req, res) {
|
||||||
|
try {
|
||||||
|
const { userid: hashedUserId } = req.headers;
|
||||||
|
const { houseId } = req.body;
|
||||||
|
const result = await FalukantService.buyUserHouse(hashedUserId, houseId);
|
||||||
|
res.status(201).json(result);
|
||||||
|
} catch (error) {
|
||||||
|
res.status(500).json({ error: error.message });
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default FalukantController;
|
export default FalukantController;
|
||||||
|
|||||||
@@ -62,6 +62,9 @@ import PromotionalGiftMood from './falukant/predefine/promotional_gift_mood.js';
|
|||||||
import RelationshipType from './falukant/type/relationship.js';
|
import RelationshipType from './falukant/type/relationship.js';
|
||||||
import Relationship from './falukant/data/relationship.js';
|
import Relationship from './falukant/data/relationship.js';
|
||||||
import PromotionalGiftLog from './falukant/log/promotional_gift.js';
|
import PromotionalGiftLog from './falukant/log/promotional_gift.js';
|
||||||
|
import HouseType from './falukant/type/house.js';
|
||||||
|
import BuyableHouse from './falukant/data/buyable_house.js';
|
||||||
|
import UserHouse from './falukant/data/user_house.js';
|
||||||
|
|
||||||
export default function setupAssociations() {
|
export default function setupAssociations() {
|
||||||
// UserParam related associations
|
// UserParam related associations
|
||||||
@@ -352,4 +355,16 @@ export default function setupAssociations() {
|
|||||||
|
|
||||||
PromotionalGiftCharacterTrait.belongsTo(PromotionalGift, { foreignKey: 'gift_id', as: 'promotionalgiftcharactertrait' });
|
PromotionalGiftCharacterTrait.belongsTo(PromotionalGift, { foreignKey: 'gift_id', as: 'promotionalgiftcharactertrait' });
|
||||||
PromotionalGiftMood.belongsTo(PromotionalGift, { foreignKey: 'gift_id', as: 'promotionalgiftcharactermood' });
|
PromotionalGiftMood.belongsTo(PromotionalGift, { foreignKey: 'gift_id', as: 'promotionalgiftcharactermood' });
|
||||||
|
|
||||||
|
HouseType.hasMany(BuyableHouse, { foreignKey: 'houseTypeId', as: 'buyableHouses' });
|
||||||
|
BuyableHouse.belongsTo(HouseType, { foreignKey: 'houseTypeId', as: 'houseType' });
|
||||||
|
|
||||||
|
HouseType.hasMany(UserHouse, { foreignKey: 'houseTypeId', as: 'userHouses' });
|
||||||
|
UserHouse.belongsTo(HouseType, { foreignKey: 'houseTypeId', as: 'houseType' });
|
||||||
|
|
||||||
|
FalukantUser.hasOne(UserHouse, { foreignKey: 'userId', as: 'userHouse' });
|
||||||
|
UserHouse.belongsTo(FalukantUser, { foreignKey: 'userId', as: 'houseUser' });
|
||||||
|
|
||||||
|
TitleOfNobility.hasMany(HouseType, { foreignKey: 'minimumNobleTitle', as: 'houseTypes' });
|
||||||
|
HouseType.belongsTo(TitleOfNobility, { foreignKey: 'minimumNobleTitle', as: 'titleOfNobility' });
|
||||||
}
|
}
|
||||||
|
|||||||
40
backend/models/falukant/data/buyable_house.js
Normal file
40
backend/models/falukant/data/buyable_house.js
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import { Model, DataTypes } from 'sequelize';
|
||||||
|
import { sequelize } from '../../../utils/sequelize.js';
|
||||||
|
|
||||||
|
class BuyableHouse extends Model { }
|
||||||
|
|
||||||
|
BuyableHouse.init({
|
||||||
|
roofCondition: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: false,
|
||||||
|
defaultValue: 100
|
||||||
|
},
|
||||||
|
floorCondition: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: false,
|
||||||
|
defaultValue: 100
|
||||||
|
},
|
||||||
|
wallCondition: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: false,
|
||||||
|
defaultValue: 100
|
||||||
|
},
|
||||||
|
windowCondition: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: false,
|
||||||
|
defaultValue: 100
|
||||||
|
},
|
||||||
|
houseTypeId: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: false,
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
sequelize,
|
||||||
|
modelName: 'BuyableHouse',
|
||||||
|
tableName: 'buyable_house',
|
||||||
|
schema: 'falukant_data',
|
||||||
|
timestamps: false,
|
||||||
|
underscored: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default BuyableHouse;
|
||||||
46
backend/models/falukant/data/user_house.js
Normal file
46
backend/models/falukant/data/user_house.js
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import { Model, DataTypes } from 'sequelize';
|
||||||
|
import { sequelize } from '../../../utils/sequelize.js';
|
||||||
|
|
||||||
|
class UserHouse extends Model { }
|
||||||
|
|
||||||
|
UserHouse.init({
|
||||||
|
roofCondition: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: false,
|
||||||
|
defaultValue: 100
|
||||||
|
},
|
||||||
|
floorCondition: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: false,
|
||||||
|
defaultValue: 100
|
||||||
|
},
|
||||||
|
wallCondition: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: false,
|
||||||
|
defaultValue: 100
|
||||||
|
},
|
||||||
|
windowCondition: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: false,
|
||||||
|
defaultValue: 100
|
||||||
|
},
|
||||||
|
houseTypeId: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: false,
|
||||||
|
defaultValue: 1
|
||||||
|
},
|
||||||
|
userId: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: false,
|
||||||
|
defaultValue: 1
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
sequelize,
|
||||||
|
modelName: 'UserHouse',
|
||||||
|
tableName: 'user_house',
|
||||||
|
schema: 'falukant_data',
|
||||||
|
timestamps: false,
|
||||||
|
underscored: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default UserHouse;
|
||||||
38
backend/models/falukant/type/house.js
Normal file
38
backend/models/falukant/type/house.js
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import { Model, DataTypes } from 'sequelize';
|
||||||
|
import { sequelize } from '../../../utils/sequelize.js';
|
||||||
|
|
||||||
|
class HouseType extends Model { }
|
||||||
|
|
||||||
|
HouseType.init({
|
||||||
|
labelTr: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
cost: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
position: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
minimumNobleTitle: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
sequelize,
|
||||||
|
modelName: 'HouseType',
|
||||||
|
tableName: 'house',
|
||||||
|
schema: 'falukant_type',
|
||||||
|
timestamps: false,
|
||||||
|
underscored: true,
|
||||||
|
indexes: [
|
||||||
|
{
|
||||||
|
unique: true,
|
||||||
|
fields: ['label_tr']
|
||||||
|
}
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
export default HouseType;
|
||||||
@@ -66,6 +66,9 @@ import PromotionalGiftCharacterTrait from './falukant/predefine/promotional_gift
|
|||||||
import PromotionalGiftMood from './falukant/predefine/promotional_gift_mood.js';
|
import PromotionalGiftMood from './falukant/predefine/promotional_gift_mood.js';
|
||||||
import Relationship from './falukant/data/relationship.js';
|
import Relationship from './falukant/data/relationship.js';
|
||||||
import PromotionalGiftLog from './falukant/log/promotional_gift.js';
|
import PromotionalGiftLog from './falukant/log/promotional_gift.js';
|
||||||
|
import HouseType from './falukant/type/house.js';
|
||||||
|
import BuyableHouse from './falukant/data/buyable_house.js';
|
||||||
|
import UserHouse from './falukant/data/user_house.js';
|
||||||
|
|
||||||
const models = {
|
const models = {
|
||||||
SettingsType,
|
SettingsType,
|
||||||
@@ -136,6 +139,9 @@ const models = {
|
|||||||
PromotionalGiftCharacterTrait,
|
PromotionalGiftCharacterTrait,
|
||||||
PromotionalGiftMood,
|
PromotionalGiftMood,
|
||||||
PromotionalGiftLog,
|
PromotionalGiftLog,
|
||||||
|
HouseType,
|
||||||
|
BuyableHouse,
|
||||||
|
UserHouse,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default models;
|
export default models;
|
||||||
|
|||||||
@@ -36,5 +36,9 @@ router.post('/family/gift', falukantController.sendGift);
|
|||||||
router.get('/family', falukantController.getFamily);
|
router.get('/family', falukantController.getFamily);
|
||||||
router.get('/nobility/titels', falukantController.getTitelsOfNobility);
|
router.get('/nobility/titels', falukantController.getTitelsOfNobility);
|
||||||
router.get('/houses/types', falukantController.getHouseTypes);
|
router.get('/houses/types', falukantController.getHouseTypes);
|
||||||
|
router.get('/houses/buyable', falukantController.getBuyableHouses);
|
||||||
|
router.get('/mood/affect', falukantController.getMoodAffect);
|
||||||
|
router.get('/character/affect', falukantController.getCharacterAffect);
|
||||||
|
router.get('/houses', falukantController.getUserHouse);
|
||||||
|
router.post('/houses', falukantController.buyUserHouse);
|
||||||
export default router;
|
export default router;
|
||||||
|
|||||||
@@ -33,6 +33,10 @@ import PromotionalGiftCharacterTrait from '../models/falukant/predefine/promotio
|
|||||||
import PromotionalGiftMood from '../models/falukant/predefine/promotional_gift_mood.js';
|
import PromotionalGiftMood from '../models/falukant/predefine/promotional_gift_mood.js';
|
||||||
import PromotionalGiftLog from '../models/falukant/log/promotional_gift.js';
|
import PromotionalGiftLog from '../models/falukant/log/promotional_gift.js';
|
||||||
import CharacterTrait from '../models/falukant/type/character_trait.js';
|
import CharacterTrait from '../models/falukant/type/character_trait.js';
|
||||||
|
import Mood from '../models/falukant/type/mood.js';
|
||||||
|
import UserHouse from '../models/falukant/data/user_house.js';
|
||||||
|
import HouseType from '../models/falukant/type/house.js';
|
||||||
|
import BuyableHouse from '../models/falukant/data/buyable_house.js';
|
||||||
|
|
||||||
function calcAge(birthdate) {
|
function calcAge(birthdate) {
|
||||||
const b = new Date(birthdate); b.setHours(0, 0);
|
const b = new Date(birthdate); b.setHours(0, 0);
|
||||||
@@ -67,6 +71,14 @@ function calculateMarriageCost(titleOfNobility, age) {
|
|||||||
return baseCost * Math.pow(adjustedTitle, 1.3) - (age - 12) * 20;
|
return baseCost * Math.pow(adjustedTitle, 1.3) - (age - 12) * 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class PreconditionError extends Error {
|
||||||
|
constructor(label) {
|
||||||
|
super(label);
|
||||||
|
this.name = 'PreconditionError';
|
||||||
|
this.status = 412;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class FalukantService extends BaseService {
|
class FalukantService extends BaseService {
|
||||||
async getFalukantUserByHashedId(hashedId) {
|
async getFalukantUserByHashedId(hashedId) {
|
||||||
const user = await FalukantUser.findOne({
|
const user = await FalukantUser.findOne({
|
||||||
@@ -78,10 +90,22 @@ class FalukantService extends BaseService {
|
|||||||
include: [
|
include: [
|
||||||
{ model: FalukantPredefineFirstname, as: 'definedFirstName', attributes: ['name'] },
|
{ model: FalukantPredefineFirstname, as: 'definedFirstName', attributes: ['name'] },
|
||||||
{ model: FalukantPredefineLastname, as: 'definedLastName', attributes: ['name'] },
|
{ model: FalukantPredefineLastname, as: 'definedLastName', attributes: ['name'] },
|
||||||
{ model: TitleOfNobility, as: 'nobleTitle', attributes: ['labelTr'] },
|
{ model: TitleOfNobility, as: 'nobleTitle', attributes: ['labelTr', 'id'] },
|
||||||
{ model: CharacterTrait, as: 'traits', attributes: ['id', 'tr'] }
|
{ model: CharacterTrait, as: 'traits', attributes: ['id', 'tr'] }
|
||||||
],
|
],
|
||||||
attributes: ['id', 'birthdate', 'gender']
|
attributes: ['id', 'birthdate', 'gender', 'moodId']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: UserHouse,
|
||||||
|
as: 'userHouse',
|
||||||
|
attributes: ['roofCondition', 'wallCondition', 'floorCondition', 'windowCondition'],
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
model: HouseType,
|
||||||
|
as: 'houseType',
|
||||||
|
attributes: ['labelTr', 'position']
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
@@ -262,9 +286,7 @@ class FalukantService extends BaseService {
|
|||||||
|
|
||||||
async getProducts(hashedUserId) {
|
async getProducts(hashedUserId) {
|
||||||
const u = await getFalukantUserOrFail(hashedUserId);
|
const u = await getFalukantUserOrFail(hashedUserId);
|
||||||
console.log(u);
|
|
||||||
const c = await FalukantCharacter.findOne({ where: { userId: u.id } });
|
const c = await FalukantCharacter.findOne({ where: { userId: u.id } });
|
||||||
console.log(c);
|
|
||||||
if (!c) {
|
if (!c) {
|
||||||
throw new Error(`No FalukantCharacter found for user with id ${u.id}`);
|
throw new Error(`No FalukantCharacter found for user with id ${u.id}`);
|
||||||
}
|
}
|
||||||
@@ -348,9 +370,7 @@ class FalukantService extends BaseService {
|
|||||||
if (!inventory.length) {
|
if (!inventory.length) {
|
||||||
throw new Error('No inventory found');
|
throw new Error('No inventory found');
|
||||||
}
|
}
|
||||||
console.log(inventory);
|
|
||||||
const available = inventory.reduce((sum, i) => sum + i.quantity, 0);
|
const available = inventory.reduce((sum, i) => sum + i.quantity, 0);
|
||||||
console.log(available);
|
|
||||||
if (available < quantity) throw new Error('Not enough inventory available');
|
if (available < quantity) throw new Error('Not enough inventory available');
|
||||||
const item = inventory[0].productType;
|
const item = inventory[0].productType;
|
||||||
const knowledgeVal = item.knowledges?.[0]?.knowledge || 0;
|
const knowledgeVal = item.knowledges?.[0]?.knowledge || 0;
|
||||||
@@ -443,7 +463,7 @@ class FalukantService extends BaseService {
|
|||||||
const branch = await Branch.findOne({
|
const branch = await Branch.findOne({
|
||||||
where: { id: branchId },
|
where: { id: branchId },
|
||||||
})
|
})
|
||||||
;
|
;
|
||||||
const daySell = await DaySell.findOne({
|
const daySell = await DaySell.findOne({
|
||||||
where: {
|
where: {
|
||||||
regionId: branch.regionId,
|
regionId: branch.regionId,
|
||||||
@@ -549,23 +569,47 @@ class FalukantService extends BaseService {
|
|||||||
async buyStorage(hashedUserId, branchId, amount, stockTypeId) {
|
async buyStorage(hashedUserId, branchId, amount, stockTypeId) {
|
||||||
const user = await getFalukantUserOrFail(hashedUserId);
|
const user = await getFalukantUserOrFail(hashedUserId);
|
||||||
const branch = await getBranchOrFail(user.id, branchId);
|
const branch = await getBranchOrFail(user.id, branchId);
|
||||||
const buyable = await BuyableStock.findOne({
|
const buyableStocks = await BuyableStock.findAll({
|
||||||
where: { regionId: branch.regionId, stockTypeId },
|
where: { regionId: branch.regionId, stockTypeId },
|
||||||
include: [{ model: FalukantStockType, as: 'stockType' }]
|
include: [{ model: FalukantStockType, as: 'stockType' }]
|
||||||
});
|
});
|
||||||
if (!buyable || buyable.quantity < amount) throw new Error('Not enough buyable stock');
|
if (!buyableStocks || buyableStocks.length === 0) {
|
||||||
const costPerUnit = buyable.stockType.cost;
|
throw new Error('Not enough buyable stock');
|
||||||
|
}
|
||||||
|
const totalAvailable = buyableStocks.reduce((sum, entry) => sum + entry.quantity, 0);
|
||||||
|
if (totalAvailable < amount) {
|
||||||
|
throw new Error('Not enough buyable stock');
|
||||||
|
}
|
||||||
|
const costPerUnit = buyableStocks[0].stockType.cost;
|
||||||
const totalCost = costPerUnit * amount;
|
const totalCost = costPerUnit * amount;
|
||||||
if (user.money < totalCost) throw new Error('notenoughmoney');
|
if (user.money < totalCost) {
|
||||||
|
throw new Error('notenoughmoney');
|
||||||
|
}
|
||||||
const moneyResult = await updateFalukantUserMoney(
|
const moneyResult = await updateFalukantUserMoney(
|
||||||
user.id,
|
user.id,
|
||||||
-totalCost,
|
-totalCost,
|
||||||
`Buy storage (type: ${buyable.stockType.labelTr})`,
|
`Buy storage (type: ${buyableStocks[0].stockType.labelTr})`,
|
||||||
user.id
|
user.id
|
||||||
);
|
);
|
||||||
if (!moneyResult.success) throw new Error('Failed to update money');
|
if (!moneyResult.success) {
|
||||||
buyable.quantity -= amount;
|
throw new Error('Failed to update money');
|
||||||
await buyable.save();
|
}
|
||||||
|
let remainingToDeduct = amount;
|
||||||
|
for (const entry of buyableStocks) {
|
||||||
|
if (entry.quantity > remainingToDeduct) {
|
||||||
|
entry.quantity -= remainingToDeduct;
|
||||||
|
await entry.save();
|
||||||
|
remainingToDeduct = 0;
|
||||||
|
break;
|
||||||
|
} else if (entry.quantity === remainingToDeduct) {
|
||||||
|
await entry.destroy();
|
||||||
|
remainingToDeduct = 0;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
remainingToDeduct -= entry.quantity;
|
||||||
|
await entry.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
let stock = await FalukantStock.findOne({
|
let stock = await FalukantStock.findOne({
|
||||||
where: { branchId: branch.id, stockTypeId },
|
where: { branchId: branch.id, stockTypeId },
|
||||||
include: [{ model: FalukantStockType, as: 'stockType' }]
|
include: [{ model: FalukantStockType, as: 'stockType' }]
|
||||||
@@ -576,13 +620,19 @@ class FalukantService extends BaseService {
|
|||||||
stockTypeId,
|
stockTypeId,
|
||||||
quantity: amount,
|
quantity: amount,
|
||||||
});
|
});
|
||||||
return { success: true, bought: amount, totalCost, stockType: buyable.stockType.labelTr };
|
} else {
|
||||||
|
stock.quantity += amount;
|
||||||
|
await stock.save();
|
||||||
}
|
}
|
||||||
stock.quantity += amount;
|
|
||||||
await stock.save();
|
|
||||||
notifyUser(user.user.hashedId, 'falukantUpdateStatus', {});
|
notifyUser(user.user.hashedId, 'falukantUpdateStatus', {});
|
||||||
notifyUser(user.user.hashedId, 'falukantBranchUpdate', { branchId });
|
notifyUser(user.user.hashedId, 'falukantBranchUpdate', { branchId });
|
||||||
return { success: true, bought: amount, totalCost, stockType: buyable.stockType.labelTr };
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
bought: amount,
|
||||||
|
totalCost,
|
||||||
|
stockType: buyableStocks[0].stockType.labelTr
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async sellStorage(hashedUserId, branchId, amount, stockTypeId) {
|
async sellStorage(hashedUserId, branchId, amount, stockTypeId) {
|
||||||
@@ -822,13 +872,10 @@ class FalukantService extends BaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async convertProposalToDirector(hashedUserId, proposalId) {
|
async convertProposalToDirector(hashedUserId, proposalId) {
|
||||||
console.log('convert proposal to director - start');
|
|
||||||
const user = await getFalukantUserOrFail(hashedUserId);
|
const user = await getFalukantUserOrFail(hashedUserId);
|
||||||
console.log('convert proposal to director - check user');
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
throw new Error('User not found');
|
throw new Error('User not found');
|
||||||
}
|
}
|
||||||
console.log('convert proposal to director - find proposal', proposalId);
|
|
||||||
const proposal = await DirectorProposal.findOne(
|
const proposal = await DirectorProposal.findOne(
|
||||||
{
|
{
|
||||||
where: { id: proposalId },
|
where: { id: proposalId },
|
||||||
@@ -837,12 +884,9 @@ class FalukantService extends BaseService {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
console.log('convert proposal to director - check proposal');
|
|
||||||
if (!proposal || proposal.employerUserId !== user.id) {
|
if (!proposal || proposal.employerUserId !== user.id) {
|
||||||
throw new Error('Proposal does not belong to the user');
|
throw new Error('Proposal does not belong to the user');
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('convert proposal to director - check existing director', user, proposal);
|
|
||||||
const existingDirector = await Director.findOne({
|
const existingDirector = await Director.findOne({
|
||||||
where: {
|
where: {
|
||||||
employerUserId: user.id
|
employerUserId: user.id
|
||||||
@@ -860,7 +904,6 @@ class FalukantService extends BaseService {
|
|||||||
if (existingDirector) {
|
if (existingDirector) {
|
||||||
throw new Error('A director already exists for this region');
|
throw new Error('A director already exists for this region');
|
||||||
}
|
}
|
||||||
console.log('convert proposal to director - create new director');
|
|
||||||
const newDirector = await Director.create({
|
const newDirector = await Director.create({
|
||||||
directorCharacterId: proposal.directorCharacterId,
|
directorCharacterId: proposal.directorCharacterId,
|
||||||
employerUserId: proposal.employerUserId,
|
employerUserId: proposal.employerUserId,
|
||||||
@@ -880,13 +923,11 @@ class FalukantService extends BaseService {
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
console.log('convert proposal to director - remove propsals');
|
|
||||||
if (regionUserDirectorProposals.length > 0) {
|
if (regionUserDirectorProposals.length > 0) {
|
||||||
for (const proposal of regionUserDirectorProposals) {
|
for (const proposal of regionUserDirectorProposals) {
|
||||||
await DirectorProposal.destroy();
|
await DirectorProposal.destroy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
console.log('convert proposal to director - notify user');
|
|
||||||
notifyUser(hashedUserId, 'directorchanged');
|
notifyUser(hashedUserId, 'directorchanged');
|
||||||
return newDirector;
|
return newDirector;
|
||||||
}
|
}
|
||||||
@@ -1008,15 +1049,17 @@ class FalukantService extends BaseService {
|
|||||||
where: {
|
where: {
|
||||||
character1Id: character.id,
|
character1Id: character.id,
|
||||||
},
|
},
|
||||||
attributes: ['createdAt', 'widowFirstName2'],
|
attributes: ['createdAt', 'widowFirstName2', 'nextStepProgress'],
|
||||||
include: [
|
include: [
|
||||||
{
|
{
|
||||||
model: FalukantCharacter,
|
model: FalukantCharacter,
|
||||||
as: 'character2',
|
as: 'character2',
|
||||||
attributes: ['id', 'birthdate', 'gender'],
|
attributes: ['id', 'birthdate', 'gender', 'moodId'],
|
||||||
include: [
|
include: [
|
||||||
{ model: FalukantPredefineFirstname, as: 'definedFirstName', attributes: ['name'] },
|
{ model: FalukantPredefineFirstname, as: 'definedFirstName', attributes: ['name'] },
|
||||||
{ model: TitleOfNobility, as: 'nobleTitle', attributes: ['labelTr'] },
|
{ model: TitleOfNobility, as: 'nobleTitle', attributes: ['labelTr'] },
|
||||||
|
{ model: CharacterTrait, as: 'traits' },
|
||||||
|
{ model: Mood, as: 'mood' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -1030,22 +1073,28 @@ class FalukantService extends BaseService {
|
|||||||
relationships = relationships.map((relationship) => ({
|
relationships = relationships.map((relationship) => ({
|
||||||
createdAt: relationship.createdAt,
|
createdAt: relationship.createdAt,
|
||||||
widowFirstName2: relationship.widowFirstName2,
|
widowFirstName2: relationship.widowFirstName2,
|
||||||
|
progress: relationship.nextStepProgress,
|
||||||
character2: {
|
character2: {
|
||||||
id: relationship.character2.id,
|
id: relationship.character2.id,
|
||||||
age: calcAge(relationship.character2.birthdate),
|
age: calcAge(relationship.character2.birthdate),
|
||||||
gender: relationship.character2.gender,
|
gender: relationship.character2.gender,
|
||||||
firstName: relationship.character2.definedFirstName?.name || 'Unknown',
|
firstName: relationship.character2.definedFirstName?.name || 'Unknown',
|
||||||
nobleTitle: relationship.character2.nobleTitle?.labelTr || '',
|
nobleTitle: relationship.character2.nobleTitle?.labelTr || '',
|
||||||
|
mood: relationship.character2.mood,
|
||||||
|
characterTrait: relationship.character2.traits,
|
||||||
},
|
},
|
||||||
relationshipType: relationship.relationshipType.tr,
|
relationshipType: relationship.relationshipType.tr,
|
||||||
}));
|
}));
|
||||||
family.relationships = relationships.filter((relationship) => ['wooing', 'engaged', 'married'].includes(relationship.relationshipType));
|
family.relationships = relationships.filter((relationship) => ['wooing', 'engaged', 'married'].includes(relationship.relationshipType));
|
||||||
family.lovers = relationships.filter((relationship) => ['lover'].includes(relationship.relationshipType.tr));
|
family.lovers = relationships.filter((relationship) => ['lover'].includes(relationship.relationshipType.tr));
|
||||||
family.deathPartners = relationships.filter((relationship) => ['widowed'].includes(relationship.relationshipType.tr));
|
family.deathPartners = relationships.filter((relationship) => ['widowed'].includes(relationship.relationshipType.tr));
|
||||||
if (family.relationships.length === 0 ) {
|
const ownAge = calcAge(character.birthdate);
|
||||||
|
if (ownAge < 12) {
|
||||||
|
family.possiblePartners = [];
|
||||||
|
} else if (family.relationships.length === 0) {
|
||||||
family.possiblePartners = await this.getPossiblePartners(character.id);
|
family.possiblePartners = await this.getPossiblePartners(character.id);
|
||||||
if (family.possiblePartners.length === 0) {
|
if (family.possiblePartners.length === 0) {
|
||||||
await this.createPossiblePartners(character.id, character.gender, character.regionId, character.titleOfNobility);
|
await this.createPossiblePartners(character.id, character.gender, character.regionId, character.titleOfNobility, ownAge);
|
||||||
family.possiblePartners = await this.getPossiblePartners(character.id);
|
family.possiblePartners = await this.getPossiblePartners(character.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1073,7 +1122,6 @@ class FalukantService extends BaseService {
|
|||||||
return proposals.map(proposal => {
|
return proposals.map(proposal => {
|
||||||
const birthdate = new Date(proposal.proposedCharacter.birthdate);
|
const birthdate = new Date(proposal.proposedCharacter.birthdate);
|
||||||
const age = calcAge(birthdate);
|
const age = calcAge(birthdate);
|
||||||
console.log(proposal.proposedCharacter);
|
|
||||||
return {
|
return {
|
||||||
id: proposal.id,
|
id: proposal.id,
|
||||||
requesterCharacterId: proposal.requesterCharacterId,
|
requesterCharacterId: proposal.requesterCharacterId,
|
||||||
@@ -1088,7 +1136,7 @@ class FalukantService extends BaseService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async createPossiblePartners(requestingCharacterId, requestingCharacterGender, requestingRegionId, requestingCharacterTitleOfNobility) {
|
async createPossiblePartners(requestingCharacterId, requestingCharacterGender, requestingRegionId, requestingCharacterTitleOfNobility, ownAge) {
|
||||||
try {
|
try {
|
||||||
const minTitleResult = await TitleOfNobility.findOne({
|
const minTitleResult = await TitleOfNobility.findOne({
|
||||||
order: [['id', 'ASC']],
|
order: [['id', 'ASC']],
|
||||||
@@ -1098,6 +1146,7 @@ class FalukantService extends BaseService {
|
|||||||
throw new Error('No title of nobility found');
|
throw new Error('No title of nobility found');
|
||||||
}
|
}
|
||||||
const minTitle = minTitleResult.id;
|
const minTitle = minTitleResult.id;
|
||||||
|
|
||||||
const potentialPartners = await FalukantCharacter.findAll({
|
const potentialPartners = await FalukantCharacter.findAll({
|
||||||
where: {
|
where: {
|
||||||
id: { [Op.ne]: requestingCharacterId },
|
id: { [Op.ne]: requestingCharacterId },
|
||||||
@@ -1106,8 +1155,12 @@ class FalukantService extends BaseService {
|
|||||||
createdAt: { [Op.lt]: new Date(new Date() - 12 * 24 * 60 * 60 * 1000) },
|
createdAt: { [Op.lt]: new Date(new Date() - 12 * 24 * 60 * 60 * 1000) },
|
||||||
titleOfNobility: { [Op.between]: [requestingCharacterTitleOfNobility - 1, requestingCharacterTitleOfNobility + 1] }
|
titleOfNobility: { [Op.between]: [requestingCharacterTitleOfNobility - 1, requestingCharacterTitleOfNobility + 1] }
|
||||||
},
|
},
|
||||||
|
order: [
|
||||||
|
[Sequelize.literal(`ABS((EXTRACT(EPOCH FROM (NOW() - "birthdate")) / 86400) - ${ownAge})`), 'ASC']
|
||||||
|
],
|
||||||
limit: 5,
|
limit: 5,
|
||||||
});
|
});
|
||||||
|
|
||||||
const proposals = potentialPartners.map(partner => {
|
const proposals = potentialPartners.map(partner => {
|
||||||
const age = calcAge(partner.birthdate);
|
const age = calcAge(partner.birthdate);
|
||||||
return {
|
return {
|
||||||
@@ -1139,7 +1192,6 @@ class FalukantService extends BaseService {
|
|||||||
throw new Error('Proposal not found');
|
throw new Error('Proposal not found');
|
||||||
}
|
}
|
||||||
if (user.money < proposal.cost) {
|
if (user.money < proposal.cost) {
|
||||||
console.log(user, proposal);
|
|
||||||
throw new Error('Not enough money to accept the proposal');
|
throw new Error('Not enough money to accept the proposal');
|
||||||
}
|
}
|
||||||
const moneyResult = await updateFalukantUserMoney(user.id, -proposal.cost, 'Marriage cost', user.id);
|
const moneyResult = await updateFalukantUserMoney(user.id, -proposal.cost, 'Marriage cost', user.id);
|
||||||
@@ -1158,9 +1210,9 @@ class FalukantService extends BaseService {
|
|||||||
relationshipTypeId: marriedType.id,
|
relationshipTypeId: marriedType.id,
|
||||||
});
|
});
|
||||||
await MarriageProposal.destroy({
|
await MarriageProposal.destroy({
|
||||||
where: { character1Id: character.id },
|
where: { requesterCharacterId: character.id },
|
||||||
})
|
})
|
||||||
;
|
;
|
||||||
return { success: true, message: 'Marriage proposal accepted' };
|
return { success: true, message: 'Marriage proposal accepted' };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1172,7 +1224,20 @@ class FalukantService extends BaseService {
|
|||||||
if (!character) {
|
if (!character) {
|
||||||
throw new Error('Character not found');
|
throw new Error('Character not found');
|
||||||
}
|
}
|
||||||
let gifts = await PromotionalGift.findAll();
|
let gifts = await PromotionalGift.findAll({
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
model: PromotionalGiftMood,
|
||||||
|
as: 'promotionalgiftmoods',
|
||||||
|
attributes: ['mood_id', 'suitability']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: PromotionalGiftCharacterTrait,
|
||||||
|
as: 'characterTraits',
|
||||||
|
attributes: ['trait_id', 'suitability']
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
const lowestTitleOfNobility = await TitleOfNobility.findOne({
|
const lowestTitleOfNobility = await TitleOfNobility.findOne({
|
||||||
order: [['id', 'ASC']],
|
order: [['id', 'ASC']],
|
||||||
});
|
});
|
||||||
@@ -1181,61 +1246,107 @@ class FalukantService extends BaseService {
|
|||||||
id: gift.id,
|
id: gift.id,
|
||||||
name: gift.name,
|
name: gift.name,
|
||||||
cost: await this.getGiftCost(gift.value, character.titleOfNobility, lowestTitleOfNobility.id),
|
cost: await this.getGiftCost(gift.value, character.titleOfNobility, lowestTitleOfNobility.id),
|
||||||
|
moodsAffects: gift.promotionalgiftmoods,
|
||||||
|
charactersAffects: gift.characterTraits,
|
||||||
};
|
};
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
async sendGift(hashedUserId, giftId) {
|
async sendGift(hashedUserId, giftId) {
|
||||||
const user = await this.getFalukantUserByHashedId(hashedUserId);
|
const user = await this.getFalukantUserByHashedId(hashedUserId);
|
||||||
const lowestTitleOfNobility = await TitleOfNobility.findOne({
|
const lowestTitle = await TitleOfNobility.findOne({ order: [['id', 'ASC']] });
|
||||||
order: [['id', 'ASC']],
|
const currentMoodId = user.character.moodId;
|
||||||
});
|
if (currentMoodId == null) {
|
||||||
const relation = Relationship.findOne({
|
throw new Error('moodNotSet');
|
||||||
where: {
|
}
|
||||||
character1Id: user.character.id,
|
const relation = await Relationship.findOne({
|
||||||
},
|
where: { character1Id: user.character.id },
|
||||||
include: [
|
include: [{
|
||||||
{
|
model: RelationshipType,
|
||||||
model: RelationshipType,
|
as: 'relationshipType',
|
||||||
as: 'relationshipType',
|
where: { tr: 'wooing' }
|
||||||
where: { tr: 'wooing' },
|
}]
|
||||||
}
|
|
||||||
],
|
|
||||||
});
|
});
|
||||||
if (!relation) {
|
if (!relation) {
|
||||||
throw new Error('User and character are not related');
|
throw new Error('notRelated');
|
||||||
|
}
|
||||||
|
const lastGift = await PromotionalGiftLog.findOne({
|
||||||
|
where: { senderCharacterId: user.character.id },
|
||||||
|
order: [['createdAt', 'DESC']],
|
||||||
|
limit: 1
|
||||||
|
});
|
||||||
|
if (lastGift && (lastGift.createdAt.getTime() + 3_600_000) > Date.now()) {
|
||||||
|
throw new PreconditionError('tooOften');
|
||||||
}
|
}
|
||||||
console.log(user);
|
|
||||||
const gift = await PromotionalGift.findOne({
|
const gift = await PromotionalGift.findOne({
|
||||||
where: { id: giftId },
|
where: { id: giftId },
|
||||||
include: [
|
include: [
|
||||||
{
|
{
|
||||||
model: PromotionalGiftCharacterTrait,
|
model: PromotionalGiftCharacterTrait,
|
||||||
as: 'characterTraits',
|
as: 'characterTraits',
|
||||||
where: { trait_id: { [Op.in]: user.character.characterTraits.map(trait => trait.id) }, },
|
where: { trait_id: { [Op.in]: user.character.traits.map(t => t.id) } },
|
||||||
|
required: false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
model: PromotionalGiftMood,
|
model: PromotionalGiftMood,
|
||||||
as: 'promotionalgiftmoods',
|
as: 'promotionalgiftmoods',
|
||||||
},
|
where: { mood_id: currentMoodId },
|
||||||
|
required: false
|
||||||
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
const cost = await this.getGiftCost(gift.value, user.character.titleOfNobility, lowestTitleOfNobility.id);
|
if (!gift) {
|
||||||
if (user.money < cost) {
|
throw new Error('notFound');
|
||||||
console.log(user, user.money, cost);
|
|
||||||
throw new Error('Not enough money to send the gift');
|
|
||||||
}
|
}
|
||||||
console.log(JSON.stringify(gift));
|
const cost = await this.getGiftCost(
|
||||||
const changeValue = gift.characterTraits.suitability + gift.promotionalgiftmoods.suitability - 4;
|
gift.value,
|
||||||
this.updateFalukantUserMoney(user.id, -cost, 'Gift cost', user.id);
|
user.character.nobleTitle.id,
|
||||||
await relation.update({ value: relation.value + changeValue });
|
lowestTitle.id
|
||||||
|
);
|
||||||
|
if (user.money < cost) {
|
||||||
|
throw new PreconditionError('insufficientFunds');
|
||||||
|
}
|
||||||
|
const traits = gift.characterTraits;
|
||||||
|
if (!traits.length) {
|
||||||
|
throw new Error('noTraits');
|
||||||
|
}
|
||||||
|
const traitAvg = traits.reduce((sum, ct) => sum + ct.suitability, 0) / traits.length;
|
||||||
|
const moodRecord = gift.promotionalgiftmoods[0];
|
||||||
|
if (!moodRecord) {
|
||||||
|
throw new Error('noMoodData');
|
||||||
|
}
|
||||||
|
const moodSuitability = moodRecord.suitability;
|
||||||
|
const changeValue = Math.round(traitAvg + moodSuitability - 5);
|
||||||
|
await updateFalukantUserMoney(user.id, -cost, 'Gift cost', user.id);
|
||||||
|
await relation.update({ nextStepProgress: relation.nextStepProgress + changeValue });
|
||||||
await PromotionalGiftLog.create({
|
await PromotionalGiftLog.create({
|
||||||
senderCharacterId: user.character.id,
|
senderCharacterId: user.character.id,
|
||||||
recipientCharacterId: relation.character2Id,
|
recipientCharacterId: relation.character2Id,
|
||||||
giftId: giftId,
|
giftId,
|
||||||
changeValue: changeValue,
|
changeValue
|
||||||
});
|
});
|
||||||
return { success: true, message: 'Gift sent' };
|
this.checkProposalProgress(relation);
|
||||||
|
return { success: true, message: 'sent' };
|
||||||
|
}
|
||||||
|
|
||||||
|
async checkProposalProgress(relation) {
|
||||||
|
const { nextStepProgress } = relation;
|
||||||
|
if (nextStepProgress >= 100) {
|
||||||
|
const engagedStatus = await RelationshipType.findOne({ where: { tr: 'engaged' } });
|
||||||
|
await relation.update({ nextStepProgress: 0, relationshipTypeId: engagedStatus.id });
|
||||||
|
const user = await User.findOne({
|
||||||
|
include: [{
|
||||||
|
model: FalukantUser,
|
||||||
|
as: 'falukantData',
|
||||||
|
include: [{
|
||||||
|
model: FalukantCharacter,
|
||||||
|
as: 'character',
|
||||||
|
where: { id: relation.character1Id }
|
||||||
|
}]
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
await notifyUser(user.hashedId, 'familychanged');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async getGiftCost(value, titleOfNobility, lowestTitleOfNobility) {
|
async getGiftCost(value, titleOfNobility, lowestTitleOfNobility) {
|
||||||
@@ -1248,8 +1359,114 @@ class FalukantService extends BaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getHouseTypes() {
|
async getHouseTypes() {
|
||||||
// return House
|
// return House
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getMoodAffect() {
|
||||||
|
return PromotionalGiftMood.findAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
async getCharacterAffect() {
|
||||||
|
return PromotionalGiftCharacterTrait.findAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
async getUserHouse(hashedUserId) {
|
||||||
|
try {
|
||||||
|
const user = await User.findOne({
|
||||||
|
where: { hashedId: hashedUserId },
|
||||||
|
include: [{
|
||||||
|
model: FalukantUser,
|
||||||
|
as: 'falukantData',
|
||||||
|
include: [{
|
||||||
|
model: UserHouse,
|
||||||
|
as: 'userHouse',
|
||||||
|
include: [{
|
||||||
|
model: HouseType,
|
||||||
|
as: 'houseType',
|
||||||
|
attributes: ['position', 'cost']
|
||||||
|
}],
|
||||||
|
attributes: ['roofCondition', 'wallCondition', 'floorCondition', 'windowCondition']
|
||||||
|
}],
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
console.log(user.falukantData[0].userHouse);
|
||||||
|
return user.falukantData[0].userHouse ?? { position: 0, roofCondition: 100, wallCondition: 100, floorCondition: 100, windowCondition: 100 };
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getBuyableHouses(hashedUserId) {
|
||||||
|
try {
|
||||||
|
const user = await this.getFalukantUserByHashedId(hashedUserId);
|
||||||
|
const houses = await BuyableHouse.findAll({
|
||||||
|
include: [{
|
||||||
|
model: HouseType,
|
||||||
|
as: 'houseType',
|
||||||
|
attributes: ['position', 'cost'],
|
||||||
|
where: {
|
||||||
|
minimumNobleTitle: {
|
||||||
|
[Op.lte]: user.character.nobleTitle.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
attributes: ['roofCondition', 'wallCondition', 'floorCondition', 'windowCondition', 'id'],
|
||||||
|
order: [
|
||||||
|
[{ model: HouseType, as: 'houseType' }, 'position', 'DESC'],
|
||||||
|
['wallCondition', 'DESC'],
|
||||||
|
['roofCondition', 'DESC'],
|
||||||
|
['floorCondition', 'DESC'],
|
||||||
|
['windowCondition', 'DESC']
|
||||||
|
]
|
||||||
|
});
|
||||||
|
return houses;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Fehler beim Laden der kaufbaren Häuser:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async buyUserHouse(hashedUserId, houseId) {
|
||||||
|
try {
|
||||||
|
const falukantUser = await getFalukantUserOrFail(hashedUserId);
|
||||||
|
const house = await BuyableHouse.findByPk(houseId, {
|
||||||
|
include: [{
|
||||||
|
model: HouseType,
|
||||||
|
as: 'houseType',
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
if (!house) {
|
||||||
|
throw new Error('Das Haus wurde nicht gefunden.');
|
||||||
|
}
|
||||||
|
const housePrice = this.housePrice(house);
|
||||||
|
const oldHouse = await UserHouse.findOne({ where: { userId: falukantUser.id } });
|
||||||
|
if (falukantUser.money < housePrice) {
|
||||||
|
throw new Error('notenoughmoney.');
|
||||||
|
}
|
||||||
|
if (oldHouse) {
|
||||||
|
await oldHouse.destroy();
|
||||||
|
}
|
||||||
|
await UserHouse.create({
|
||||||
|
userId: falukantUser.id,
|
||||||
|
houseTypeId: house.houseTypeId,
|
||||||
|
});
|
||||||
|
await house.destroy();
|
||||||
|
await updateFalukantUserMoney(falukantUser.id, -housePrice, "housebuy", falukantUser.id);
|
||||||
|
const user = await User.findByPk(falukantUser.userId);
|
||||||
|
notifyUser(user.hashedId, 'falukantHouseUpdate', {});
|
||||||
|
return {};
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Fehler beim Kaufen des Hauses:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
housePrice(house) {
|
||||||
|
const houseQuality = (house.roofCondition + house.windowCondition + house.floorCondition + house.wallCondition) / 4;
|
||||||
|
return (house.houseType.cost / 100 * houseQuality ).toFixed(2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default new FalukantService();
|
export default new FalukantService();
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import CharacterTrait from "../../models/falukant/type/character_trait.js";
|
|||||||
import PromotionalGift from "../../models/falukant/type/promotional_gift.js";
|
import PromotionalGift from "../../models/falukant/type/promotional_gift.js";
|
||||||
import PromotionalGiftCharacterTrait from "../../models/falukant/predefine/promotional_gift_character_trait.js";
|
import PromotionalGiftCharacterTrait from "../../models/falukant/predefine/promotional_gift_character_trait.js";
|
||||||
import PromotionalGiftMood from "../../models/falukant/predefine/promotional_gift_mood.js";
|
import PromotionalGiftMood from "../../models/falukant/predefine/promotional_gift_mood.js";
|
||||||
|
import HouseType from '../../models/falukant/type/house.js';
|
||||||
|
import TitleOfNobility from "../../models/falukant/type/title_of_nobility.js";
|
||||||
|
|
||||||
export const initializeFalukantTypes = async () => {
|
export const initializeFalukantTypes = async () => {
|
||||||
await initializeFalukantTypeRegions();
|
await initializeFalukantTypeRegions();
|
||||||
@@ -14,6 +16,7 @@ export const initializeFalukantTypes = async () => {
|
|||||||
await initializeFalukantCharacterTraits();
|
await initializeFalukantCharacterTraits();
|
||||||
await initializeFalukantPromotionalGifts();
|
await initializeFalukantPromotionalGifts();
|
||||||
await initializePromotionalGiftMoodLinks();
|
await initializePromotionalGiftMoodLinks();
|
||||||
|
await initializeFalukantHouseTypes();
|
||||||
};
|
};
|
||||||
|
|
||||||
const regionTypes = [];
|
const regionTypes = [];
|
||||||
@@ -204,6 +207,52 @@ const promotionalGiftMoodLinks = [
|
|||||||
{ gift: "Horse", mood: "nervous", suitability: 4 },
|
{ gift: "Horse", mood: "nervous", suitability: 4 },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const houseTypes = [
|
||||||
|
{ labelTr: 'Unter der Brücke', abbr: 'under_bridge', cost: 10, position: 1, minimumTitle: 'noncivil' },
|
||||||
|
{ labelTr: 'Strohhütte', abbr: 'straw_hut', cost: 20, position: 2, minimumTitle: 'noncivil' },
|
||||||
|
{ labelTr: 'Holzhaus', abbr: 'wooden_house', cost: 50, position: 3, minimumTitle: 'civil' },
|
||||||
|
{ labelTr: 'Hinterhofzimmer', abbr: 'backyard_room', cost: 5, position: 4, minimumTitle: 'civil' },
|
||||||
|
{ labelTr: 'Kleines Familienhaus', abbr: 'family_house', cost: 100, position: 5, minimumTitle: 'sir' },
|
||||||
|
{ labelTr: 'Stadthaus', abbr: 'townhouse', cost: 200, position: 6, minimumTitle: 'townlord' },
|
||||||
|
{ labelTr: 'Villa', abbr: 'villa', cost: 500, position: 7, minimumTitle: 'knight' },
|
||||||
|
{ labelTr: 'Herrenhaus', abbr: 'mansion', cost: 1000, position: 8, minimumTitle: 'ruler' },
|
||||||
|
{ labelTr: 'Schloss', abbr: 'castle', cost: 5000, position: 9, minimumTitle: 'prince-regent' },
|
||||||
|
];
|
||||||
|
|
||||||
|
{
|
||||||
|
const giftNames = promotionalGifts.map(g => g.name);
|
||||||
|
const traitNames = characterTraits.map(t => t.name);
|
||||||
|
|
||||||
|
giftNames.forEach(giftName => {
|
||||||
|
traitNames.forEach(traitName => {
|
||||||
|
if (!promotionalGiftTraitLinks.some(l => l.gift === giftName && l.trait === traitName)) {
|
||||||
|
promotionalGiftTraitLinks.push({
|
||||||
|
gift: giftName,
|
||||||
|
trait: traitName,
|
||||||
|
suitability: Math.floor(Math.random() * 5) + 1,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const giftNames = promotionalGifts.map(g => g.name);
|
||||||
|
const moodNames = moods.map(m => m.name);
|
||||||
|
|
||||||
|
giftNames.forEach(giftName => {
|
||||||
|
moodNames.forEach(moodName => {
|
||||||
|
if (!promotionalGiftMoodLinks.some(l => l.gift === giftName && l.mood === moodName)) {
|
||||||
|
promotionalGiftMoodLinks.push({
|
||||||
|
gift: giftName,
|
||||||
|
mood: moodName,
|
||||||
|
suitability: Math.floor(Math.random() * 5) + 1,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const initializeFalukantTypeRegions = async () => {
|
const initializeFalukantTypeRegions = async () => {
|
||||||
for (const regionType of regionTypeTrs) {
|
for (const regionType of regionTypeTrs) {
|
||||||
const [regionTypeRecord] = await RegionType.findOrCreate({
|
const [regionTypeRecord] = await RegionType.findOrCreate({
|
||||||
@@ -303,6 +352,8 @@ export const initializePromotionalGiftTraitLinks = async () => {
|
|||||||
},
|
},
|
||||||
defaults: {
|
defaults: {
|
||||||
suitability: link.suitability,
|
suitability: link.suitability,
|
||||||
|
gift_id: gift.id,
|
||||||
|
trait_id: trait.id,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -316,15 +367,27 @@ export const initializePromotionalGiftMoodLinks = async () => {
|
|||||||
console.error(`Gift or Mood not found for: ${link.gift}, ${link.mood}`);
|
console.error(`Gift or Mood not found for: ${link.gift}, ${link.mood}`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
await PromotionalGiftMood.findOrCreate({
|
|
||||||
where: {
|
await PromotionalGiftMood.create({
|
||||||
gift_id: gift.id,
|
gift_id: gift.id,
|
||||||
mood_id: mood.id,
|
mood_id: mood.id,
|
||||||
},
|
suitability: link.suitability,
|
||||||
defaults: {
|
}).catch(err => {
|
||||||
suitability: link.suitability,
|
if (err.name !== 'SequelizeUniqueConstraintError') throw err;
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const initializeFalukantHouseTypes = async () => {
|
||||||
|
for (const ht of houseTypes) {
|
||||||
|
const [record, created] = await HouseType.findOrCreate({
|
||||||
|
where: { labelTr: ht.abbr },
|
||||||
|
defaults: {
|
||||||
|
cost: ht.cost,
|
||||||
|
imageUrl: null,
|
||||||
|
position: ht.position,
|
||||||
|
minimumNobleTitle: await TitleOfNobility.findOne({ where: { labelTr: ht.minimumTitle } }).then(title => title.id),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
BIN
frontend/public/images/falukant/houses.png
Normal file
BIN
frontend/public/images/falukant/houses.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.8 MiB |
@@ -70,7 +70,7 @@ export default {
|
|||||||
}
|
}
|
||||||
this.statusItems = [
|
this.statusItems = [
|
||||||
{ key: "age", icon: "👶", value: age },
|
{ key: "age", icon: "👶", value: age },
|
||||||
{ key: "wealth", icon: "💰", value: money },
|
{ key: "wealth", icon: "💰", value: Intl.NumberFormat(navigator.language, { minimumFractionDigits: 2, maximumFractionDigits: 2 }).format(money) },
|
||||||
{ key: "health", icon: "❤️", value: healthStatus },
|
{ key: "health", icon: "❤️", value: healthStatus },
|
||||||
{ key: "events", icon: "📰", value: events || null },
|
{ key: "events", icon: "📰", value: events || null },
|
||||||
];
|
];
|
||||||
@@ -80,12 +80,15 @@ export default {
|
|||||||
},
|
},
|
||||||
async handleDaemonSocketMessage(event) {
|
async handleDaemonSocketMessage(event) {
|
||||||
try {
|
try {
|
||||||
|
if (event.data === 'ping') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const data = JSON.parse(event.data);
|
const data = JSON.parse(event.data);
|
||||||
if (data.event === "falukantUpdateStatus") {
|
if (data.event === "falukantUpdateStatus") {
|
||||||
this.fetchStatus();
|
this.fetchStatus();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error parsing daemonSocket message:", error);
|
console.error("Error parsing daemonSocket message:", error, event.data);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
openPage(url, hasSubmenu = false) {
|
openPage(url, hasSubmenu = false) {
|
||||||
|
|||||||
@@ -211,8 +211,22 @@
|
|||||||
"accept": "Werbung mit diesem Partner starten",
|
"accept": "Werbung mit diesem Partner starten",
|
||||||
"wooing": {
|
"wooing": {
|
||||||
"gifts": "Werbegeschenke",
|
"gifts": "Werbegeschenke",
|
||||||
"sendGift": "Werbegeschenk senden"
|
"sendGift": "Werbegeschenk senden",
|
||||||
}
|
"gift": "Geschenk",
|
||||||
|
"value": "Kosten",
|
||||||
|
"effect": "Wirkung"
|
||||||
|
},
|
||||||
|
"giftAffect": {
|
||||||
|
"0": "Keiner",
|
||||||
|
"1": "Sehr niedrig",
|
||||||
|
"2": "Niedrig",
|
||||||
|
"3": "Mittel",
|
||||||
|
"4": "Hoch",
|
||||||
|
"5": "Sehr hoch"
|
||||||
|
|
||||||
|
},
|
||||||
|
"mood": "Stimmung",
|
||||||
|
"progress": "Zuneigung"
|
||||||
},
|
},
|
||||||
"relationships": {
|
"relationships": {
|
||||||
"name": "Name"
|
"name": "Name"
|
||||||
@@ -242,6 +256,15 @@
|
|||||||
"addSpouse": "Ehepartner hinzufügen",
|
"addSpouse": "Ehepartner hinzufügen",
|
||||||
"viewDetails": "Details anzeigen",
|
"viewDetails": "Details anzeigen",
|
||||||
"remove": "Entfernen"
|
"remove": "Entfernen"
|
||||||
|
},
|
||||||
|
"sendgift": {
|
||||||
|
"error": {
|
||||||
|
"nogiftselected": "Bitte wähle ein Geschenk aus.",
|
||||||
|
"generic": "Ein unbekannter Fehler ist aufgetreten.",
|
||||||
|
"tooOften": "Du kannst nicht so oft Geschenke machen.",
|
||||||
|
"insufficientFunds": "Du hast nicht genug Geld."
|
||||||
|
},
|
||||||
|
"success": "Das Geschenk wurde überreicht."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"product": {
|
"product": {
|
||||||
@@ -291,9 +314,15 @@
|
|||||||
"changeValue": "Wertänderung",
|
"changeValue": "Wertänderung",
|
||||||
"time": "Zeit",
|
"time": "Zeit",
|
||||||
"activities": {
|
"activities": {
|
||||||
"Product sale": "Produktverkauf",
|
"Product sale": "Produkte verkauft",
|
||||||
"Production cost": "Produktionskosten",
|
"Production cost": "Produktionskosten",
|
||||||
"Sell all products": "Alle Produkte verkaufen"
|
"Sell all products": "Alle Produkte verkauft",
|
||||||
|
"sell products": "Produkte verkauft",
|
||||||
|
"director starts production": "Direktor beginnt Produktion",
|
||||||
|
"Buy storage (type: field)": "Lagerplatz gekauft (Typ: Feld)",
|
||||||
|
"Buy storage (type: iron)": "Lagerplatz gekauft (Typ: Eisen)",
|
||||||
|
"Buy storage (type: stone)": "Lagerplatz gekauft (Typ: Stein)",
|
||||||
|
"Buy storage (type: wood)": "Lagerplatz gekauft (Typ: Holz)"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"newdirector": {
|
"newdirector": {
|
||||||
@@ -327,6 +356,63 @@
|
|||||||
"Cat": "Katze",
|
"Cat": "Katze",
|
||||||
"Dog": "Hund",
|
"Dog": "Hund",
|
||||||
"Horse": "Pferd"
|
"Horse": "Pferd"
|
||||||
|
},
|
||||||
|
"mood": {
|
||||||
|
"happy": "Glücklich",
|
||||||
|
"sad": "Traurig",
|
||||||
|
"angry": "Wütend",
|
||||||
|
"scared": "Verängstigt",
|
||||||
|
"surprised": "Überrascht",
|
||||||
|
"normal": "Normal"
|
||||||
|
},
|
||||||
|
"character": {
|
||||||
|
"brave": "Mutig",
|
||||||
|
"kind": "Freundlich",
|
||||||
|
"greedy": "Gierig",
|
||||||
|
"wise": "Weise",
|
||||||
|
"loyal": "Loyal",
|
||||||
|
"cunning": "Listig",
|
||||||
|
"generous": "Großzügig",
|
||||||
|
"arrogant": "Arrogant",
|
||||||
|
"honest": "Ehrlich",
|
||||||
|
"ambitious": "Ehrgeizig",
|
||||||
|
"patient": "Geduldig",
|
||||||
|
"impatient": "Ungeduldig",
|
||||||
|
"selfish": "Egoistisch",
|
||||||
|
"charismatic": "Charismatisch",
|
||||||
|
"empathetic": "Einfühlsam",
|
||||||
|
"timid": "Schüchtern",
|
||||||
|
"stubborn": "Stur",
|
||||||
|
"resourceful": "Einfallsreich",
|
||||||
|
"reckless": "Rücksichtslos",
|
||||||
|
"disciplined": "Diszipliniert",
|
||||||
|
"optimistic": "Optimistisch",
|
||||||
|
"pessimistic": "Pessimistisch",
|
||||||
|
"manipulative": "Manipulativ",
|
||||||
|
"independent": "Unabhängig",
|
||||||
|
"dependent": "Abhängig",
|
||||||
|
"adventurous": "Abenteuerlustig",
|
||||||
|
"humble": "Bescheiden",
|
||||||
|
"vengeful": "Rachsüchtig",
|
||||||
|
"pragmatic": "Pragmatisch",
|
||||||
|
"idealistic": "Idealistisch"
|
||||||
|
},
|
||||||
|
"house": {
|
||||||
|
"title": "Haus",
|
||||||
|
"statusreport": "Zustand des Hauses",
|
||||||
|
"element": "Bereich",
|
||||||
|
"state": "Zustand",
|
||||||
|
"buyablehouses": "Kaufe ein Haus",
|
||||||
|
"buy": "Kaufen",
|
||||||
|
"price": "Kaufpreis",
|
||||||
|
"worth": "Restwert",
|
||||||
|
"sell": "Verkaufen",
|
||||||
|
"status": {
|
||||||
|
"roofCondition": "Dach",
|
||||||
|
"wallCondition": "Wände",
|
||||||
|
"floorCondition": "Böden",
|
||||||
|
"windowCondition": "Fenster"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,6 +3,7 @@ import Createview from '../views/falukant/CreateView.vue';
|
|||||||
import FalukantOverviewView from '../views/falukant/OverviewView.vue';
|
import FalukantOverviewView from '../views/falukant/OverviewView.vue';
|
||||||
import MoneyHistoryView from '../views/falukant/MoneyHistoryView.vue';
|
import MoneyHistoryView from '../views/falukant/MoneyHistoryView.vue';
|
||||||
import FamilyView from '../views/falukant/FamilyView.vue';
|
import FamilyView from '../views/falukant/FamilyView.vue';
|
||||||
|
import HouseView from '../views/falukant/HouseView.vue';
|
||||||
|
|
||||||
const falukantRoutes = [
|
const falukantRoutes = [
|
||||||
{
|
{
|
||||||
@@ -35,6 +36,12 @@ const falukantRoutes = [
|
|||||||
component: FamilyView,
|
component: FamilyView,
|
||||||
meta: { requiresAuth: true }
|
meta: { requiresAuth: true }
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/falukant/house',
|
||||||
|
name: 'HouseView',
|
||||||
|
component: HouseView,
|
||||||
|
meta: { requiresAuth: true },
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export default falukantRoutes;
|
export default falukantRoutes;
|
||||||
|
|||||||
@@ -3,31 +3,50 @@
|
|||||||
<StatusBar />
|
<StatusBar />
|
||||||
<div class="contentscroll">
|
<div class="contentscroll">
|
||||||
|
|
||||||
<!-- Titel -->
|
|
||||||
<h2>{{ $t('falukant.family.title') }}</h2>
|
<h2>{{ $t('falukant.family.title') }}</h2>
|
||||||
|
|
||||||
<!-- Ehepartner -->
|
|
||||||
<div class="spouse-section">
|
<div class="spouse-section">
|
||||||
<h3>{{ $t('falukant.family.spouse.title') }}</h3>
|
<h3>{{ $t('falukant.family.spouse.title') }}</h3>
|
||||||
<div v-if="relationships.length > 0">
|
<div v-if="relationships.length > 0">
|
||||||
<table>
|
<div class="relationship">
|
||||||
<tr>
|
<table>
|
||||||
<td>{{ $t('falukant.family.relationships.name') }}</td>
|
<tr>
|
||||||
<td>
|
<td>{{ $t('falukant.family.relationships.name') }}</td>
|
||||||
{{ $t('falukant.titles.' + relationships[0].character2.gender + '.' +
|
<td>
|
||||||
relationships[0].character2.nobleTitle) }}
|
{{ $t('falukant.titles.' + relationships[0].character2.gender + '.' +
|
||||||
{{ relationships[0].character2.firstName }}
|
relationships[0].character2.nobleTitle) }}
|
||||||
</td>
|
{{ relationships[0].character2.firstName }}
|
||||||
</tr>
|
</td>
|
||||||
<tr>
|
</tr>
|
||||||
<td>{{ $t('falukant.family.spouse.age') }}</td>
|
<tr>
|
||||||
<td>{{ relationships[0].character2.age }}</td>
|
<td>{{ $t('falukant.family.spouse.age') }}</td>
|
||||||
</tr>
|
<td>{{ relationships[0].character2.age }}</td>
|
||||||
<tr>
|
</tr>
|
||||||
<td>{{ $t('falukant.family.spouse.status') }}</td>
|
<tr>
|
||||||
<td>{{ $t('falukant.family.statuses.' + relationships[0].relationshipType) }}</td>
|
<td>{{ $t('falukant.family.spouse.mood') }}</td>
|
||||||
</tr>
|
<td>{{ $t(`falukant.mood.${relationships[0].character2.mood.tr}`) }}</td>
|
||||||
</table>
|
</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'">
|
<div v-if="relationships[0].relationshipType === 'wooing'">
|
||||||
<h3>{{ $t('falukant.family.spouse.wooing.gifts') }}</h3>
|
<h3>{{ $t('falukant.family.spouse.wooing.gifts') }}</h3>
|
||||||
<table class="spouse-table">
|
<table class="spouse-table">
|
||||||
@@ -35,6 +54,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th></th>
|
<th></th>
|
||||||
<th>{{ $t('falukant.family.spouse.wooing.gift') }}</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>
|
<th>{{ $t('falukant.family.spouse.wooing.value') }}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@@ -42,12 +62,14 @@
|
|||||||
<tr v-for="gift in gifts" :key="gift.id">
|
<tr v-for="gift in gifts" :key="gift.id">
|
||||||
<td><input type="radio" name="gift" :value="gift.id" v-model="selectedGiftId"></td>
|
<td><input type="radio" name="gift" :value="gift.id" v-model="selectedGiftId"></td>
|
||||||
<td>{{ $t(`falukant.gifts.${gift.name}`) }}</td>
|
<td>{{ $t(`falukant.gifts.${gift.name}`) }}</td>
|
||||||
|
<td>{{ $t(`falukant.family.spouse.giftAffect.${getEffect(gift)}`) }}</td>
|
||||||
<td>{{ formatCost(gift.cost) }}</td>
|
<td>{{ formatCost(gift.cost) }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<div>
|
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -67,7 +89,7 @@
|
|||||||
v-model="selectedProposalId"></td>
|
v-model="selectedProposalId"></td>
|
||||||
<td>{{
|
<td>{{
|
||||||
$t(`falukant.titles.${proposal.proposedCharacterGender}.${proposal.proposedCharacterNobleTitle}`)
|
$t(`falukant.titles.${proposal.proposedCharacterGender}.${proposal.proposedCharacterNobleTitle}`)
|
||||||
}} {{ proposal.proposedCharacterName }}</td>
|
}} {{ proposal.proposedCharacterName }}</td>
|
||||||
<td>{{ proposal.proposedCharacterAge }}</td>
|
<td>{{ proposal.proposedCharacterAge }}</td>
|
||||||
<td>{{ formatCost(proposal.cost) }}</td>
|
<td>{{ formatCost(proposal.cost) }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -80,7 +102,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Kinder -->
|
|
||||||
<div class="children-section">
|
<div class="children-section">
|
||||||
<h3>{{ $t('falukant.family.children.title') }}</h3>
|
<h3>{{ $t('falukant.family.children.title') }}</h3>
|
||||||
<div v-if="children && children.length > 0">
|
<div v-if="children && children.length > 0">
|
||||||
@@ -131,9 +152,6 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- Dialog-Beispiele oder ähnliche Komponenten -->
|
|
||||||
<MessageDialog ref="messageDialog" />
|
|
||||||
<ErrorDialog ref="errorDialog" />
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@@ -160,7 +178,9 @@ export default {
|
|||||||
proposals: [],
|
proposals: [],
|
||||||
selectedProposalId: null,
|
selectedProposalId: null,
|
||||||
gifts: [],
|
gifts: [],
|
||||||
selectedGiftId: null
|
selectedGiftId: null,
|
||||||
|
moodAffects: [],
|
||||||
|
characterAffects: []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -169,12 +189,13 @@ export default {
|
|||||||
async mounted() {
|
async mounted() {
|
||||||
await this.loadFamilyData();
|
await this.loadFamilyData();
|
||||||
await this.loadGifts();
|
await this.loadGifts();
|
||||||
|
await this.loadMoodAffects();
|
||||||
|
await this.loadCharacterAffects();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async loadFamilyData() {
|
async loadFamilyData() {
|
||||||
try {
|
try {
|
||||||
const response = await apiClient.get('/api/falukant/family');
|
const response = await apiClient.get('/api/falukant/family');
|
||||||
console.log(response.data);
|
|
||||||
this.relationships = response.data.relationships;
|
this.relationships = response.data.relationships;
|
||||||
this.children = response.data.children;
|
this.children = response.data.children;
|
||||||
this.lovers = response.data.lovers;
|
this.lovers = response.data.lovers;
|
||||||
@@ -193,6 +214,22 @@ export default {
|
|||||||
return new Intl.NumberFormat(navigator.language, { minimumFractionDigits: 2, maximumFractionDigits: 2 }).format(value);
|
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() {
|
async acceptProposal() {
|
||||||
const response = await apiClient.post('/api/falukant/family/acceptmarriageproposal'
|
const response = await apiClient.post('/api/falukant/family/acceptmarriageproposal'
|
||||||
, { proposalId: this.selectedProposalId });
|
, { proposalId: this.selectedProposalId });
|
||||||
@@ -200,19 +237,54 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async loadGifts() {
|
async loadGifts() {
|
||||||
const response = await apiClient.get('/api/falukant/family/gifts');
|
const response = await apiClient.get('/api/falukant/family/gifts');
|
||||||
this.gifts = response.data;
|
this.gifts = response.data;
|
||||||
},
|
},
|
||||||
|
|
||||||
async sendGift() {
|
async sendGift() {
|
||||||
if (!this.selectedGiftId) {
|
if (!this.selectedGiftId) {
|
||||||
alert('Please select a gift');
|
this.$root.$refs.errorDialog.open(`tr:falukant.family.sendgift.error.nogiftselected`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const response = await apiClient.post('/api/falukant/family/gift'
|
try {
|
||||||
, { giftId: this.selectedGiftId });
|
await apiClient.post('/api/falukant/family/gift'
|
||||||
this.loadFamilyData();
|
, { 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>
|
</script>
|
||||||
@@ -284,4 +356,28 @@ export default {
|
|||||||
h2 {
|
h2 {
|
||||||
padding-top: 20px;
|
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>
|
</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>
|
||||||
<div class="imagecontainer">
|
<div class="imagecontainer">
|
||||||
<div :style="getAvatarStyle" class="avatar"></div>
|
<div :style="getAvatarStyle" class="avatar"></div>
|
||||||
|
<div :style="getHouseStyle" class="house"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -168,6 +169,24 @@ export default {
|
|||||||
height: `${height}px`,
|
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() {
|
moneyValue() {
|
||||||
const m = this.falukantUser?.money;
|
const m = this.falukantUser?.money;
|
||||||
return typeof m === 'string' ? parseFloat(m) : m;
|
return typeof m === 'string' ? parseFloat(m) : m;
|
||||||
@@ -298,7 +317,16 @@ export default {
|
|||||||
image-rendering: crisp-edges;
|
image-rendering: crisp-edges;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.house {
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 4px;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: cover;
|
||||||
|
image-rendering: crisp-edges;
|
||||||
|
}
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
padding-top: 20px;
|
padding-top: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user