Add new requirements for nobility titles and enhance service logic: Introduce checks for reputation, house position, house condition, office rank, and lover count in the FalukantService. Update title requirements in the initialization script to include these new criteria. Enhance localization for requirements in English, German, and Spanish, ensuring accurate translations for new conditions.
This commit is contained in:
@@ -110,6 +110,33 @@ function calcRegionalSellPriceSync(product, knowledgeFactor, worthPercent) {
|
||||
return min + (max - min) * (knowledgeFactor / 100);
|
||||
}
|
||||
|
||||
const POLITICAL_OFFICE_RANKS = {
|
||||
assessor: 1,
|
||||
councillor: 1,
|
||||
council: 2,
|
||||
beadle: 2,
|
||||
'town-clerk': 2,
|
||||
mayor: 3,
|
||||
'master-builder': 2,
|
||||
'village-major': 2,
|
||||
judge: 3,
|
||||
bailif: 3,
|
||||
taxman: 2,
|
||||
sheriff: 3,
|
||||
consultant: 3,
|
||||
treasurer: 4,
|
||||
hangman: 2,
|
||||
'territorial-council': 3,
|
||||
'territorial-council-speaker': 4,
|
||||
'ruler-consultant': 4,
|
||||
'state-administrator': 4,
|
||||
'super-state-administrator': 5,
|
||||
governor: 5,
|
||||
'ministry-helper': 4,
|
||||
minister: 5,
|
||||
chancellor: 6
|
||||
};
|
||||
|
||||
async function calcRegionalSellPrice(product, knowledgeFactor, regionId, worthPercent = null) {
|
||||
// Wenn worthPercent nicht übergeben wurde, hole es aus der Datenbank
|
||||
if (worthPercent === null) {
|
||||
@@ -5244,6 +5271,21 @@ class FalukantService extends BaseService {
|
||||
case 'branches':
|
||||
fulfilled = fulfilled && await this.checkBranchesRequirement(hashedUserId, requirement);
|
||||
break;
|
||||
case 'reputation':
|
||||
fulfilled = fulfilled && await this.checkReputationRequirement(user, requirement);
|
||||
break;
|
||||
case 'house_position':
|
||||
fulfilled = fulfilled && await this.checkHousePositionRequirement(user, requirement);
|
||||
break;
|
||||
case 'house_condition':
|
||||
fulfilled = fulfilled && await this.checkHouseConditionRequirement(user, requirement);
|
||||
break;
|
||||
case 'office_rank_any':
|
||||
fulfilled = fulfilled && await this.checkOfficeRankAnyRequirement(user, requirement);
|
||||
break;
|
||||
case 'lover_count_max':
|
||||
fulfilled = fulfilled && await this.checkLoverCountMaxRequirement(user, requirement);
|
||||
break;
|
||||
default:
|
||||
fulfilled = false;
|
||||
};
|
||||
@@ -5278,6 +5320,103 @@ class FalukantService extends BaseService {
|
||||
return branchCount >= requirement.requirementValue;
|
||||
}
|
||||
|
||||
async checkReputationRequirement(user, requirement) {
|
||||
const character = user.character || await FalukantCharacter.findOne({
|
||||
where: { userId: user.id },
|
||||
attributes: ['reputation']
|
||||
});
|
||||
return Number(character?.reputation || 0) >= Number(requirement.requirementValue || 0);
|
||||
}
|
||||
|
||||
async checkHousePositionRequirement(user, requirement) {
|
||||
const house = await UserHouse.findOne({
|
||||
where: { userId: user.id },
|
||||
include: [{ model: HouseType, as: 'houseType', attributes: ['position'] }]
|
||||
});
|
||||
return Number(house?.houseType?.position || 0) >= Number(requirement.requirementValue || 0);
|
||||
}
|
||||
|
||||
async checkHouseConditionRequirement(user, requirement) {
|
||||
const house = await UserHouse.findOne({
|
||||
where: { userId: user.id },
|
||||
attributes: ['roofCondition', 'wallCondition', 'floorCondition', 'windowCondition']
|
||||
});
|
||||
if (!house) return false;
|
||||
const averageCondition = (
|
||||
Number(house.roofCondition || 0) +
|
||||
Number(house.wallCondition || 0) +
|
||||
Number(house.floorCondition || 0) +
|
||||
Number(house.windowCondition || 0)
|
||||
) / 4;
|
||||
return averageCondition >= Number(requirement.requirementValue || 0);
|
||||
}
|
||||
|
||||
async getHighestOfficeRankAny(userId) {
|
||||
const character = await FalukantCharacter.findOne({
|
||||
where: { userId },
|
||||
attributes: ['id']
|
||||
});
|
||||
if (!character) return 0;
|
||||
|
||||
const [politicalOffices, politicalHistories, churchOffices] = await Promise.all([
|
||||
PoliticalOffice.findAll({
|
||||
where: { characterId: character.id },
|
||||
include: [{ model: PoliticalOfficeType, as: 'officeType', attributes: ['name'] }],
|
||||
attributes: ['officeTypeId']
|
||||
}),
|
||||
PoliticalOfficeHistory.findAll({
|
||||
where: { characterId: character.id },
|
||||
include: [{ model: PoliticalOfficeType, as: 'officeTypeHistory', attributes: ['name'] }],
|
||||
attributes: ['officeTypeId']
|
||||
}),
|
||||
ChurchOffice.findAll({
|
||||
where: { characterId: character.id },
|
||||
include: [{ model: ChurchOfficeType, as: 'type', attributes: ['hierarchyLevel'] }],
|
||||
attributes: ['officeTypeId']
|
||||
})
|
||||
]);
|
||||
|
||||
const politicalRanks = politicalOffices.map((office) => POLITICAL_OFFICE_RANKS[office.officeType?.name] || 0);
|
||||
const politicalHistoryRanks = politicalHistories.map((history) => POLITICAL_OFFICE_RANKS[history.officeTypeHistory?.name] || 0);
|
||||
const churchRanks = churchOffices.map((office) => Number(office.type?.hierarchyLevel || 0));
|
||||
|
||||
return Math.max(0, ...politicalRanks, ...politicalHistoryRanks, ...churchRanks);
|
||||
}
|
||||
|
||||
async checkOfficeRankAnyRequirement(user, requirement) {
|
||||
const highestRank = await this.getHighestOfficeRankAny(user.id);
|
||||
return highestRank >= Number(requirement.requirementValue || 0);
|
||||
}
|
||||
|
||||
async checkLoverCountMaxRequirement(user, requirement) {
|
||||
const character = user.character || await FalukantCharacter.findOne({
|
||||
where: { userId: user.id },
|
||||
attributes: ['id']
|
||||
});
|
||||
if (!character) return false;
|
||||
|
||||
const loverType = await RelationshipType.findOne({
|
||||
where: { tr: 'lover' },
|
||||
attributes: ['id']
|
||||
});
|
||||
if (!loverType) return true;
|
||||
|
||||
const loverRelationships = await Relationship.findAll({
|
||||
where: {
|
||||
character1Id: character.id,
|
||||
relationshipTypeId: loverType.id
|
||||
},
|
||||
include: [{
|
||||
model: RelationshipState,
|
||||
as: 'state',
|
||||
required: false
|
||||
}]
|
||||
});
|
||||
|
||||
const activeLoverCount = loverRelationships.filter((rel) => (rel.state?.active ?? true) !== false).length;
|
||||
return activeLoverCount <= Number(requirement.requirementValue || 0);
|
||||
}
|
||||
|
||||
async getHealth(hashedUserId) {
|
||||
const user = await this.getFalukantUserByHashedId(hashedUserId);
|
||||
const healthActivities = FalukantService.HEALTH_ACTIVITIES.map((activity) => { return { tr: activity.tr, cost: activity.cost } });
|
||||
|
||||
120
backend/sql/update_nobility_requirements_extended.sql
Normal file
120
backend/sql/update_nobility_requirements_extended.sql
Normal file
@@ -0,0 +1,120 @@
|
||||
-- PostgreSQL only
|
||||
-- Ersetzt die Standesanforderungen fuer Falukant durch das erweiterte Profilmodell.
|
||||
|
||||
DELETE FROM falukant_type.title_requirement
|
||||
WHERE title_id IN (
|
||||
SELECT id
|
||||
FROM falukant_type.title
|
||||
WHERE label_tr IN (
|
||||
'civil', 'sir', 'townlord', 'by', 'landlord', 'knight',
|
||||
'baron', 'count', 'palsgrave', 'margrave', 'landgrave',
|
||||
'ruler', 'elector', 'imperial-prince', 'duke', 'grand-duke',
|
||||
'prince-regent', 'king'
|
||||
)
|
||||
);
|
||||
|
||||
INSERT INTO falukant_type.title_requirement (title_id, requirement_type, requirement_value)
|
||||
SELECT tm.id, req.requirement_type, req.requirement_value
|
||||
FROM (
|
||||
SELECT id, label_tr
|
||||
FROM falukant_type.title
|
||||
WHERE label_tr IN (
|
||||
'civil', 'sir', 'townlord', 'by', 'landlord', 'knight',
|
||||
'baron', 'count', 'palsgrave', 'margrave', 'landgrave',
|
||||
'ruler', 'elector', 'imperial-prince', 'duke', 'grand-duke',
|
||||
'prince-regent', 'king'
|
||||
)
|
||||
) tm
|
||||
JOIN (
|
||||
VALUES
|
||||
('civil', 'money', 5000::numeric),
|
||||
('civil', 'cost', 500::numeric),
|
||||
|
||||
('sir', 'branches', 2::numeric),
|
||||
('sir', 'cost', 1000::numeric),
|
||||
|
||||
('townlord', 'cost', 3000::numeric),
|
||||
('townlord', 'money', 12000::numeric),
|
||||
('townlord', 'reputation', 18::numeric),
|
||||
|
||||
('by', 'cost', 5000::numeric),
|
||||
('by', 'money', 18000::numeric),
|
||||
('by', 'house_position', 1::numeric),
|
||||
|
||||
('landlord', 'cost', 7500::numeric),
|
||||
('landlord', 'money', 26000::numeric),
|
||||
('landlord', 'reputation', 24::numeric),
|
||||
('landlord', 'house_condition', 60::numeric),
|
||||
|
||||
('knight', 'cost', 11000::numeric),
|
||||
('knight', 'money', 38000::numeric),
|
||||
('knight', 'office_rank_any', 1::numeric),
|
||||
|
||||
('baron', 'branches', 4::numeric),
|
||||
('baron', 'cost', 16000::numeric),
|
||||
('baron', 'money', 55000::numeric),
|
||||
('baron', 'house_position', 2::numeric),
|
||||
|
||||
('count', 'cost', 23000::numeric),
|
||||
('count', 'money', 80000::numeric),
|
||||
('count', 'reputation', 32::numeric),
|
||||
('count', 'house_condition', 68::numeric),
|
||||
|
||||
('palsgrave', 'cost', 32000::numeric),
|
||||
('palsgrave', 'money', 115000::numeric),
|
||||
('palsgrave', 'office_rank_any', 2::numeric),
|
||||
('palsgrave', 'house_position', 3::numeric),
|
||||
|
||||
('margrave', 'cost', 45000::numeric),
|
||||
('margrave', 'money', 165000::numeric),
|
||||
('margrave', 'reputation', 40::numeric),
|
||||
('margrave', 'house_condition', 72::numeric),
|
||||
('margrave', 'lover_count_max', 2::numeric),
|
||||
|
||||
('landgrave', 'cost', 62000::numeric),
|
||||
('landgrave', 'money', 230000::numeric),
|
||||
('landgrave', 'office_rank_any', 3::numeric),
|
||||
('landgrave', 'house_position', 4::numeric),
|
||||
|
||||
('ruler', 'cost', 85000::numeric),
|
||||
('ruler', 'money', 320000::numeric),
|
||||
('ruler', 'reputation', 48::numeric),
|
||||
('ruler', 'house_condition', 76::numeric),
|
||||
|
||||
('elector', 'cost', 115000::numeric),
|
||||
('elector', 'money', 440000::numeric),
|
||||
('elector', 'office_rank_any', 4::numeric),
|
||||
('elector', 'house_position', 5::numeric),
|
||||
('elector', 'lover_count_max', 2::numeric),
|
||||
|
||||
('imperial-prince', 'cost', 155000::numeric),
|
||||
('imperial-prince', 'money', 600000::numeric),
|
||||
('imperial-prince', 'reputation', 56::numeric),
|
||||
('imperial-prince', 'house_condition', 80::numeric),
|
||||
|
||||
('duke', 'cost', 205000::numeric),
|
||||
('duke', 'money', 820000::numeric),
|
||||
('duke', 'office_rank_any', 5::numeric),
|
||||
('duke', 'house_position', 6::numeric),
|
||||
|
||||
('grand-duke', 'cost', 270000::numeric),
|
||||
('grand-duke', 'money', 1120000::numeric),
|
||||
('grand-duke', 'reputation', 64::numeric),
|
||||
('grand-duke', 'house_condition', 84::numeric),
|
||||
('grand-duke', 'lover_count_max', 1::numeric),
|
||||
|
||||
('prince-regent', 'cost', 360000::numeric),
|
||||
('prince-regent', 'money', 1520000::numeric),
|
||||
('prince-regent', 'office_rank_any', 6::numeric),
|
||||
('prince-regent', 'house_position', 7::numeric),
|
||||
|
||||
('king', 'cost', 500000::numeric),
|
||||
('king', 'money', 2100000::numeric),
|
||||
('king', 'reputation', 72::numeric),
|
||||
('king', 'house_position', 8::numeric),
|
||||
('king', 'house_condition', 88::numeric),
|
||||
('king', 'lover_count_max', 1::numeric)
|
||||
) AS req(label_tr, requirement_type, requirement_value)
|
||||
ON req.label_tr = tm.label_tr
|
||||
ON CONFLICT (title_id, requirement_type)
|
||||
DO UPDATE SET requirement_value = EXCLUDED.requirement_value;
|
||||
@@ -299,22 +299,22 @@ async function initializeFalukantTitleRequirements() {
|
||||
const titleRequirements = [
|
||||
{ labelTr: "civil", requirements: [{ type: "money", value: 5000 }, { type: "cost", value: 500 }] },
|
||||
{ labelTr: "sir", requirements: [{ type: "branches", value: 2 }, { type: "cost", value: 1000 }] },
|
||||
{ labelTr: "townlord", requirements: [{ type: "cost", value: 3000 }] },
|
||||
{ labelTr: "by", requirements: [{ type: "cost", value: 6000 }] },
|
||||
{ labelTr: "landlord", requirements: [{ type: "cost", value: 9000 }] },
|
||||
{ labelTr: "knight", requirements: [{ type: "cost", value: 11000 }] },
|
||||
{ labelTr: "baron", requirements: [{ type: "branches", value: 4 }, { type: "cost", value: 15000 }] },
|
||||
{ labelTr: "count", requirements: [{ type: "cost", value: 19000 }] },
|
||||
{ labelTr: "palsgrave", requirements: [{ type: "cost", value: 25000 }] },
|
||||
{ labelTr: "margrave", requirements: [{ type: "cost", value: 33000 }] },
|
||||
{ labelTr: "landgrave", requirements: [{ type: "cost", value: 47000 }] },
|
||||
{ labelTr: "ruler", requirements: [{ type: "cost", value: 66000 }] },
|
||||
{ labelTr: "elector", requirements: [{ type: "cost", value: 79000 }] },
|
||||
{ labelTr: "imperial-prince", requirements: [{ type: "cost", value: 99999 }] },
|
||||
{ labelTr: "duke", requirements: [{ type: "cost", value: 130000 }] },
|
||||
{ labelTr: "grand-duke",requirements: [{ type: "cost", value: 170000 }] },
|
||||
{ labelTr: "prince-regent", requirements: [{ type: "cost", value: 270000 }] },
|
||||
{ labelTr: "king", requirements: [{ type: "cost", value: 500000 }] },
|
||||
{ labelTr: "townlord", requirements: [{ type: "cost", value: 3000 }, { type: "money", value: 12000 }, { type: "reputation", value: 18 }] },
|
||||
{ labelTr: "by", requirements: [{ type: "cost", value: 5000 }, { type: "money", value: 18000 }, { type: "house_position", value: 1 }] },
|
||||
{ labelTr: "landlord", requirements: [{ type: "cost", value: 7500 }, { type: "money", value: 26000 }, { type: "reputation", value: 24 }, { type: "house_condition", value: 60 }] },
|
||||
{ labelTr: "knight", requirements: [{ type: "cost", value: 11000 }, { type: "money", value: 38000 }, { type: "office_rank_any", value: 1 }] },
|
||||
{ labelTr: "baron", requirements: [{ type: "branches", value: 4 }, { type: "cost", value: 16000 }, { type: "money", value: 55000 }, { type: "house_position", value: 2 }] },
|
||||
{ labelTr: "count", requirements: [{ type: "cost", value: 23000 }, { type: "money", value: 80000 }, { type: "reputation", value: 32 }, { type: "house_condition", value: 68 }] },
|
||||
{ labelTr: "palsgrave", requirements: [{ type: "cost", value: 32000 }, { type: "money", value: 115000 }, { type: "office_rank_any", value: 2 }, { type: "house_position", value: 3 }] },
|
||||
{ labelTr: "margrave", requirements: [{ type: "cost", value: 45000 }, { type: "money", value: 165000 }, { type: "reputation", value: 40 }, { type: "house_condition", value: 72 }, { type: "lover_count_max", value: 2 }] },
|
||||
{ labelTr: "landgrave", requirements: [{ type: "cost", value: 62000 }, { type: "money", value: 230000 }, { type: "office_rank_any", value: 3 }, { type: "house_position", value: 4 }] },
|
||||
{ labelTr: "ruler", requirements: [{ type: "cost", value: 85000 }, { type: "money", value: 320000 }, { type: "reputation", value: 48 }, { type: "house_condition", value: 76 }] },
|
||||
{ labelTr: "elector", requirements: [{ type: "cost", value: 115000 }, { type: "money", value: 440000 }, { type: "office_rank_any", value: 4 }, { type: "house_position", value: 5 }, { type: "lover_count_max", value: 2 }] },
|
||||
{ labelTr: "imperial-prince", requirements: [{ type: "cost", value: 155000 }, { type: "money", value: 600000 }, { type: "reputation", value: 56 }, { type: "house_condition", value: 80 }] },
|
||||
{ labelTr: "duke", requirements: [{ type: "cost", value: 205000 }, { type: "money", value: 820000 }, { type: "office_rank_any", value: 5 }, { type: "house_position", value: 6 }] },
|
||||
{ labelTr: "grand-duke",requirements: [{ type: "cost", value: 270000 }, { type: "money", value: 1120000 }, { type: "reputation", value: 64 }, { type: "house_condition", value: 84 }, { type: "lover_count_max", value: 1 }] },
|
||||
{ labelTr: "prince-regent", requirements: [{ type: "cost", value: 360000 }, { type: "money", value: 1520000 }, { type: "office_rank_any", value: 6 }, { type: "house_position", value: 7 }] },
|
||||
{ labelTr: "king", requirements: [{ type: "cost", value: 500000 }, { type: "money", value: 2100000 }, { type: "reputation", value: 72 }, { type: "house_position", value: 8 }, { type: "house_condition", value: 88 }, { type: "lover_count_max", value: 1 }] },
|
||||
];
|
||||
|
||||
const titles = await TitleOfNobility.findAll();
|
||||
@@ -325,13 +325,6 @@ async function initializeFalukantTitleRequirements() {
|
||||
const title = titles.find(t => t.labelTr === titleReq.labelTr);
|
||||
if (!title) continue;
|
||||
|
||||
if (i > 1) {
|
||||
titleReq.requirements.push({
|
||||
type: "money",
|
||||
value: 5000 * Math.pow(3, i - 1),
|
||||
});
|
||||
}
|
||||
|
||||
for (const req of titleReq.requirements) {
|
||||
requirementsToInsert.push({
|
||||
titleId: title.id,
|
||||
@@ -341,6 +334,7 @@ async function initializeFalukantTitleRequirements() {
|
||||
}
|
||||
}
|
||||
|
||||
await TitleRequirement.destroy({ where: {} });
|
||||
await TitleRequirement.bulkCreate(requirementsToInsert, { ignoreDuplicates: true });
|
||||
}
|
||||
|
||||
@@ -359,4 +353,4 @@ async function initializeFalukantTownProductWorth() {
|
||||
from \"falukant_type\".\"product\" ftp, \"falukant_data\".\"region\" fdr';
|
||||
sequelize.query(deleteQuery);
|
||||
sequelize.query(createQuery);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user