Compare commits
5 Commits
bad5b86951
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8f8fb8418a | ||
|
|
106c3950eb | ||
|
|
6ed88f7ad7 | ||
|
|
41c0e5a6f2 | ||
|
|
35377e3b59 |
0
.gitea/workflows/deploy-daemon.yml
Normal file → Executable file
0
.gitea/workflows/deploy-daemon.yml
Normal file → Executable file
0
.gitignore
vendored
Normal file → Executable file
0
.gitignore
vendored
Normal file → Executable file
0
.vscode/tasks.json
vendored
Normal file → Executable file
0
.vscode/tasks.json
vendored
Normal file → Executable file
2
Cargo.lock
generated
Normal file → Executable file
2
Cargo.lock
generated
Normal file → Executable file
@@ -11,7 +11,7 @@ dependencies = [
|
||||
"futures-util",
|
||||
"libsystemd",
|
||||
"postgres",
|
||||
"rand 0.8.5",
|
||||
"rand 0.9.2",
|
||||
"rustls-pemfile",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
||||
0
Cargo.toml
Normal file → Executable file
0
Cargo.toml
Normal file → Executable file
0
YpDaemon/Cargo.toml
Normal file → Executable file
0
YpDaemon/Cargo.toml
Normal file → Executable file
0
YpDaemon/src/worker/base.rs
Normal file → Executable file
0
YpDaemon/src/worker/base.rs
Normal file → Executable file
0
YpDaemon/src/worker/character_creation.rs
Normal file → Executable file
0
YpDaemon/src/worker/character_creation.rs
Normal file → Executable file
0
YpDaemon/src/worker/director.rs
Normal file → Executable file
0
YpDaemon/src/worker/director.rs
Normal file → Executable file
0
YpDaemon/src/worker/events.rs
Normal file → Executable file
0
YpDaemon/src/worker/events.rs
Normal file → Executable file
0
YpDaemon/src/worker/politics.rs
Normal file → Executable file
0
YpDaemon/src/worker/politics.rs
Normal file → Executable file
0
YpDaemon/src/worker/sql.rs
Normal file → Executable file
0
YpDaemon/src/worker/sql.rs
Normal file → Executable file
0
YpDaemon/src/worker/user_character.rs
Normal file → Executable file
0
YpDaemon/src/worker/user_character.rs
Normal file → Executable file
0
build_output.txt
Normal file → Executable file
0
build_output.txt
Normal file → Executable file
8
docs/DEBUG_DEATH_SUCCESSION_QUERIES.sql
Normal file → Executable file
8
docs/DEBUG_DEATH_SUCCESSION_QUERIES.sql
Normal file → Executable file
@@ -153,8 +153,8 @@ WITH removed AS (
|
||||
),
|
||||
archived_removed AS (
|
||||
INSERT INTO falukant_log.political_office_history
|
||||
(character_id, office_type_id, region_id, start_date, end_date)
|
||||
SELECT character_id, office_type_id, region_id, created_at, NOW()
|
||||
(character_id, office_type_id, region_id, start_date, end_date, created_at, updated_at)
|
||||
SELECT character_id, office_type_id, region_id, created_at, NOW(), NOW(), NOW()
|
||||
FROM removed
|
||||
),
|
||||
affected AS (
|
||||
@@ -183,8 +183,8 @@ to_delete AS (
|
||||
),
|
||||
archived_trim AS (
|
||||
INSERT INTO falukant_log.political_office_history
|
||||
(character_id, office_type_id, region_id, start_date, end_date)
|
||||
SELECT character_id, office_type_id, region_id, created_at, NOW()
|
||||
(character_id, office_type_id, region_id, start_date, end_date, created_at, updated_at)
|
||||
SELECT character_id, office_type_id, region_id, created_at, NOW(), NOW(), NOW()
|
||||
FROM to_delete
|
||||
)
|
||||
DELETE FROM falukant_data.political_office
|
||||
|
||||
0
docs/FALUKANT_CHURCH_DAEMON.md
Normal file → Executable file
0
docs/FALUKANT_CHURCH_DAEMON.md
Normal file → Executable file
0
docs/FALUKANT_DAEMON_AENDERUNGSNOTIZ_ZEITMASSSTAB.md
Normal file → Executable file
0
docs/FALUKANT_DAEMON_AENDERUNGSNOTIZ_ZEITMASSSTAB.md
Normal file → Executable file
0
docs/FALUKANT_DAEMON_HANDOFF.md
Normal file → Executable file
0
docs/FALUKANT_DAEMON_HANDOFF.md
Normal file → Executable file
0
docs/FALUKANT_DEATH_SUCCESSION_SMOKE_TEST.md
Normal file → Executable file
0
docs/FALUKANT_DEATH_SUCCESSION_SMOKE_TEST.md
Normal file → Executable file
0
docs/FALUKANT_DEBTORS_DAEMON.md
Normal file → Executable file
0
docs/FALUKANT_DEBTORS_DAEMON.md
Normal file → Executable file
0
docs/FALUKANT_DIRECTOR_AUTO_ADJUST_INCOME.md
Normal file → Executable file
0
docs/FALUKANT_DIRECTOR_AUTO_ADJUST_INCOME.md
Normal file → Executable file
0
docs/FALUKANT_DIRECTOR_PRODUCTION_COST.md
Normal file → Executable file
0
docs/FALUKANT_DIRECTOR_PRODUCTION_COST.md
Normal file → Executable file
0
docs/FALUKANT_MARRIAGE_HOUSEPEACE_DAEMON_HANDOFF.md
Normal file → Executable file
0
docs/FALUKANT_MARRIAGE_HOUSEPEACE_DAEMON_HANDOFF.md
Normal file → Executable file
0
docs/FALUKANT_PRODUCTION_CERTIFICATE.md
Normal file → Executable file
0
docs/FALUKANT_PRODUCTION_CERTIFICATE.md
Normal file → Executable file
0
docs/FALUKANT_SERVANTS_DAEMON.md
Normal file → Executable file
0
docs/FALUKANT_SERVANTS_DAEMON.md
Normal file → Executable file
0
docs/FALUKANT_TRANSPORT_RAID_DAEMON.md
Normal file → Executable file
0
docs/FALUKANT_TRANSPORT_RAID_DAEMON.md
Normal file → Executable file
0
docs/FALUKANT_UI_WEBSOCKET.md
Normal file → Executable file
0
docs/FALUKANT_UI_WEBSOCKET.md
Normal file → Executable file
0
docs/FALUKANT_UNDERGROUND_INVESTIGATE_AFFAIR.md
Normal file → Executable file
0
docs/FALUKANT_UNDERGROUND_INVESTIGATE_AFFAIR.md
Normal file → Executable file
0
docs/FALUKANT_WORKER_SCHEDULE_SOCKET.md
Normal file → Executable file
0
docs/FALUKANT_WORKER_SCHEDULE_SOCKET.md
Normal file → Executable file
0
docs/LOVER_STAND_EFFECTS.md
Normal file → Executable file
0
docs/LOVER_STAND_EFFECTS.md
Normal file → Executable file
0
migrations/001_falukant_family_lovers.sql
Normal file → Executable file
0
migrations/001_falukant_family_lovers.sql
Normal file → Executable file
0
migrations/002_falukant_family_rename_legacy_columns.sql
Normal file → Executable file
0
migrations/002_falukant_family_rename_legacy_columns.sql
Normal file → Executable file
0
migrations/003_falukant_family_marriage_buffs.sql
Normal file → Executable file
0
migrations/003_falukant_family_marriage_buffs.sql
Normal file → Executable file
0
migrations/004_falukant_servants_daemon.sql
Normal file → Executable file
0
migrations/004_falukant_servants_daemon.sql
Normal file → Executable file
0
migrations/005_falukant_marriage_housepeace.sql
Normal file → Executable file
0
migrations/005_falukant_marriage_housepeace.sql
Normal file → Executable file
0
migrations/006_falukant_lover_installments.sql
Normal file → Executable file
0
migrations/006_falukant_lover_installments.sql
Normal file → Executable file
0
migrations/007_falukant_character_church_career.sql
Normal file → Executable file
0
migrations/007_falukant_character_church_career.sql
Normal file → Executable file
0
migrations/008_falukant_marriage_pregnancy_due.sql
Normal file → Executable file
0
migrations/008_falukant_marriage_pregnancy_due.sql
Normal file → Executable file
0
migrations/008_falukant_transport_raid.sql
Normal file → Executable file
0
migrations/008_falukant_transport_raid.sql
Normal file → Executable file
0
migrations/009_falukant_region_display_name.sql
Normal file → Executable file
0
migrations/009_falukant_region_display_name.sql
Normal file → Executable file
0
migrations/010_falukant_marriage_started_at.sql
Normal file → Executable file
0
migrations/010_falukant_marriage_started_at.sql
Normal file → Executable file
0
migrations/011_falukant_character_planned_pregnancy.sql
Normal file → Executable file
0
migrations/011_falukant_character_planned_pregnancy.sql
Normal file → Executable file
0
migrations/012_falukant_political_benefits_daemon.sql
Normal file → Executable file
0
migrations/012_falukant_political_benefits_daemon.sql
Normal file → Executable file
0
migrations/013_falukant_political_daily_salary.sql
Normal file → Executable file
0
migrations/013_falukant_political_daily_salary.sql
Normal file → Executable file
0
migrations/014_falukant_certificate_productions_count_since.sql
Normal file → Executable file
0
migrations/014_falukant_certificate_productions_count_since.sql
Normal file → Executable file
0
migrations/015_falukant_log_production_completion_count.sql
Normal file → Executable file
0
migrations/015_falukant_log_production_completion_count.sql
Normal file → Executable file
9
migrations/016_falukant_log_political_office_history.sql
Normal file → Executable file
9
migrations/016_falukant_log_political_office_history.sql
Normal file → Executable file
@@ -8,13 +8,20 @@ CREATE TABLE IF NOT EXISTS falukant_log.political_office_history (
|
||||
region_id INTEGER,
|
||||
start_date TIMESTAMPTZ NOT NULL,
|
||||
end_date TIMESTAMPTZ NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Tabelle kann bereits vom Backend existieren (ohne region_id o. Ä.): CREATE TABLE IF NOT EXISTS ergänzt keine Spalten.
|
||||
ALTER TABLE falukant_log.political_office_history
|
||||
ADD COLUMN IF NOT EXISTS region_id INTEGER;
|
||||
|
||||
ALTER TABLE falukant_log.political_office_history
|
||||
ADD COLUMN IF NOT EXISTS created_at TIMESTAMPTZ NOT NULL DEFAULT NOW();
|
||||
|
||||
ALTER TABLE falukant_log.political_office_history
|
||||
ADD COLUMN IF NOT EXISTS updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW();
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_pol_office_hist_character
|
||||
ON falukant_log.political_office_history (character_id);
|
||||
|
||||
|
||||
0
migrations/017_falukant_money_precision_14_2.sql
Normal file → Executable file
0
migrations/017_falukant_money_precision_14_2.sql
Normal file → Executable file
0
migrations/018_worker_schedule_socket_right.sql
Normal file → Executable file
0
migrations/018_worker_schedule_socket_right.sql
Normal file → Executable file
0
migrations/README.md
Normal file → Executable file
0
migrations/README.md
Normal file → Executable file
0
renovate.json
Normal file → Executable file
0
renovate.json
Normal file → Executable file
0
src/config.rs
Normal file → Executable file
0
src/config.rs
Normal file → Executable file
0
src/db/connection.rs
Normal file → Executable file
0
src/db/connection.rs
Normal file → Executable file
0
src/db/mod.rs
Normal file → Executable file
0
src/db/mod.rs
Normal file → Executable file
0
src/main.rs
Normal file → Executable file
0
src/main.rs
Normal file → Executable file
0
src/message_broker.rs
Normal file → Executable file
0
src/message_broker.rs
Normal file → Executable file
0
src/websocket_server.rs
Normal file → Executable file
0
src/websocket_server.rs
Normal file → Executable file
0
src/worker/base.rs
Normal file → Executable file
0
src/worker/base.rs
Normal file → Executable file
21
src/worker/character_creation.rs
Normal file → Executable file
21
src/worker/character_creation.rs
Normal file → Executable file
@@ -1,8 +1,7 @@
|
||||
use crate::db::{ConnectionPool, DbError, Rows};
|
||||
use crate::message_broker::MessageBroker;
|
||||
use rand::distributions::{Distribution, Uniform};
|
||||
use rand::rngs::StdRng;
|
||||
use rand::{thread_rng, Rng, SeedableRng};
|
||||
use rand::{Rng, SeedableRng};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Arc;
|
||||
@@ -29,7 +28,6 @@ use crate::worker::sql::{
|
||||
pub struct CharacterCreationWorker {
|
||||
pub(crate) base: BaseWorker,
|
||||
rng: StdRng,
|
||||
dist: Uniform<i32>,
|
||||
first_name_cache: HashMap<String, HashSet<i32>>,
|
||||
last_name_cache: HashSet<i32>,
|
||||
death_check_running: Arc<AtomicBool>,
|
||||
@@ -46,8 +44,8 @@ impl CharacterCreationWorker {
|
||||
/// Interner Konstruktor, der optional den NPC-Todes-Monitor startet.
|
||||
fn new_internal(pool: ConnectionPool, broker: MessageBroker, start_death_thread: bool) -> Self {
|
||||
let base = BaseWorker::new("CharacterCreationWorker", pool.clone(), broker.clone());
|
||||
let rng = StdRng::from_entropy();
|
||||
let dist = Uniform::from(2..=3);
|
||||
let mut base_rng = rand::rng();
|
||||
let rng = StdRng::from_rng(&mut base_rng);
|
||||
let death_check_running = Arc::new(AtomicBool::new(start_death_thread));
|
||||
|
||||
let death_thread = if start_death_thread {
|
||||
@@ -80,7 +78,6 @@ impl CharacterCreationWorker {
|
||||
Self {
|
||||
base,
|
||||
rng,
|
||||
dist,
|
||||
first_name_cache: HashMap::new(),
|
||||
last_name_cache: HashSet::new(),
|
||||
death_check_running,
|
||||
@@ -139,7 +136,7 @@ impl CharacterCreationWorker {
|
||||
|
||||
for &nobility in &nobility_stands {
|
||||
for &gender in &genders {
|
||||
let num_chars = self.rng.sample(self.dist);
|
||||
let num_chars = self.rng.random_range(2..=3);
|
||||
for _ in 0..num_chars {
|
||||
self.create_character(region_id, gender, nobility);
|
||||
}
|
||||
@@ -248,8 +245,8 @@ impl CharacterCreationWorker {
|
||||
if set.is_empty() {
|
||||
return -1;
|
||||
}
|
||||
let mut rng = thread_rng();
|
||||
let idx = rng.gen_range(0..set.len());
|
||||
let mut rng = rand::rng();
|
||||
let idx = rng.random_range(0..set.len());
|
||||
*set.iter().nth(idx).unwrap_or(&-1)
|
||||
}
|
||||
|
||||
@@ -390,9 +387,8 @@ impl CharacterCreationWorker {
|
||||
let death_probability =
|
||||
base_probability + increase_per_year * (age.saturating_sub(60) as f64);
|
||||
|
||||
let mut rng = thread_rng();
|
||||
let dist = Uniform::from(0.0..1.0);
|
||||
let roll: f64 = dist.sample(&mut rng);
|
||||
let mut rng = rand::rng();
|
||||
let roll: f64 = rng.random_range(0.0..1.0);
|
||||
roll < death_probability
|
||||
}
|
||||
|
||||
@@ -537,4 +533,3 @@ impl CharacterCreationWorker {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
0
src/worker/death_log.rs
Normal file → Executable file
0
src/worker/death_log.rs
Normal file → Executable file
4
src/worker/director.rs
Normal file → Executable file
4
src/worker/director.rs
Normal file → Executable file
@@ -193,7 +193,7 @@ impl DirectorWorker {
|
||||
.filter_map(Self::map_row_to_resignation_candidate)
|
||||
.collect();
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
let mut rng = rand::rng();
|
||||
for candidate in candidates {
|
||||
let sat = candidate.satisfaction.clamp(0, 100) as f64;
|
||||
let resignation_probability = 1.0 - (sat / 100.0);
|
||||
@@ -214,7 +214,7 @@ impl DirectorWorker {
|
||||
);
|
||||
let _ = conn.execute("insert_notification", &[&candidate.employer_user_id, &payload]);
|
||||
}
|
||||
let roll: f64 = rng.gen_range(0.0..1.0);
|
||||
let roll: f64 = rng.random_range(0.0..1.0);
|
||||
if roll >= resignation_probability {
|
||||
continue;
|
||||
}
|
||||
|
||||
42
src/worker/events.rs
Normal file → Executable file
42
src/worker/events.rs
Normal file → Executable file
@@ -1,7 +1,7 @@
|
||||
use crate::db::{ConnectionPool, DbConnection, DbError};
|
||||
use crate::message_broker::MessageBroker;
|
||||
use rand::Rng;
|
||||
use rand::seq::SliceRandom;
|
||||
use rand::prelude::SliceRandom;
|
||||
use serde_json::json;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::Arc;
|
||||
@@ -447,7 +447,7 @@ impl EventsWorker {
|
||||
|
||||
fn run_loop(pool: ConnectionPool, broker: MessageBroker, state: Arc<WorkerState>) {
|
||||
let mut last_event_check = None;
|
||||
let mut rng = rand::thread_rng();
|
||||
let mut rng = rand::rng();
|
||||
let events = Self::initialize_events();
|
||||
|
||||
loop {
|
||||
@@ -502,7 +502,7 @@ impl EventsWorker {
|
||||
// Prüfe jedes mögliche Ereignis
|
||||
for event in events {
|
||||
// Zufällige Prüfung basierend auf Wahrscheinlichkeit
|
||||
let roll = rng.gen_range(0.0..=1.0);
|
||||
let roll = rng.random_range(0.0..=1.0);
|
||||
let effective_prob = event.probability_per_minute * Self::EVENT_RATE_SCALE;
|
||||
if roll < effective_prob {
|
||||
eprintln!(
|
||||
@@ -560,7 +560,7 @@ impl EventsWorker {
|
||||
// Wende Effekte an
|
||||
let mut effect_results = Vec::new();
|
||||
for effect in &event.effects {
|
||||
let effect_roll = rng.gen_range(0.0..=1.0);
|
||||
let effect_roll = rng.random_range(0.0..=1.0);
|
||||
match effect {
|
||||
EventEffect::MoneyChange {
|
||||
probability,
|
||||
@@ -750,7 +750,7 @@ impl EventsWorker {
|
||||
// Wende Effekte an (in diesem Fall nur CharacterDeath)
|
||||
let mut effect_results = Vec::new();
|
||||
for effect in &event.effects {
|
||||
let effect_roll = rng.gen_range(0.0..=1.0);
|
||||
let effect_roll = rng.random_range(0.0..=1.0);
|
||||
match effect {
|
||||
EventEffect::CharacterDeath { probability } => {
|
||||
if effect_roll < *probability
|
||||
@@ -835,7 +835,7 @@ impl EventsWorker {
|
||||
let mut price_change_note: Option<serde_json::Value> = None;
|
||||
let mut production_quality_note: Option<serde_json::Value> = None;
|
||||
for effect in &event.effects {
|
||||
let effect_roll = rng.gen_range(0.0..=1.0);
|
||||
let effect_roll = rng.random_range(0.0..=1.0);
|
||||
match effect {
|
||||
EventEffect::WeatherChange { probability } => {
|
||||
if effect_roll < *probability {
|
||||
@@ -851,7 +851,7 @@ impl EventsWorker {
|
||||
max_change,
|
||||
} => {
|
||||
if effect_roll < *probability {
|
||||
let change = rng.gen_range(*min_change..=*max_change);
|
||||
let change = rng.random_range(*min_change..=*max_change);
|
||||
Self::apply_production_quality_change(&mut conn, region_id, change)?;
|
||||
effect_results.push(json!({
|
||||
"type": "production_quality_change",
|
||||
@@ -879,7 +879,7 @@ impl EventsWorker {
|
||||
max_percent,
|
||||
} => {
|
||||
if effect_roll < *probability {
|
||||
let percent_change = rng.gen_range(*min_percent..=*max_percent);
|
||||
let percent_change = rng.random_range(*min_percent..=*max_percent);
|
||||
Self::apply_price_change(&mut conn, region_id, percent_change)?;
|
||||
effect_results.push(json!({
|
||||
"type": "price_change",
|
||||
@@ -907,7 +907,7 @@ impl EventsWorker {
|
||||
max_percent,
|
||||
} => {
|
||||
if effect_roll < *probability {
|
||||
let percent_change = rng.gen_range(*min_percent..=*max_percent);
|
||||
let percent_change = rng.random_range(*min_percent..=*max_percent);
|
||||
Self::apply_transport_speed_change(&mut conn, region_id, percent_change)?;
|
||||
effect_results.push(json!({
|
||||
"type": "transport_speed_change",
|
||||
@@ -1218,7 +1218,7 @@ impl EventsWorker {
|
||||
}
|
||||
|
||||
// Berechne die prozentuale Änderung
|
||||
let percent_change = rng.gen_range(min_percent..=max_percent);
|
||||
let percent_change = rng.random_range(min_percent..=max_percent);
|
||||
|
||||
// Reduziere die Kapazität aller Stocks
|
||||
conn.prepare("update_stock_capacity_regional", QUERY_UPDATE_STOCK_CAPACITY_REGIONAL)?;
|
||||
@@ -1283,7 +1283,7 @@ impl EventsWorker {
|
||||
}
|
||||
|
||||
// Berechne die Änderung
|
||||
let quality_change = rng.gen_range(min_change..=max_change);
|
||||
let quality_change = rng.random_range(min_change..=max_change);
|
||||
|
||||
// Reduziere die Qualität aller Häuser
|
||||
conn.prepare("update_house_quality", QUERY_UPDATE_HOUSE_QUALITY)?;
|
||||
@@ -1393,7 +1393,7 @@ impl EventsWorker {
|
||||
|
||||
// Spezialfall: Unerwarteter Geldsegen -> absoluter Zufallsbetrag 1..500
|
||||
if event.id == "windfall" {
|
||||
let absolute_change: f64 = rng.gen_range(1.0..=500.0);
|
||||
let absolute_change: f64 = rng.random_range(1.0..=500.0);
|
||||
let percent_change = if current_money > 0.0 {
|
||||
(absolute_change / current_money) * 100.0
|
||||
} else {
|
||||
@@ -1430,7 +1430,7 @@ impl EventsWorker {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let loss: f64 = rng.gen_range(1.0..=max_loss);
|
||||
let loss: f64 = rng.random_range(1.0..=max_loss);
|
||||
let percent_change = -(loss / current_money) * 100.0;
|
||||
let absolute_change = -loss;
|
||||
|
||||
@@ -1456,7 +1456,7 @@ impl EventsWorker {
|
||||
}
|
||||
|
||||
// Standardfall: prozentuale Änderung, optional mit Mindestbetrag für positive Änderungen
|
||||
let percent_change = rng.gen_range(min_percent..=max_percent);
|
||||
let percent_change = rng.random_range(min_percent..=max_percent);
|
||||
let computed_change = current_money * (percent_change / 100.0);
|
||||
let effective_change = match min_absolute_positive {
|
||||
Some(min_abs) if percent_change > 0.0 && computed_change < *min_abs => *min_abs,
|
||||
@@ -1491,7 +1491,7 @@ impl EventsWorker {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let percent_change = rng.gen_range(min_percent..=max_percent);
|
||||
let percent_change = rng.random_range(min_percent..=max_percent);
|
||||
Self::apply_storage_capacity_change(conn, user_id, percent_change)?;
|
||||
|
||||
Ok(Some(json!({
|
||||
@@ -1744,7 +1744,7 @@ impl EventsWorker {
|
||||
.and_then(|v| v.parse::<i32>().ok())
|
||||
.unwrap_or(100);
|
||||
|
||||
let health_change = rng.gen_range(min_change..=max_change);
|
||||
let health_change = rng.random_range(min_change..=max_change);
|
||||
// Durch Unfälle/Krankheiten soll Gesundheit von Spieler-Charakteren nicht auf 0 fallen
|
||||
let new_health = (current_health + health_change).clamp(1, 100);
|
||||
|
||||
@@ -1816,7 +1816,7 @@ impl EventsWorker {
|
||||
.and_then(|v| v.parse::<i32>().ok())
|
||||
.unwrap_or(100);
|
||||
|
||||
let health_change = rng.gen_range(min_change..=max_change);
|
||||
let health_change = rng.random_range(min_change..=max_change);
|
||||
let user_id: Option<i32> = row
|
||||
.get("user_id")
|
||||
.and_then(|v| v.parse::<i32>().ok());
|
||||
@@ -2235,9 +2235,9 @@ impl EventsWorker {
|
||||
|
||||
// 3. Berechne Schäden
|
||||
let inventory_damage_percent =
|
||||
rng.gen_range(params.inventory_damage_min_percent..=params.inventory_damage_max_percent);
|
||||
rng.random_range(params.inventory_damage_min_percent..=params.inventory_damage_max_percent);
|
||||
let storage_destruction_percent =
|
||||
rng.gen_range(params.storage_destruction_min_percent..=params.storage_destruction_max_percent);
|
||||
rng.random_range(params.storage_destruction_min_percent..=params.storage_destruction_max_percent);
|
||||
|
||||
let total_stocks = stock_rows.len();
|
||||
let stocks_to_destroy_requested = ((total_stocks as f64 * storage_destruction_percent / 100.0)
|
||||
@@ -2455,9 +2455,9 @@ impl EventsWorker {
|
||||
|
||||
// 3. Berechne Schäden
|
||||
let inventory_damage_percent =
|
||||
rng.gen_range(params.inventory_damage_min_percent..=params.inventory_damage_max_percent);
|
||||
rng.random_range(params.inventory_damage_min_percent..=params.inventory_damage_max_percent);
|
||||
let storage_destruction_percent =
|
||||
rng.gen_range(params.storage_destruction_min_percent..=params.storage_destruction_max_percent);
|
||||
rng.random_range(params.storage_destruction_min_percent..=params.storage_destruction_max_percent);
|
||||
|
||||
let total_stocks = stock_rows.len();
|
||||
let stocks_to_destroy_requested = ((total_stocks as f64 * storage_destruction_percent / 100.0)
|
||||
|
||||
0
src/worker/falukant_certificate.rs
Normal file → Executable file
0
src/worker/falukant_certificate.rs
Normal file → Executable file
0
src/worker/falukant_debtors.rs
Normal file → Executable file
0
src/worker/falukant_debtors.rs
Normal file → Executable file
56
src/worker/falukant_family.rs
Normal file → Executable file
56
src/worker/falukant_family.rs
Normal file → Executable file
@@ -12,9 +12,8 @@ use std::sync::atomic::Ordering;
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use rand::distributions::{Distribution, Uniform};
|
||||
use rand::rngs::StdRng;
|
||||
use rand::SeedableRng;
|
||||
use rand::{Rng, SeedableRng};
|
||||
|
||||
use super::base::{BaseWorker, Worker, WorkerState};
|
||||
use super::sql::{
|
||||
@@ -46,7 +45,6 @@ const GAME_MONTH_SLICE_INTERVAL: Duration = Duration::from_secs(2 * 3600);
|
||||
pub struct FalukantFamilyWorker {
|
||||
base: BaseWorker,
|
||||
rng: StdRng,
|
||||
dist: Uniform<f64>,
|
||||
last_daily: Option<Instant>,
|
||||
last_monthly: Option<Instant>,
|
||||
/// Gemeinsamer Tick für Liebschafts-Raten + Dienerschaft (Monatstick ≈ 2 h).
|
||||
@@ -63,8 +61,10 @@ impl FalukantFamilyWorker {
|
||||
pub fn new(pool: ConnectionPool, broker: MessageBroker) -> Self {
|
||||
Self {
|
||||
base: BaseWorker::new("FalukantFamilyWorker", pool, broker),
|
||||
rng: StdRng::from_entropy(),
|
||||
dist: Uniform::from(0.0..1.0),
|
||||
rng: {
|
||||
let mut base_rng = rand::rng();
|
||||
StdRng::from_rng(&mut base_rng)
|
||||
},
|
||||
last_daily: None,
|
||||
last_monthly: None,
|
||||
last_game_month_slice: None,
|
||||
@@ -266,6 +266,9 @@ impl FalukantFamilyWorker {
|
||||
});
|
||||
|
||||
let mut v_net = 0i32;
|
||||
if l.min_age_years <= 12 {
|
||||
v_net += 3;
|
||||
}
|
||||
if l.maintenance_level < 35 {
|
||||
v_net += 1;
|
||||
}
|
||||
@@ -289,6 +292,9 @@ impl FalukantFamilyWorker {
|
||||
let new_vis = clamp_i32(l.visibility + v_net, 0, 100);
|
||||
|
||||
let mut d_net = 0i32;
|
||||
if l.min_age_years <= 12 {
|
||||
d_net -= 3;
|
||||
}
|
||||
if l.maintenance_level >= 70 {
|
||||
d_net += 1;
|
||||
}
|
||||
@@ -361,7 +367,7 @@ impl FalukantFamilyWorker {
|
||||
}
|
||||
}
|
||||
}
|
||||
if breakup_risk_pct > 0.0 && self.dist.sample(&mut self.rng) * 100.0 < breakup_risk_pct {
|
||||
if breakup_risk_pct > 0.0 && self.rng.random_range(0.0..1.0) * 100.0 < breakup_risk_pct {
|
||||
conn.execute("deactivate_lover", &[&l.rel_id])?;
|
||||
ended_rel_ids.insert(l.rel_id);
|
||||
push_user_id(&mut breakup_socket_users, l.user1_id);
|
||||
@@ -452,8 +458,8 @@ impl FalukantFamilyWorker {
|
||||
{
|
||||
delta -= 1;
|
||||
}
|
||||
if touching.iter().any(|l| l.min_age_years <= 15) {
|
||||
delta -= 1;
|
||||
if touching.iter().any(|l| l.min_age_years <= 12) {
|
||||
delta -= 4;
|
||||
}
|
||||
// Standsabhängige Deltas aus der Liebschaften-Verarbeitung anwenden
|
||||
if let Some(extra_delta) = spouse_satisfaction_delta.get(&m.id) {
|
||||
@@ -499,8 +505,8 @@ impl FalukantFamilyWorker {
|
||||
if (g1 as i32 - g2 as i32).abs() >= 2 {
|
||||
pst -= 1;
|
||||
}
|
||||
if touching.iter().any(|l| l.min_age_years <= 15) {
|
||||
pst -= 2;
|
||||
if touching.iter().any(|l| l.min_age_years <= 12) {
|
||||
pst -= 4;
|
||||
}
|
||||
let ho_avg = (m.household_order_1 + m.household_order_2) / 2;
|
||||
if ho_avg < 35 {
|
||||
@@ -676,7 +682,7 @@ impl FalukantFamilyWorker {
|
||||
p -= 2.0;
|
||||
}
|
||||
p = p.clamp(0.0, 25.0);
|
||||
if self.dist.sample(&mut self.rng) * 100.0 < p {
|
||||
if self.rng.random_range(0.0..1.0) * 100.0 < p {
|
||||
self.base.broker.publish(format!(
|
||||
r#"{{"event":"falukant_family_scandal_hint","relationship_id":{}}}"#,
|
||||
l.rel_id
|
||||
@@ -709,7 +715,7 @@ impl FalukantFamilyWorker {
|
||||
}
|
||||
let mut rng_malus_cids: Vec<i32> = Vec::new();
|
||||
for cid in unique_cids.iter().copied() {
|
||||
let r = self.dist.sample(&mut self.rng) * 100.0;
|
||||
let r = self.rng.random_range(0.0..1.0) * 100.0;
|
||||
let malus = if r < 1.0 {
|
||||
-5.0
|
||||
} else if r < 3.0 {
|
||||
@@ -1307,12 +1313,10 @@ fn rank_rep_modifier(g: u8, scandal: bool, order_ok: bool) -> f64 {
|
||||
|
||||
/// Spec 5a: Altersmalus (jüngeres Alter der Beteiligten), zusätzlich zur Basis-Reputation.
|
||||
fn age_reputation_delta(min_age_years: i32) -> f64 {
|
||||
if min_age_years <= 13 {
|
||||
-1.5
|
||||
} else if min_age_years <= 15 {
|
||||
-0.8
|
||||
} else if min_age_years <= 17 {
|
||||
-0.3
|
||||
if min_age_years <= 10 {
|
||||
-6.0
|
||||
} else if min_age_years <= 12 {
|
||||
-3.0
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
@@ -1320,8 +1324,10 @@ fn age_reputation_delta(min_age_years: i32) -> f64 {
|
||||
|
||||
/// Spec 5a: minAge <= 15 und hohe Sichtbarkeit.
|
||||
fn visibility_young_penalty(min_age_years: i32, visibility: i32) -> f64 {
|
||||
if min_age_years <= 15 && visibility >= 50 {
|
||||
-0.5
|
||||
if min_age_years <= 10 && visibility >= 20 {
|
||||
-4.0
|
||||
} else if min_age_years <= 12 && visibility >= 25 {
|
||||
-2.0
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
@@ -1414,12 +1420,10 @@ fn breakup_risk_percent(l: &LoverData) -> f64 {
|
||||
|
||||
/// Spec Skandalrisiko: stufenweise Zusatz (exklusiv).
|
||||
fn scandal_age_extra_pct(min_age_years: i32) -> f64 {
|
||||
if min_age_years <= 13 {
|
||||
6.0
|
||||
} else if min_age_years <= 15 {
|
||||
3.0
|
||||
} else if min_age_years <= 17 {
|
||||
1.0
|
||||
if min_age_years <= 10 {
|
||||
12.0
|
||||
} else if min_age_years <= 12 {
|
||||
8.0
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
|
||||
0
src/worker/falukant_servants.rs
Normal file → Executable file
0
src/worker/falukant_servants.rs
Normal file → Executable file
14
src/worker/falukant_transport_raid.rs
Normal file → Executable file
14
src/worker/falukant_transport_raid.rs
Normal file → Executable file
@@ -177,19 +177,19 @@ fn try_resolve_one_raid(
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
let mut rng = rand::rng();
|
||||
|
||||
// Begegnung (Bande + Sichtbarkeit großer Ladung)
|
||||
let visibility_penalty = ((transport_size as f64 / vehicle_cap as f64).min(2.0) - 1.0) * 0.04;
|
||||
let encounter_p = (0.08_f64 + band_size as f64 * 0.012 + visibility_penalty).clamp(0.05, 0.45);
|
||||
if !rng.gen_bool(encounter_p) {
|
||||
if !rng.random_bool(encounter_p) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Kampf
|
||||
let raid_power = band_size + rng.gen_range(0..=band_size);
|
||||
let raid_power = band_size + rng.random_range(0..=band_size);
|
||||
let gmax = guard_count.max(1);
|
||||
let guard_power = guard_count + rng.gen_range(0..=gmax);
|
||||
let guard_power = guard_count + rng.random_range(0..=gmax);
|
||||
|
||||
let outcome = if raid_power > (guard_power as f64 * 1.25) as i32 {
|
||||
"major_success"
|
||||
@@ -218,15 +218,15 @@ fn try_resolve_one_raid(
|
||||
|
||||
// Beuteanteil (nie Totalverlust)
|
||||
let base_loot = match outcome {
|
||||
"major_success" => rng.gen_range(0.35..0.60),
|
||||
_ => rng.gen_range(0.15..0.45),
|
||||
"major_success" => rng.random_range(0.35..0.60),
|
||||
_ => rng.random_range(0.15..0.45),
|
||||
};
|
||||
let guard_mul = (1.0_f64 - 0.05_f64 * (guard_count.min(12) as f64)).max(0.35);
|
||||
let loot_share = (base_loot * guard_mul).clamp(0.05, 0.55);
|
||||
let mut stolen = ((transport_size as f64) * loot_share).floor() as i32;
|
||||
stolen = stolen.clamp(1, transport_size - 1);
|
||||
|
||||
let carry_loss = rng.gen_range(0.65..0.90);
|
||||
let carry_loss = rng.random_range(0.65..0.90);
|
||||
let mut to_store = (stolen as f64 * carry_loss).floor() as i32;
|
||||
to_store = to_store.max(0).min(stolen);
|
||||
|
||||
|
||||
0
src/worker/house.rs
Normal file → Executable file
0
src/worker/house.rs
Normal file → Executable file
0
src/worker/mod.rs
Normal file → Executable file
0
src/worker/mod.rs
Normal file → Executable file
0
src/worker/notify.rs
Normal file → Executable file
0
src/worker/notify.rs
Normal file → Executable file
0
src/worker/political_benefits.rs
Normal file → Executable file
0
src/worker/political_benefits.rs
Normal file → Executable file
0
src/worker/politics.rs
Normal file → Executable file
0
src/worker/politics.rs
Normal file → Executable file
0
src/worker/produce.rs
Normal file → Executable file
0
src/worker/produce.rs
Normal file → Executable file
0
src/worker/simple.rs
Normal file → Executable file
0
src/worker/simple.rs
Normal file → Executable file
157
src/worker/sql.rs
Normal file → Executable file
157
src/worker/sql.rs
Normal file → Executable file
@@ -1000,7 +1000,16 @@ SELECT child_character_id FROM falukant_data.child_relation WHERE father_charact
|
||||
"#;
|
||||
|
||||
pub const QUERY_SET_CHARACTER_USER: &str = r#"
|
||||
UPDATE falukant_data.character SET user_id = $1, updated_at = NOW() WHERE id = $2;
|
||||
WITH cleared AS (
|
||||
UPDATE falukant_data.character
|
||||
SET user_id = NULL,
|
||||
updated_at = NOW()
|
||||
WHERE user_id = $1
|
||||
)
|
||||
UPDATE falukant_data.character
|
||||
SET user_id = $1,
|
||||
updated_at = NOW()
|
||||
WHERE id = $2;
|
||||
"#;
|
||||
|
||||
pub const QUERY_GET_CURRENT_MONEY: &str = r#"
|
||||
@@ -1134,8 +1143,8 @@ pub const QUERY_SELECT_NEEDED_ELECTIONS: &str = r#"
|
||||
WHERE (po.created_at + (pot.term_length * INTERVAL '1 day'))::date = td.election_date
|
||||
),
|
||||
archived_expiring AS (
|
||||
INSERT INTO falukant_log.political_office_history (character_id, office_type_id, region_id, start_date, end_date)
|
||||
SELECT character_id, office_type_id, region_id, start_date, end_date
|
||||
INSERT INTO falukant_log.political_office_history (character_id, office_type_id, region_id, start_date, end_date, created_at, updated_at)
|
||||
SELECT character_id, office_type_id, region_id, start_date, end_date, NOW(), NOW()
|
||||
FROM offices_ending_today
|
||||
),
|
||||
expired_today AS (
|
||||
@@ -1265,8 +1274,8 @@ pub const QUERY_PROCESS_EXPIRED_AND_SCHEDULE: &str = r#"
|
||||
WHERE (po.created_at + (pot.term_length * INTERVAL '1 day')) <= NOW()
|
||||
),
|
||||
archived_expired AS (
|
||||
INSERT INTO falukant_log.political_office_history (character_id, office_type_id, region_id, start_date, end_date)
|
||||
SELECT character_id, office_type_id, region_id, start_date, term_end
|
||||
INSERT INTO falukant_log.political_office_history (character_id, office_type_id, region_id, start_date, end_date, created_at, updated_at)
|
||||
SELECT character_id, office_type_id, region_id, start_date, term_end, NOW(), NOW()
|
||||
FROM doomed
|
||||
),
|
||||
expired_offices AS (
|
||||
@@ -1653,8 +1662,8 @@ pub const QUERY_TRIM_EXCESS_OFFICES_GLOBAL: &str = r#"
|
||||
WHERE rn > seats_total
|
||||
),
|
||||
archived_trim AS (
|
||||
INSERT INTO falukant_log.political_office_history (character_id, office_type_id, region_id, start_date, end_date)
|
||||
SELECT character_id, office_type_id, region_id, created_at, NOW()
|
||||
INSERT INTO falukant_log.political_office_history (character_id, office_type_id, region_id, start_date, end_date, created_at, updated_at)
|
||||
SELECT character_id, office_type_id, region_id, created_at, NOW(), NOW(), NOW()
|
||||
FROM to_delete
|
||||
)
|
||||
DELETE FROM falukant_data.political_office
|
||||
@@ -2161,13 +2170,7 @@ pub const QUERY_TRY_MARRIAGE_CONCEPTION_UPDATE: &str = r#"
|
||||
CASE WHEN c1.gender = 'male' THEN c1.region_id ELSE c2.region_id END AS region_id,
|
||||
CASE WHEN c1.gender = 'male' THEN fu1.id ELSE fu2.id END AS father_uid,
|
||||
CASE WHEN c1.gender = 'female' THEN fu1.id ELSE fu2.id END AS mother_uid,
|
||||
(CURRENT_DATE - c_female.birthdate::date)::int AS mother_age_days,
|
||||
(
|
||||
SELECT COUNT(*)::int
|
||||
FROM falukant_data.child_relation cr
|
||||
WHERE cr.father_character_id = (CASE WHEN c1.gender = 'male' THEN c1.id ELSE c2.id END)
|
||||
AND cr.mother_character_id = (CASE WHEN c1.gender = 'female' THEN c1.id ELSE c2.id END)
|
||||
) AS shared_children_count
|
||||
(CURRENT_DATE - c_female.birthdate::date)::int AS mother_age_days
|
||||
FROM falukant_data.relationship r
|
||||
JOIN falukant_type.relationship r2
|
||||
ON r2.id = r.relationship_type_id AND r2.tr = 'married'
|
||||
@@ -2194,58 +2197,13 @@ pub const QUERY_TRY_MARRIAGE_CONCEPTION_UPDATE: &str = r#"
|
||||
p.father_uid,
|
||||
p.mother_uid,
|
||||
p.mother_age_days,
|
||||
p.shared_children_count,
|
||||
CASE
|
||||
WHEN p.mother_age_days < 4380 THEN 0.005
|
||||
WHEN p.mother_age_days < 4745 THEN 0.30
|
||||
WHEN p.mother_age_days < 5110 THEN 0.45
|
||||
WHEN p.mother_age_days < 5475 THEN 0.55
|
||||
WHEN p.mother_age_days < 5840 THEN 0.60
|
||||
WHEN p.mother_age_days < 6205 THEN 0.725
|
||||
WHEN p.mother_age_days < 6570 THEN 0.80
|
||||
WHEN p.mother_age_days < 7305 THEN 0.855
|
||||
WHEN p.mother_age_days < 9125 THEN 0.875
|
||||
WHEN p.mother_age_days < 10950 THEN 0.84
|
||||
WHEN p.mother_age_days < 11315 THEN 0.785
|
||||
WHEN p.mother_age_days < 11680 THEN 0.765
|
||||
WHEN p.mother_age_days < 12045 THEN 0.74
|
||||
WHEN p.mother_age_days < 12410 THEN 0.72
|
||||
WHEN p.mother_age_days < 12775 THEN 0.695
|
||||
WHEN p.mother_age_days < 13140 THEN 0.65
|
||||
WHEN p.mother_age_days < 13505 THEN 0.63
|
||||
WHEN p.mother_age_days < 13870 THEN 0.60
|
||||
WHEN p.mother_age_days < 14235 THEN 0.55
|
||||
WHEN p.mother_age_days < 14600 THEN 0.50
|
||||
WHEN p.mother_age_days < 14965 THEN 0.45
|
||||
WHEN p.mother_age_days < 15330 THEN 0.35
|
||||
WHEN p.mother_age_days < 15695 THEN 0.25
|
||||
WHEN p.mother_age_days < 16060 THEN 0.15
|
||||
WHEN p.mother_age_days < 16425 THEN 0.075
|
||||
WHEN p.mother_age_days < 16790 THEN 0.03
|
||||
WHEN p.mother_age_days < 17155 THEN 0.02
|
||||
WHEN p.mother_age_days < 17520 THEN 0.015
|
||||
WHEN p.mother_age_days < 18250 THEN 0.005
|
||||
ELSE 0.001
|
||||
END AS prob_year_raw
|
||||
0.72::float AS prob_year
|
||||
FROM paired p
|
||||
),
|
||||
mother_age_final AS (
|
||||
SELECT
|
||||
ma.*,
|
||||
CASE
|
||||
WHEN ma.shared_children_count = 0
|
||||
AND ma.mother_age_days >= 6570
|
||||
AND ma.mother_age_days < 16000
|
||||
THEN GREATEST(ma.prob_year_raw, 1.0)
|
||||
ELSE ma.prob_year_raw
|
||||
END AS prob_year
|
||||
FROM mother_age ma
|
||||
),
|
||||
conceivable AS (
|
||||
SELECT ma.relationship_id
|
||||
FROM mother_age_final ma
|
||||
WHERE ma.mother_age_days >= 4380
|
||||
AND ma.mother_age_days < 18993
|
||||
FROM mother_age ma
|
||||
WHERE ma.mother_age_days >= 5110
|
||||
AND random() < ma.prob_year
|
||||
)
|
||||
UPDATE falukant_data.relationship r
|
||||
@@ -2275,13 +2233,7 @@ pub const QUERY_GET_LEGACY_MARRIAGE_INSTANT_PREGNANCY_CANDIDATES: &str = r#"
|
||||
CASE WHEN c1.gender = 'male' THEN c1.region_id ELSE c2.region_id END AS region_id,
|
||||
CASE WHEN c1.gender = 'male' THEN fu1.id ELSE fu2.id END AS father_uid,
|
||||
CASE WHEN c1.gender = 'female' THEN fu1.id ELSE fu2.id END AS mother_uid,
|
||||
(CURRENT_DATE - c_female.birthdate::date)::int AS mother_age_days,
|
||||
(
|
||||
SELECT COUNT(*)::int
|
||||
FROM falukant_data.child_relation cr
|
||||
WHERE cr.father_character_id = (CASE WHEN c1.gender = 'male' THEN c1.id ELSE c2.id END)
|
||||
AND cr.mother_character_id = (CASE WHEN c1.gender = 'female' THEN c1.id ELSE c2.id END)
|
||||
) AS shared_children_count
|
||||
(CURRENT_DATE - c_female.birthdate::date)::int AS mother_age_days
|
||||
FROM falukant_data.relationship r
|
||||
JOIN falukant_type.relationship r2
|
||||
ON r2.id = r.relationship_type_id AND r2.tr = 'married'
|
||||
@@ -2312,52 +2264,8 @@ pub const QUERY_GET_LEGACY_MARRIAGE_INSTANT_PREGNANCY_CANDIDATES: &str = r#"
|
||||
p.father_uid,
|
||||
p.mother_uid,
|
||||
p.mother_age_days,
|
||||
p.shared_children_count,
|
||||
CASE
|
||||
WHEN p.mother_age_days < 4380 THEN 0.005
|
||||
WHEN p.mother_age_days < 4745 THEN 0.30
|
||||
WHEN p.mother_age_days < 5110 THEN 0.45
|
||||
WHEN p.mother_age_days < 5475 THEN 0.55
|
||||
WHEN p.mother_age_days < 5840 THEN 0.60
|
||||
WHEN p.mother_age_days < 6205 THEN 0.725
|
||||
WHEN p.mother_age_days < 6570 THEN 0.80
|
||||
WHEN p.mother_age_days < 7305 THEN 0.855
|
||||
WHEN p.mother_age_days < 9125 THEN 0.875
|
||||
WHEN p.mother_age_days < 10950 THEN 0.84
|
||||
WHEN p.mother_age_days < 11315 THEN 0.785
|
||||
WHEN p.mother_age_days < 11680 THEN 0.765
|
||||
WHEN p.mother_age_days < 12045 THEN 0.74
|
||||
WHEN p.mother_age_days < 12410 THEN 0.72
|
||||
WHEN p.mother_age_days < 12775 THEN 0.695
|
||||
WHEN p.mother_age_days < 13140 THEN 0.65
|
||||
WHEN p.mother_age_days < 13505 THEN 0.63
|
||||
WHEN p.mother_age_days < 13870 THEN 0.60
|
||||
WHEN p.mother_age_days < 14235 THEN 0.55
|
||||
WHEN p.mother_age_days < 14600 THEN 0.50
|
||||
WHEN p.mother_age_days < 14965 THEN 0.45
|
||||
WHEN p.mother_age_days < 15330 THEN 0.35
|
||||
WHEN p.mother_age_days < 15695 THEN 0.25
|
||||
WHEN p.mother_age_days < 16060 THEN 0.15
|
||||
WHEN p.mother_age_days < 16425 THEN 0.075
|
||||
WHEN p.mother_age_days < 16790 THEN 0.03
|
||||
WHEN p.mother_age_days < 17155 THEN 0.02
|
||||
WHEN p.mother_age_days < 17520 THEN 0.015
|
||||
WHEN p.mother_age_days < 18250 THEN 0.005
|
||||
ELSE 0.001
|
||||
END AS prob_year_raw
|
||||
0.72::float AS prob_year
|
||||
FROM paired p
|
||||
),
|
||||
mother_age_final AS (
|
||||
SELECT
|
||||
ma.*,
|
||||
CASE
|
||||
WHEN ma.shared_children_count = 0
|
||||
AND ma.mother_age_days >= 6570
|
||||
AND ma.mother_age_days < 16000
|
||||
THEN GREATEST(ma.prob_year_raw, 1.0)
|
||||
ELSE ma.prob_year_raw
|
||||
END AS prob_year
|
||||
FROM mother_age ma
|
||||
)
|
||||
SELECT
|
||||
father_cid,
|
||||
@@ -2369,9 +2277,8 @@ pub const QUERY_GET_LEGACY_MARRIAGE_INSTANT_PREGNANCY_CANDIDATES: &str = r#"
|
||||
mother_uid,
|
||||
mother_age_days,
|
||||
prob_year * 100.0 AS prob_pct
|
||||
FROM mother_age_final
|
||||
WHERE mother_age_days >= 4380
|
||||
AND mother_age_days < 18993
|
||||
FROM mother_age
|
||||
WHERE mother_age_days >= 5110
|
||||
AND random() < prob_year;
|
||||
"#;
|
||||
|
||||
@@ -2532,8 +2439,8 @@ pub const QUERY_DELETE_POLITICAL_OFFICE: &str = r#"
|
||||
RETURNING character_id, office_type_id, region_id, created_at
|
||||
),
|
||||
archived_removed AS (
|
||||
INSERT INTO falukant_log.political_office_history (character_id, office_type_id, region_id, start_date, end_date)
|
||||
SELECT character_id, office_type_id, region_id, created_at, NOW()
|
||||
INSERT INTO falukant_log.political_office_history (character_id, office_type_id, region_id, start_date, end_date, created_at, updated_at)
|
||||
SELECT character_id, office_type_id, region_id, created_at, NOW(), NOW(), NOW()
|
||||
FROM removed
|
||||
),
|
||||
affected AS (
|
||||
@@ -2575,8 +2482,8 @@ pub const QUERY_DELETE_POLITICAL_OFFICE: &str = r#"
|
||||
WHERE rn > seats_total
|
||||
),
|
||||
archived_trim AS (
|
||||
INSERT INTO falukant_log.political_office_history (character_id, office_type_id, region_id, start_date, end_date)
|
||||
SELECT character_id, office_type_id, region_id, created_at, NOW()
|
||||
INSERT INTO falukant_log.political_office_history (character_id, office_type_id, region_id, start_date, end_date, created_at, updated_at)
|
||||
SELECT character_id, office_type_id, region_id, created_at, NOW(), NOW(), NOW()
|
||||
FROM to_delete
|
||||
)
|
||||
DELETE FROM falukant_data.political_office
|
||||
@@ -3927,8 +3834,7 @@ pub const QUERY_GET_LOVER_PREGNANCY_CANDIDATES: &str = r#"
|
||||
WHERE cr.mother_character_id = c_female.id
|
||||
AND cr.created_at::date = CURRENT_DATE
|
||||
)
|
||||
AND (CURRENT_DATE - c_female.birthdate::date) >= 4380
|
||||
AND (CURRENT_DATE - c_female.birthdate::date) < 18993
|
||||
AND (CURRENT_DATE - c_female.birthdate::date) >= 5110
|
||||
AND random() * 100.0 < (
|
||||
LEAST(12.0, GREATEST(0.0,
|
||||
CASE rs.lover_role
|
||||
@@ -3939,11 +3845,6 @@ pub const QUERY_GET_LOVER_PREGNANCY_CANDIDATES: &str = r#"
|
||||
END
|
||||
+ CASE WHEN rs.affection >= 75 THEN 2.0 ELSE 0.0 END
|
||||
+ CASE WHEN rs.visibility >= 70 AND rs.affection < 50 THEN -2.0 ELSE 0.0 END
|
||||
+ CASE
|
||||
WHEN (CURRENT_DATE - c_female.birthdate::date) > 14600
|
||||
THEN -3.0
|
||||
ELSE 0.0
|
||||
END
|
||||
))
|
||||
);
|
||||
"#;
|
||||
|
||||
10
src/worker/stockage_manager.rs
Normal file → Executable file
10
src/worker/stockage_manager.rs
Normal file → Executable file
@@ -1,8 +1,6 @@
|
||||
use crate::db::{ConnectionPool, DbError};
|
||||
use crate::message_broker::MessageBroker;
|
||||
use rand::distributions::{Distribution, Uniform};
|
||||
use rand::rngs::StdRng;
|
||||
use rand::SeedableRng;
|
||||
use rand::Rng;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, Instant};
|
||||
@@ -64,8 +62,7 @@ impl StockageManager {
|
||||
}
|
||||
|
||||
fn add_local_stocks(pool: &ConnectionPool, broker: &MessageBroker) -> Result<(), DbError> {
|
||||
let mut rng = StdRng::from_entropy();
|
||||
let dist = Uniform::from(0.0..1.0);
|
||||
let mut rng = rand::rng();
|
||||
|
||||
let town_ids = Self::get_town_ids(pool)?;
|
||||
eprintln!("[StockageManager] Prüfe {} Städte auf neue Lager", town_ids.len());
|
||||
@@ -76,7 +73,7 @@ impl StockageManager {
|
||||
// Das bedeutet: 2/217 ≈ 0.92% pro Stadt pro Durchlauf
|
||||
// Pro Tag (1440 Durchläufe): ~73% Chance pro Stadt
|
||||
// Erhöht von 2/2161 (0.0926%) auf 2/217 (0.92%) = 10x höher
|
||||
let roll: f64 = dist.sample(&mut rng) * 216.0_f64;
|
||||
let roll: f64 = rng.random_range(0.0..1.0) * 216.0_f64;
|
||||
let chance = roll.round();
|
||||
if chance <= 1.0 {
|
||||
eprintln!("[StockageManager] Erstelle Lager für Stadt {}", town_id);
|
||||
@@ -185,4 +182,3 @@ impl Worker for StockageManager {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
0
src/worker/transport.rs
Normal file → Executable file
0
src/worker/transport.rs
Normal file → Executable file
29
src/worker/underground.rs
Normal file → Executable file
29
src/worker/underground.rs
Normal file → Executable file
@@ -1,7 +1,6 @@
|
||||
use crate::db::{ConnectionPool, DbError, Row, Rows};
|
||||
use crate::message_broker::MessageBroker;
|
||||
use rand::distributions::{Distribution, Uniform};
|
||||
use rand::seq::SliceRandom;
|
||||
use rand::prelude::{IndexedRandom, SliceRandom};
|
||||
use rand::Rng;
|
||||
use serde_json::json;
|
||||
use serde_json::Value as Json;
|
||||
@@ -388,9 +387,8 @@ impl UndergroundWorker {
|
||||
}
|
||||
|
||||
let current = parse_i32(&rows[0], "health", 0);
|
||||
let mut rng = rand::thread_rng();
|
||||
let dist = Uniform::from(0..=current.max(0));
|
||||
let new_health = dist.sample(&mut rng);
|
||||
let mut rng = rand::rng();
|
||||
let new_health = rng.random_range(0..=current.max(0));
|
||||
|
||||
conn.execute("ug_update_char_health", &[&victim_id, &new_health])?;
|
||||
|
||||
@@ -705,7 +703,7 @@ impl UndergroundWorker {
|
||||
}));
|
||||
}
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
let mut rng = rand::rng();
|
||||
let mut to_remove = random_ll(1, cap);
|
||||
rows.shuffle(&mut rng);
|
||||
|
||||
@@ -867,7 +865,7 @@ impl UndergroundWorker {
|
||||
let cap = max(1_i64, total / 2);
|
||||
let mut to_remove = random_ll(1, cap);
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
let mut rng = rand::rng();
|
||||
rows.shuffle(&mut rng);
|
||||
|
||||
let mut affected = Vec::new();
|
||||
@@ -1107,7 +1105,7 @@ impl UndergroundWorker {
|
||||
}
|
||||
let pick = *best_indices
|
||||
.as_slice()
|
||||
.choose(&mut rand::thread_rng())
|
||||
.choose(&mut rand::rng())
|
||||
.unwrap_or(&scored[0].1);
|
||||
let target = &lover_rows[pick];
|
||||
|
||||
@@ -1542,18 +1540,18 @@ fn parse_opt_i32(row: &Row, key: &str) -> Option<i32> {
|
||||
// Hilfsfunktionen für Zufall und Parsing
|
||||
|
||||
fn random_int(lo: i32, hi: i32) -> i32 {
|
||||
let mut rng = rand::thread_rng();
|
||||
rng.gen_range(lo..=hi)
|
||||
let mut rng = rand::rng();
|
||||
rng.random_range(lo..=hi)
|
||||
}
|
||||
|
||||
fn random_ll(lo: i64, hi: i64) -> i64 {
|
||||
let mut rng = rand::thread_rng();
|
||||
rng.gen_range(lo..=hi)
|
||||
let mut rng = rand::rng();
|
||||
rng.random_range(lo..=hi)
|
||||
}
|
||||
|
||||
fn random_indices(n: usize, k: usize) -> Vec<usize> {
|
||||
let mut idx: Vec<usize> = (0..n).collect();
|
||||
let mut rng = rand::thread_rng();
|
||||
let mut rng = rand::rng();
|
||||
idx.shuffle(&mut rng);
|
||||
if k < idx.len() {
|
||||
idx.truncate(k);
|
||||
@@ -1562,8 +1560,8 @@ fn random_indices(n: usize, k: usize) -> Vec<usize> {
|
||||
}
|
||||
|
||||
fn random_double(lo: f64, hi: f64) -> f64 {
|
||||
let mut rng = rand::thread_rng();
|
||||
rng.gen_range(lo..hi)
|
||||
let mut rng = rand::rng();
|
||||
rng.random_range(lo..hi)
|
||||
}
|
||||
|
||||
fn parse_i32(row: &Row, key: &str, default: i32) -> i32 {
|
||||
@@ -1620,4 +1618,3 @@ fn change_falukant_user_money(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
|
||||
25
src/worker/user_character.rs
Normal file → Executable file
25
src/worker/user_character.rs
Normal file → Executable file
@@ -1,9 +1,8 @@
|
||||
use crate::db::{ConnectionPool, DbError, Rows};
|
||||
use crate::message_broker::MessageBroker;
|
||||
use chrono::{Local, NaiveDate};
|
||||
use rand::distributions::{Distribution, Uniform};
|
||||
use rand::rngs::StdRng;
|
||||
use rand::SeedableRng;
|
||||
use rand::{Rng, SeedableRng};
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, Instant};
|
||||
@@ -68,7 +67,6 @@ struct Character {
|
||||
pub struct UserCharacterWorker {
|
||||
base: BaseWorker,
|
||||
rng: StdRng,
|
||||
dist: Uniform<f64>,
|
||||
last_hourly_run: Option<Instant>,
|
||||
last_pregnancy_run: Option<Instant>,
|
||||
/// Letzter Kalendertag, an dem Ehe-Konzeption (oder Legacy-Instant) gelaufen ist — höchstens 1×/Tag.
|
||||
@@ -86,13 +84,12 @@ pub struct UserCharacterWorker {
|
||||
impl UserCharacterWorker {
|
||||
pub fn new(pool: ConnectionPool, broker: MessageBroker) -> Self {
|
||||
let base = BaseWorker::new("UserCharacterWorker", pool, broker);
|
||||
let rng = StdRng::from_entropy();
|
||||
let dist = Uniform::from(0.0..1.0);
|
||||
let mut base_rng = rand::rng();
|
||||
let rng = StdRng::from_rng(&mut base_rng);
|
||||
|
||||
Self {
|
||||
base,
|
||||
rng,
|
||||
dist,
|
||||
last_hourly_run: None,
|
||||
last_pregnancy_run: None,
|
||||
last_marriage_fertility_date: None,
|
||||
@@ -192,7 +189,7 @@ impl UserCharacterWorker {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Ehe: Geburten/stale stündlich; Konzeption höchstens einmal pro Kalendertag (ein Jahreswurf, `prob_year`).
|
||||
/// Ehe/geplante Geburten zeitnah; Konzeption höchstens einmal pro Kalendertag (ein Jahreswurf, `prob_year`).
|
||||
/// Konzeption setzt `marriage_pregnancy_due_at` (+18 Stunden); Geburt über `QUERY_GET_MARRIAGE_BIRTH_DELIVERIES`.
|
||||
/// Kein separates „Hochzeitsnacht“-Event — erste Konzeption am nächsten Tageslauf nach der Hochzeit.
|
||||
fn maybe_run_hourly_pregnancies(&mut self) {
|
||||
@@ -201,7 +198,7 @@ impl UserCharacterWorker {
|
||||
|
||||
let run_hourly = match self.last_pregnancy_run {
|
||||
None => true,
|
||||
Some(last) => now.saturating_duration_since(last) >= Duration::from_secs(3600),
|
||||
Some(last) => now.saturating_duration_since(last) >= Duration::from_secs(60),
|
||||
};
|
||||
|
||||
let run_daily_fertility = self.last_marriage_fertility_date != Some(today);
|
||||
@@ -301,15 +298,14 @@ impl UserCharacterWorker {
|
||||
|
||||
if age >= 45 {
|
||||
let probability = (0.1 + (age - 45) as f64 * 0.02).min(1.0);
|
||||
if self.dist.sample(&mut self.rng) < probability {
|
||||
let damage_dist = Uniform::from(1..=10);
|
||||
return -damage_dist.sample(&mut self.rng);
|
||||
if self.rng.random_range(0.0..1.0) < probability {
|
||||
return -self.rng.random_range(1..=10);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
let probability = (age - 30) as f64 / 30.0;
|
||||
if self.dist.sample(&mut self.rng) < probability {
|
||||
if self.rng.random_range(0.0..1.0) < probability {
|
||||
-1
|
||||
} else {
|
||||
0
|
||||
@@ -686,7 +682,7 @@ impl UserCharacterWorker {
|
||||
.filter(|s| !s.is_empty())
|
||||
.unwrap_or("marriage");
|
||||
|
||||
let gender = if self.dist.sample(&mut self.rng) < 0.5 {
|
||||
let gender = if self.rng.random_range(0.0..1.0) < 0.5 {
|
||||
"male"
|
||||
} else {
|
||||
"female"
|
||||
@@ -741,7 +737,7 @@ impl UserCharacterWorker {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let gender = if self.dist.sample(&mut self.rng) < 0.5 {
|
||||
let gender = if self.rng.random_range(0.0..1.0) < 0.5 {
|
||||
"male"
|
||||
} else {
|
||||
"female"
|
||||
@@ -1263,4 +1259,3 @@ impl Worker for UserCharacterWorker {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
0
src/worker/value_recalculation.rs
Normal file → Executable file
0
src/worker/value_recalculation.rs
Normal file → Executable file
0
src/worker/weather.rs
Normal file → Executable file
0
src/worker/weather.rs
Normal file → Executable file
Reference in New Issue
Block a user