#ifndef USERCHARACTERWORKER_H #define USERCHARACTERWORKER_H #include "worker.h" #include class UserCharacterWorker : public Worker { public: UserCharacterWorker(ConnectionPool &pool, MessageBroker &broker); ~UserCharacterWorker() override; protected: void run() override; private: struct Character { int id; int age; int health; }; void processCharacterEvents(); void updateCharacterHealth(Character& character); void updateCharactersMood(); int calculateHealthChange(int age); void handleCharacterDeath(int characterId); void recalculateKnowledge(); void processPregnancies(); void handleCredits(); void setHeir(int characterId); int getFalukantUserId(int characterId); int getHeirFromChildren(int deceasedCharacterId); int getRandomHeir(int deceasedCharacterId); void setNewCharacter(int falukantUserId, int heirCharacterId); void setNewMoney(int falukantUserId, double newAmount); double getHouseValue(int falukantUserId); double getSettlementValue(int falukantUserId); double getInventoryValue(int falukantUserId); double getCreditDebt(int falukantUserId); double getCurrentMoney(int falukantUserId); double calculateNewMoney(int falukantUserId, bool hasHeir); int getChildCount(int deceasedUserId); std::random_device rd; std::mt19937 gen; std::uniform_real_distribution<> dist; bool didRunToday { false }; static constexpr const char *QUERY_GET_USERS_TO_UPDATE = R"( SELECT "id", CURRENT_DATE - birthdate::date AS age, "health" FROM "falukant_data"."character" WHERE "user_id" IS NOT NULL; )"; static constexpr const char *QUERY_UPDATE_CHARACTERS_HEALTH = R"( UPDATE "falukant_data"."character" SET health = $1 WHERE id = $2 )"; static constexpr const char *QUERY_UPDATE_GET_ITEMS_TO_UPDATE = R"( SELECT id, product_id, producer_id, quantity FROM falukant_log.production p WHERE p.production_timestamp::date < current_date )"; static constexpr const char *QUERY_UPDATE_GET_CHARACTER_IDS = R"( select fu.id user_id, c.id character_id, c2.id director_id from falukant_data.falukant_user fu join falukant_data."character" c on c.user_id = fu.id left join falukant_data.director d on d.employer_user_id = fu.id left join falukant_data."character" c2 on c2.id = d.director_character_id where fu.id = $1 )"; static constexpr const char *QUERY_UPDATE_KNOWLEDGE = R"( update falukant_data.knowledge set knowledge = least(knowledge + $3, 100) where character_id = $1 and product_id = $2 )"; static constexpr const char *QUERY_DELETE_LOG_ENTRY = R"( delete from falukant_log.production where id = $1 )"; static constexpr char const* QUERY_GET_PREGNANCY_CANDIDATES = R"( SELECT r.character1_id AS father_cid, r.character2_id AS mother_cid, c1.title_of_nobility, c1.last_name, c1.region_id, fu1.id AS father_uid, fu2.id AS mother_uid, -- Durchschnittsalter in Tagen ((NOW()::date - c1.birthdate::date) + (NOW()::date - c2.birthdate::date)) / 2 AS avg_age_days, -- Angepasste Schwangerschaftswahrscheinlichkeit in Prozent 100.0 / (1 + EXP( 0.0647 * ( ((NOW()::date - c1.birthdate::date) + (NOW()::date - c2.birthdate::date)) / 2 ) - 0.0591 ) ) AS prob_pct FROM falukant_data.relationship r JOIN falukant_type.relationship r2 ON r2.id = r.relationship_type_id AND r2.tr = 'married' JOIN falukant_data."character" c1 ON c1.id = r.character1_id JOIN falukant_data."character" c2 ON c2.id = r.character2_id LEFT JOIN falukant_data.falukant_user fu1 ON fu1.id = c1.user_id LEFT JOIN falukant_data.falukant_user fu2 ON fu2.id = c2.user_id WHERE random()*100 < ( 100.0 / (1 + EXP( 0.11166347 * ( ((NOW()::date - c1.birthdate::date) + (NOW()::date - c2.birthdate::date)) / 2 ) - 2.638267 ) ) ) / 2; -- Geburtenrate halbiert )"; static constexpr char const* QUERY_INSERT_CHILD = R"( INSERT INTO falukant_data."character" ( user_id, region_id, first_name, last_name, birthdate, gender, title_of_nobility, mood_id, created_at, updated_at ) VALUES ( NULL, $1::int, -- region_id /* zufälliger Vorname passend zum Gender */ ( SELECT id FROM falukant_predefine.firstname WHERE gender = $2 ORDER BY RANDOM() LIMIT 1 ), $3::int, -- last_name (Eltern-Nachname) NOW(), $2::varchar, -- gender $4::int, -- title_of_nobility /* zufällige Stimmung */ ( SELECT id FROM falukant_type.mood ORDER BY RANDOM() LIMIT 1 ), NOW(), NOW() ) RETURNING id AS child_cid )"; static constexpr char const* QUERY_INSERT_CHILD_RELATION = R"( -- QUERY_INSERT_CHILD_RELATION INSERT INTO falukant_data.child_relation ( father_character_id, mother_character_id, child_character_id, name_set, created_at, updated_at ) VALUES ( $1::int, -- father_cid $2::int, -- mother_cid $3::int, -- child_cid false, NOW(), NOW() ) RETURNING father_character_id, -- Vater-User (SELECT user_id FROM falukant_data."character" WHERE id = father_character_id) AS father_user_id, mother_character_id, -- Mutter-User (SELECT user_id FROM falukant_data."character" WHERE id = mother_character_id) AS mother_user_id, child_character_id, -- Kind-User (SELECT user_id FROM falukant_data."character" WHERE id = child_character_id) AS child_user_id; )"; static constexpr char const* QUERY_AUTOBATISM = R"( update falukant_data.child_relation set name_set = true where id in ( select cr.id from falukant_data.child_relation cr join falukant_data."character" c on c.id = cr.child_character_id where cr.name_set = false and c.birthdate < current_date - interval '5 days' ) )"; static constexpr char const* QUERY_UPDATE_MOOD = R"( UPDATE falukant_data."character" AS c SET mood_id = falukant_data.get_random_mood_id() WHERE c.health > 0; )"; static constexpr char const* QUERY_GET_OPEN_CREDITS = R"( select c.id credit_id, c.amount, c.remaining_amount, c.interest_rate, fu.id user_id, fu."money", c2.id character_id, dp.created_at debitor_prism_start, dp.created_at::date < current_date prism_started_previously from falukant_data.credit c join falukant_data.falukant_user fu on fu.id = c.id join falukant_data."character" c2 on c2.user_id = c.falukant_user_id left join falukant_data.debtors_prism dp on dp.character_id = c2.id where c.remaining_amount > 0 and c.updated_at::date < current_date )"; static constexpr char const* QUERY_UPDATE_CREDIT = R"( update falukant_data.credit c set remaining_amount = $1 where falukant_user_id = $2 )"; static constexpr char const* QUERY_CLEANUP_CREDITS = R"( delete from falukant_data.credit where remaining_amount >= 0.01 )"; static constexpr char const* QUERY_ADD_CHARACTER_TO_DEBTORS_PRISM = R"( insert into falukant_data.debtors_prism (character_id) values ($1) )"; static constexpr const char* QUERY_GET_HEIR = R"( SELECT child_character_id FROM falukant_data.child_relation WHERE father_character_id = $1 OR mother_character_id = $1 ORDER BY (is_heir IS TRUE) DESC, updated_at DESC LIMIT 1 )"; static constexpr const char* QUERY_RANDOM_HEIR = R"( WITH chosen AS ( SELECT cr.id AS relation_id, cr.child_character_id FROM falukant_data.child_relation AS cr JOIN falukant_data."character" AS ch ON ch.id = cr.child_character_id WHERE (cr.father_character_id = $1 OR cr.mother_character_id = $1) -- gleicher Wohnort wie der Verstorbene AND ch.region_id = ( SELECT region_id FROM falukant_data."character" WHERE id = $1 ) -- nicht älter als 10 Tage AND ch.birthdate >= NOW() - INTERVAL '10 days' -- Titel "noncivil" AND ch.title_of_nobility = ( SELECT id FROM falukant_type.title WHERE label_tr = 'noncivil' ) ORDER BY RANDOM() LIMIT 1 ) UPDATE falukant_data.child_relation AS cr2 SET is_heir = true, updated_at = NOW() FROM chosen WHERE cr2.id = chosen.relation_id RETURNING chosen.child_character_id )"; static constexpr const char* QUERY_SET_CHARACTER_USER = R"( UPDATE falukant_data."character" SET user_id = $1, updated_at = NOW() WHERE id = $2 )"; static constexpr const char* QUERY_UPDATE_USER_MONEY = R"( UPDATE falukant_data.falukant_user SET money = $1, updated_at = NOW() WHERE user_id = $2 )"; static constexpr const char* QUERY_GET_FALUKANT_USER_ID = R"( SELECT user_id FROM falukant_data."character" WHERE id = $1 LIMIT 1 )"; // Sub‐Queries static constexpr const char* QUERY_GET_CURRENT_MONEY = R"( SELECT COALESCE(money,0) AS sum FROM falukant_data.falukant_user WHERE user_id = $1 )"; static constexpr const char* QUERY_HOUSE_VALUE = R"( SELECT COALESCE(SUM(h.cost),0) AS sum FROM falukant_data.user_house AS uh JOIN falukant_type.house AS h ON uh.house_type_id = h.id WHERE uh.user_id = $1 )"; static constexpr const char* QUERY_SETTLEMENT_VALUE = R"( SELECT COALESCE(SUM(b.base_cost),0) AS sum FROM falukant_data.branch AS br JOIN falukant_type.branch AS b ON br.branch_type_id = b.id WHERE br.falukant_user_id = $1 )"; static constexpr const char* QUERY_INVENTORY_VALUE = R"( SELECT COALESCE(SUM(i.quantity * p.sell_cost),0) AS sum FROM falukant_data.inventory AS i JOIN falukant_type.product AS p ON i.product_id = p.id JOIN falukant_data.branch AS br ON i.stock_id = br.id WHERE br.falukant_user_id = $1 )"; static constexpr const char* QUERY_CREDIT_DEBT = R"( SELECT COALESCE(SUM(remaining_amount),0) AS sum FROM falukant_data.credit WHERE falukant_user_id = $1 )"; static constexpr const char* QUERY_COUNT_CHILDREN = R"( SELECT COUNT(*) AS cnt FROM falukant_data.child_relation WHERE father_character_id = $1 OR mother_character_id = $1 )"; // Queries zum Löschen von Character-Verknüpfungen beim Tod static constexpr const char *QUERY_DELETE_DIRECTOR = R"( DELETE FROM falukant_data.director WHERE director_character_id = $1 RETURNING employer_user_id; )"; static constexpr const char *QUERY_DELETE_RELATIONSHIP = R"( DELETE FROM falukant_data.relationship WHERE character1_id = $1 OR character2_id = $1; )"; static constexpr const char *QUERY_DELETE_CHILD_RELATION = R"( DELETE FROM falukant_data.child_relation WHERE child_character_id = $1 OR father_character_id = $1 OR mother_character_id = $1; )"; static constexpr const char *QUERY_DELETE_KNOWLEDGE = R"( DELETE FROM falukant_data.knowledge WHERE character_id = $1; )"; static constexpr const char *QUERY_DELETE_DEBTORS_PRISM = R"( DELETE FROM falukant_data.debtors_prism WHERE character_id = $1; )"; static constexpr const char *QUERY_DELETE_POLITICAL_OFFICE = R"( DELETE FROM falukant_data.political_office WHERE character_id = $1; )"; static constexpr const char *QUERY_DELETE_ELECTION_CANDIDATE = R"( DELETE FROM falukant_data.election_candidate WHERE character_id = $1; )"; }; #endif // USERCHARACTERWORKER_H