1use std::collections::HashMap;
23
24use rpfm_lib::error::Result;
25use rpfm_lib::files::esf::{hash_map_order, NodeType, RecordNode, VecI32Node, VecU32Node};
26
27use super::plan::{CaiPlan, ManagerIds};
28use super::stances::StartingStances;
29use super::CAI_PATH;
30use crate::startpos::layout::{child_path, variant_path, GroupBuilder, RecordBuilder, StartposLayout};
31use crate::startpos::writer::{bool_node, i32_node, u32_node};
32
33#[cfg(test)] #[path = "managers_test.rs"] mod managers_test;
34
35pub const FACTION_DESIRES: [u32; 28] = [39, 44, 37, 41, 38, 40, 34, 94, 108, 150, 23, 82, 152, 144, 114, 141, 140, 25, 171, 98, 175, 185, 84, 173, 183, 90, 88, 159];
37
38pub const HUMAN_DESIRES: [u32; 20] = [39, 44, 37, 41, 38, 40, 34, 94, 108, 150, 23, 82, 152, 144, 114, 141, 140, 25, 159, 90];
40
41pub const REBEL_DESIRES: usize = 18;
43
44pub(crate) const FACTION_LEVEL: u32 = 7;
46
47pub(crate) const HUMAN_LEVEL: u32 = 6;
49
50pub(crate) const REBEL_LEVEL: u32 = 2;
52
53const NON_AI_POOL_VALUE: u32 = 3;
55
56const HUMAN_PRIORITIES: [(u32, f32); 2] = [(TAXATION, 500.0), (CONSTRUCTION, 100.0)];
58
59const POOL_SLOTS: [(u32, u32); 17] = [
61 (3, 23), (4, 82), (5, 152), (6, 144), (7, 114), (8, 141), (9, 140), (20, 39), (23, 44), (25, 37), (26, 41), (27, 38),
62 (30, 40), (32, 34), (36, 94), (37, 108), (38, 150),
63];
64
65const LAST_POOL_SLOT: u32 = 38;
67
68const COMPONENT_HASH_SEED: u32 = 0x4a54_5eed;
70
71const CAI_FACTION_USERS: [u32; 3] = [114, 141, 144];
73
74const TARGET_LIST: u32 = 25;
76const FINANCE_MANAGER: u32 = 82;
77const RESEARCH: u32 = 88;
78const TAXATION: u32 = 90;
79const FINANCIAL_ANALYSER: u32 = 94;
80const TMS_COORDINATION: u32 = 140;
81const STANCE_MANAGER: u32 = 144;
82const STRATEGIC_ANALYSER: u32 = 150;
83const DIPLOMACY_MANAGER: u32 = 152;
84const TASK_GENERATOR: u32 = 114;
85const CONSTRUCTION: u32 = 159;
86const FORCE_SKILLS: u32 = 171;
87const CHARACTER_SKILLS: u32 = 173;
88const FINANCE_SAVINGS: u32 = 84;
89
90const FINANCIAL_ANALYSIS: u32 = 95;
92const STRATEGIC_CONTEXT_ANALYSIS: u32 = 151;
93
94const FACTION_INTENTION: u32 = 79;
96const FORCE_SKILLS_INTENTION: u32 = 172;
97const CHARACTER_SKILLS_INTENTION: u32 = 174;
98
99pub(crate) const OWNER_LINK: (f32, f32, u32) = (0.0, 0.0, 1);
101
102const INTENTION_LINK: (f32, f32, u32) = (0.0, 1.0, 0);
104
105const FRESH_FLAGS: u32 = 97;
107
108const DIPLOMACY_FLAGS: (u32, u32) = (96, 369);
110
111const NO_WARS_VALUE: i32 = -100;
113
114const DIPLOMACY_ENTRY_PAIRS: u32 = 41;
116
117const DIPLOMACY_WAR_STATE: u32 = 3;
119
120const STRATEGIC_CONTEXT: u32 = 4;
123
124const STANCE_SCORES: usize = 7;
126
127const BUDGET_CATEGORIES: usize = 6;
129
130#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
136pub enum ManagerKind {
137
138 #[default]
140 Ai,
141
142 Human,
145
146 Rebels,
148}
149
150#[derive(Debug, Clone, Copy)]
152pub struct ManagerInputs<'a> {
153
154 pub layout: &'a StartposLayout,
156
157 pub plan: &'a CaiPlan,
159
160 pub stances: &'a StartingStances,
162
163 pub characters: &'a [Vec<u32>],
165
166 pub mobiles: &'a [Vec<u32>],
168
169 pub start_year: u32,
171}
172
173#[derive(Debug, Clone, Default)]
175pub(crate) struct ComponentFields {
176
177 pub(crate) flags: Option<u32>,
179
180 pub(crate) priority: Option<f32>,
182
183 pub(crate) users: Option<Vec<u32>>,
185
186 pub(crate) links: Vec<(u32, (f32, f32, u32))>,
188
189 pub(crate) references: Vec<u32>,
191}
192
193struct ManagerContext<'a> {
195 inputs: &'a ManagerInputs<'a>,
196 ids: &'a ManagerIds,
197 kind: ManagerKind,
198
199 faction: u32,
201 cai_faction: u32,
202
203 desires: HashMap<u32, u32>,
205}
206
207impl ManagerKind {
212
213 pub fn desires(self) -> &'static [u32] {
215 match self {
216 Self::Ai => &FACTION_DESIRES,
217 Self::Human => &HUMAN_DESIRES,
218 Self::Rebels => &FACTION_DESIRES[..REBEL_DESIRES],
219 }
220 }
221
222 pub fn level(self) -> u32 {
224 match self {
225 Self::Ai => FACTION_LEVEL,
226 Self::Human => HUMAN_LEVEL,
227 Self::Rebels => REBEL_LEVEL,
228 }
229 }
230}
231
232pub fn write_managers(inputs: &ManagerInputs) -> Result<RecordNode> {
242 let path = managers_path();
243 let mut list = inputs.layout.builder(&path)?;
244 for ids in inputs.plan.managers() {
245 let faction = &inputs.plan.factions()[ids.faction()];
246 let context = ManagerContext {
247 inputs,
248 ids,
249 faction: faction.faction(),
250 cai_faction: faction.cai_faction(),
251 kind: inputs.plan.manager_kinds()[ids.faction()],
252 desires: inputs.plan.manager_kinds()[ids.faction()].desires().iter().copied().zip(ids.desires().iter().copied()).collect(),
253 };
254 list.push_group()?
255 .set_record("CAI_BDI_POOL", Some(context.write_pool()?))?
256 .set_record("CAI_FACTION_BDI_POOL", Some(context.write_pool_slots()?))?
257 .set_record("CAI_FACTION_MANAGER", Some(context.write_faction_manager()?))?;
258 }
259 list.build()
260}
261
262pub(crate) fn pool_component(layout: &StartposLayout, variant: &str, id: u32, fields: ComponentFields) -> Result<RecordNode> {
275 let path = child_path(variant, "CAI_BDI_COMPONENT");
276 let mut ownership = layout.builder(&child_path(&path, "CAI_BDI_COMPONENT_BLOCK_OWNS"))?;
277 for (linked, (first, second, flag)) in &fields.links {
278 ownership.push_group()?
279 .set_u32("^+1", *linked)?
280 .set_u32("^+2", id)?
281 .set_f32("^+3", *first)?
282 .set_f32("^+4", *second)?
283 .set_u32("^+5", *flag)?;
284 }
285
286 let mut component = layout.builder(&path)?;
287 let group = component.group()?;
288 group.set_u32("^+1", id)?
289 .set_record("CAI_BDI_COMPONENT_BLOCK_OWNS", Some(ownership.build()?))?
290 .set_u32s("CAI_BDI_COMPONENT_BLOCK_OWNS+1", fields.references)?;
291 if let Some(users) = fields.users {
292 group.set_u32s("^+5", users)?;
293 }
294 if let Some(flags) = fields.flags {
295 group.set_u32("^+4", flags)?;
296 }
297 if let Some(priority) = fields.priority {
298 group.set_f32("^+2", priority)?;
299 }
300 component.build()
301}
302
303pub(crate) fn empty_analyser(layout: &StartposLayout, variant: &str) -> Result<Option<RecordNode>> {
315 let path = child_path(variant, "CAI_ANALYSER");
316 if layout.record(&path).is_none() {
317 return Ok(None);
318 }
319 let mut analyser = layout.builder(&path)?;
320 analyser.group()?
321 .set_u32s("^+1", vec![])?
322 .set("^+2", NodeType::U64Array(vec![]))?;
323 analyser.build().map(Some)
324}
325
326fn managers_path() -> String {
328 child_path(CAI_PATH, "CAI_INTERFACE_MANAGERS")
329}
330
331pub(crate) fn component_map_order(components: &[u32]) -> Vec<usize> {
334 hash_map_order(&components.iter().map(|component| component ^ COMPONENT_HASH_SEED).collect::<Vec<_>>())
335}
336
337fn empty_u32s() -> NodeType {
339 NodeType::U32Array(VecU32Node::default())
340}
341
342impl ManagerContext<'_> {
343
344
345 fn desire(&self, code: u32) -> u32 {
347 self.desires.get(&code).copied().unwrap_or_default()
348 }
349
350 fn write_pool(&self) -> Result<RecordNode> {
352 let layout = self.inputs.layout;
353 let path = child_path(&managers_path(), "CAI_BDI_POOL");
354
355 let mut desires = layout.builder(&child_path(&path, "CAI_BDI_POOL_DESIRES"))?;
356 let ids = self.ids.desires();
357 for index in component_map_order(ids) {
358 self.write_desire(&mut desires, self.kind.desires()[index], ids[index])?;
359 }
360
361 let mut beliefs = layout.builder(&child_path(&path, "CAI_BDI_POOL_BELIEFS"))?;
362 if let Some((financial, strategic)) = self.ids.beliefs() {
363 let list = [(FINANCIAL_ANALYSIS, financial), (STRATEGIC_CONTEXT_ANALYSIS, strategic)];
364 for index in component_map_order(&[financial, strategic]) {
365 self.write_belief(&mut beliefs, list[index].0, list[index].1)?;
366 }
367 }
368
369 let mut intentions = layout.builder(&child_path(&path, "CAI_BDI_POOL_INTENTIONS"))?;
370 let position = self.ids.faction();
371 let characters = self.inputs.characters.get(position).map(Vec::as_slice).unwrap_or_default();
372 let mobiles = self.inputs.mobiles.get(position).map(Vec::as_slice).unwrap_or_default();
373 let list = std::iter::once((FACTION_INTENTION, self.ids.intention(), None))
374 .chain(self.ids.character_intentions().iter().zip(characters).map(|(id, target)| (CHARACTER_SKILLS_INTENTION, *id, Some(*target))))
375 .chain(self.ids.mobile_intentions().iter().zip(mobiles).map(|(id, target)| (FORCE_SKILLS_INTENTION, *id, Some(*target))))
376 .collect::<Vec<_>>();
377 for index in component_map_order(&list.iter().map(|(_, id, _)| *id).collect::<Vec<_>>()) {
378 let (code, id, target) = list[index];
379 self.write_intention(&mut intentions, code, id, target)?;
380 }
381
382 let mut pool = layout.builder(&path)?;
383 let group = pool.group()?;
384 group.set_record("CAI_BDI_POOL_BELIEFS", Some(beliefs.build()?))?
385 .set_record("CAI_BDI_POOL_DESIRES", Some(desires.build()?))?
386 .set_record("CAI_BDI_POOL_INTENTIONS", Some(intentions.build()?))?;
387 if self.kind != ManagerKind::Ai {
388 group.set_u32("CAI_BDI_POOL_FAILED+1", NON_AI_POOL_VALUE)?;
389 }
390 pool.build()
391 }
392
393 fn write_desire(&self, desires: &mut RecordBuilder, code: u32, id: u32) -> Result<()> {
395 let layout = self.inputs.layout;
396 let variant = variant_path(&child_path(&managers_path(), "CAI_BDI_POOL/CAI_BDI_POOL_DESIRES"), code);
397 let analysed_belief = match code {
398 FINANCIAL_ANALYSER => self.ids.beliefs().map(|(financial, _)| financial),
399 STRATEGIC_ANALYSER => self.ids.beliefs().map(|(_, strategic)| strategic),
400 _ => None,
401 };
402 let links = match code {
403 TARGET_LIST => vec![(self.desire(TMS_COORDINATION), OWNER_LINK)],
404 FORCE_SKILLS => self.ids.mobile_intentions().iter().map(|intention| (*intention, INTENTION_LINK)).collect(),
405 CHARACTER_SKILLS => self.ids.character_intentions().iter().map(|intention| (*intention, INTENTION_LINK)).collect(),
406 _ => analysed_belief.map(|belief| (belief, OWNER_LINK)).into_iter().collect(),
407 };
408 let wars = self.wars();
409 let flags = match code {
410 FINANCE_MANAGER => Some(FRESH_FLAGS),
411 DIPLOMACY_MANAGER => Some(if wars.is_empty() { DIPLOMACY_FLAGS.1 } else { DIPLOMACY_FLAGS.0 }),
412 _ => None,
413 };
414 let users = CAI_FACTION_USERS.contains(&code).then(|| vec![self.cai_faction]);
415 let human = self.kind == ManagerKind::Human;
416 let priority = HUMAN_PRIORITIES.iter().find(|(human_code, _)| human && *human_code == code).map(|(_, priority)| *priority);
417 let component = pool_component(layout, &variant, id, ComponentFields { flags, priority, users, links, references: vec![] })?;
418
419 let group = desires.push_variant(code)?;
420 group.set_record("CAI_BDI_COMPONENT", Some(component))?;
421 match code {
422 TARGET_LIST => self.set_single(group, &variant, "CAI_MANOEUVRE_AND_TARGET_LIST", "^+1", self.desire(TMS_COORDINATION))?,
423 FINANCE_MANAGER => { group.set_record("CAI_FACTION_BDI_POOL_FINANCE_MANAGER_BASIC", Some(self.write_finance_manager(&variant)?))?; },
424 RESEARCH => self.set_single(group, &variant, "CAI_BDI_DESIRE_RESEARCH_TECHNOLOGY", "^+1", self.faction)?,
425 TAXATION => self.set_single(group, &variant, "CAI_BDI_DESIRE_TAXATION", "^+1", self.faction)?,
426 FINANCE_SAVINGS => {
427 let savings_path = child_path(&variant, "CAI_BDI_DESIRE_FINANCE_SAVINGS");
428 let mut date = layout.builder(&child_path(&savings_path, "DATE"))?;
429 date.group()?.set_u32("^+1", self.inputs.start_year)?;
430 let mut savings = layout.builder(&savings_path)?;
431 savings.group()?.set_record("DATE", Some(date.build()?))?;
432 group.set_record("CAI_BDI_DESIRE_FINANCE_SAVINGS", Some(savings.build()?))?;
433 },
434 FINANCIAL_ANALYSER | STRATEGIC_ANALYSER => {
435 let mut analyser = layout.builder(&child_path(&variant, "CAI_ANALYSER"))?;
436 analyser.group()?
437 .set_u32s("^+1", analysed_belief.map(|belief| vec![self.faction, belief]).unwrap_or_default())?
438 .set("^+2", NodeType::U64Array(vec![]))?;
439 group.set_record("CAI_ANALYSER", Some(analyser.build()?))?;
440 },
441 TMS_COORDINATION => {
442 let mut properties = layout.builder(&child_path(&variant, "CAI_BDI_COMPONENT_PROPERTY_SET"))?;
443 properties.group()?.set_u32("^+12", self.faction)?;
444 let mut coordination = layout.builder(&child_path(&variant, "CAI_TMS_COORDINATION_MANAGER"))?;
445 coordination.group()?
446 .set_u32("^+1", self.faction)?
447 .set_u32("^+2", self.desire(TARGET_LIST))?;
448 group.set_record("CAI_BDI_COMPONENT_PROPERTY_SET", Some(properties.build()?))?
449 .set_record("CAI_TMS_COORDINATION_MANAGER", Some(coordination.build()?))?;
450 },
451 STANCE_MANAGER => { group.set_record("CAI_STRATEGIC_STANCE_MANAGER", Some(self.write_stance_manager(&variant)?))?; },
452 TASK_GENERATOR => {
453 let mut generator = layout.builder(&child_path(&variant, "CAI_TASK_GENERATOR_MANAGER"))?;
454 generator.group()?.set_bool("^+4", human)?;
455 group.set_record("CAI_TASK_GENERATOR_MANAGER", Some(generator.build()?))?;
456 },
457 DIPLOMACY_MANAGER => { group.set_record("CAI_DIPLOMACY_MANAGER", Some(self.write_diplomacy_manager(&variant, &wars)?))?; },
458 _ => if let Some(analyser) = empty_analyser(layout, &variant)? {
459 group.set_record("CAI_ANALYSER", Some(analyser))?;
460 },
461 }
462 Ok(())
463 }
464
465 fn write_belief(&self, beliefs: &mut RecordBuilder, code: u32, id: u32) -> Result<()> {
468 let layout = self.inputs.layout;
469 let variant = variant_path(&child_path(&managers_path(), "CAI_BDI_POOL/CAI_BDI_POOL_BELIEFS"), code);
470 let key = if code == FINANCIAL_ANALYSIS { "CAI_FINANCIAL_ANALYSIS" } else { "CAI_STRATEGIC_CONTEXT_ANALYSIS" };
471 let references = if code == FINANCIAL_ANALYSIS { vec![self.desire(FINANCE_MANAGER)] } else { vec![] };
472 let component = pool_component(layout, &variant, id, ComponentFields { flags: Some(FRESH_FLAGS), references, ..Default::default() })?;
473
474 let mut properties = layout.builder(&child_path(&variant, "CAI_BDI_COMPONENT_PROPERTY_SET"))?;
475 properties.group()?.set_u32("^+9", self.faction)?;
476 let mut analysis = layout.builder(&child_path(&variant, key))?;
477 let group = analysis.group()?;
478 group.set_u32("^+1", self.faction)?;
479 if code == FINANCIAL_ANALYSIS {
480 group.set_f32("^+2", 0.0)?.set_f32("^+3", 0.0)?;
481 } else {
482 group.set_u32("^+2", STRATEGIC_CONTEXT)?.set_u32("^+3", 0)?;
483 }
484
485 beliefs.push_variant(code)?
486 .set_record("CAI_BDI_COMPONENT_PROPERTY_SET", Some(properties.build()?))?
487 .set_record("CAI_BDI_COMPONENT", Some(component))?
488 .set_record(key, Some(analysis.build()?))?;
489 Ok(())
490 }
491
492 fn write_intention(&self, intentions: &mut RecordBuilder, code: u32, id: u32, target: Option<u32>) -> Result<()> {
494 let layout = self.inputs.layout;
495 let variant = variant_path(&child_path(&managers_path(), "CAI_BDI_POOL/CAI_BDI_POOL_INTENTIONS"), code);
496 let links = target.map(|target| vec![(target, OWNER_LINK)]).unwrap_or_default();
497 let component = pool_component(layout, &variant, id, ComponentFields { links, ..Default::default() })?;
498 let group = intentions.push_variant(code)?;
499 group.set_record("CAI_BDI_COMPONENT", Some(component))?;
500 if let Some(target) = target {
501 let mut properties = layout.builder(&child_path(&variant, "CAI_BDI_COMPONENT_PROPERTY_SET"))?;
502 properties.group()?.set_u32("^+12", target)?;
503 group.set_record("CAI_BDI_COMPONENT_PROPERTY_SET", Some(properties.build()?))?;
504
505 let key = if code == CHARACTER_SKILLS_INTENTION { "CAI_BDI_CHARACTER_SKILL_MANAGEMENT_FOR_CHARACTER" } else { "CAI_BDI_FORCE_LEGACY_SKILL_MANAGEMENT_FOR_FORCE" };
506 self.set_single(group, &variant, key, "^+1", target)?;
507 }
508 Ok(())
509 }
510
511 fn set_single(&self, group: &mut GroupBuilder, variant: &str, key: &str, field: &str, value: u32) -> Result<()> {
513 let mut record = self.inputs.layout.builder(&child_path(variant, key))?;
514 record.group()?.set_u32(field, value)?;
515 group.set_record(key, Some(record.build()?))?;
516 Ok(())
517 }
518
519 fn wars(&self) -> Vec<u32> {
521 let position = self.ids.faction();
522 self.inputs.plan.factions().iter().enumerate()
523 .filter(|(other, _)| self.inputs.stances.at_war(position, *other))
524 .map(|(_, ids)| ids.faction())
525 .collect()
526 }
527
528 fn write_finance_manager(&self, variant: &str) -> Result<RecordNode> {
531 let mut record = self.inputs.layout.builder(&child_path(variant, "CAI_FACTION_BDI_POOL_FINANCE_MANAGER_BASIC"))?;
532 let mut repeated = VecI32Node::default();
533 repeated.set_value(vec![2; BUDGET_CATEGORIES]).set_optimized(true);
534 let mut never = VecU32Node::default();
535 never.set_value(vec![0; BUDGET_CATEGORIES]).set_optimized(true);
536 record.group()?
537 .set_u32s("^+1", vec![])?
538 .set_u32("^+2", u32::MAX)?
539 .set_u32s("^+3", vec![])?
540 .set_f32("^+4", 0.0)?
541 .set_i32("^+5", -1)?
542 .set_f32("^+6", 0.0)?
543 .set_bool("^+7", false)?
544 .set_f32("^+8", -1.0)?
545 .set_f32("^+9", -1.0)?
546 .set_f32("^+10", 0.0)?
547 .set_f32("^+11", 0.0)?
548 .set_f32("^+12", 0.0)?
549 .set("^+13", NodeType::F32Array(vec![1.0 / BUDGET_CATEGORIES as f32; BUDGET_CATEGORIES]))?
550 .set("^+14", NodeType::F32Array(vec![0.0; BUDGET_CATEGORIES]))?
551 .set("^+15", NodeType::BoolArray(vec![false; BUDGET_CATEGORIES]))?
552 .set("^+16", NodeType::U32Array(never))?
553 .set("^+17", NodeType::F32Array(vec![1000.0; BUDGET_CATEGORIES]))?
554 .set("^+18", NodeType::F32Array(vec![0.0; BUDGET_CATEGORIES]))?
555 .set("^+19", NodeType::F32Array(vec![0.0; BUDGET_CATEGORIES]))?
556 .set("^+20", NodeType::I32Array(repeated))?
557 .set_u32("^+21", 5)?
558 .set_u32("^+22", 5)?;
559 record.build()
560 }
561
562 fn write_stance_manager(&self, variant: &str) -> Result<RecordNode> {
565 let layout = self.inputs.layout;
566 let path = child_path(variant, "CAI_STRATEGIC_STANCE_MANAGER");
567 let factions = self.inputs.plan.factions();
568 let marks = self.inputs.stances.marks(self.ids.faction()).iter().map(|position| factions[*position].faction()).collect::<Vec<_>>();
569 let mut blocking = layout.builder(&child_path(&path, "CAI_FACTION_STANCE_BLOCKING_AND_PROMOTION"))?;
570 let adjustments_path = child_path(&child_path(&path, "CAI_FACTION_STANCE_BLOCKING_AND_PROMOTION"), "CAI_FACTION_STANCE_BLOCKING_AND_PROMOTION_SCORE_ADJUSTMENT");
571 for index in component_map_order(&marks) {
572 let mut adjustments = layout.builder(&adjustments_path)?;
573 for _ in 0..STANCE_SCORES {
574 adjustments.push_group()?;
575 }
576 blocking.push_group()?
577 .set_u32("^+1", marks[index])?
578 .set_record("CAI_FACTION_STANCE_BLOCKING_AND_PROMOTION_SCORE_ADJUSTMENT", Some(adjustments.build()?))?;
579 }
580
581 let mut empire = layout.builder(&child_path(&path, "CAI_STRATEGIC_STANCE_MANAGER_EMPIRE_DATA"))?;
582 let group = empire.group()?;
583 for field in 1..=6 {
584 group.set_u32(&format!("^+{field}"), 0)?;
585 }
586 for field in 7..=12 {
587 group.set_f32(&format!("^+{field}"), 0.5)?;
588 }
589
590 let mut manager = layout.builder(&path)?;
591 manager.group()?
592 .set_u32("^+1", self.faction)?
593 .set_record("CAI_FACTION_STANCE_BLOCKING_AND_PROMOTION", Some(blocking.build()?))?
594 .set_record("CAI_STRATEGIC_STANCE_MANAGER_EMPIRE_DATA", Some(empire.build()?))?
595 .set_u32("CAI_STRATEGIC_STANCE_MANAGER_EMPIRE_DATA+1", u32::MAX)?;
596 manager.build()
597 }
598
599 fn write_diplomacy_manager(&self, variant: &str, wars: &[u32]) -> Result<RecordNode> {
601 let mut manager = self.inputs.layout.builder(&child_path(variant, "CAI_DIPLOMACY_MANAGER"))?;
602 let group = manager.group()?;
603 group.push_child(i32_node(if wars.is_empty() { NO_WARS_VALUE } else { 0 }))?
604 .push_child(u32_node(wars.len() as u32))?
605 .push_child(u32_node(DIPLOMACY_ENTRY_PAIRS + 1))?;
606 for enemy in wars {
607 group.push_child(u32_node(*enemy))?
608 .push_child(i32_node(3))?
609 .push_child(i32_node(0))?;
610 for _ in 0..DIPLOMACY_ENTRY_PAIRS {
611 group.push_child(i32_node(-1))?.push_child(i32_node(i32::MAX))?;
612 }
613 }
614 group.push_child(u32_node(if wars.is_empty() { 0 } else { DIPLOMACY_WAR_STATE }))?
615 .push_child(bool_node(false))?
616 .push_child(empty_u32s())?
617 .push_child(empty_u32s())?
618 .push_child(u32_node(0))?;
619 manager.build()
620 }
621
622 fn write_pool_slots(&self) -> Result<RecordNode> {
624 let mut record = self.inputs.layout.builder(&child_path(&managers_path(), "CAI_FACTION_BDI_POOL"))?;
625 let group = record.group()?;
626 for slot in 3..=LAST_POOL_SLOT {
627 let desire = POOL_SLOTS.iter().find(|(pool_slot, _)| *pool_slot == slot).map(|(_, code)| self.desire(*code)).unwrap_or_default();
628 group.set_u32(&format!("^+{slot}"), desire)?;
629 }
630 record.build()
631 }
632
633 fn write_faction_manager(&self) -> Result<RecordNode> {
635 let mut record = self.inputs.layout.builder(&child_path(&managers_path(), "CAI_FACTION_MANAGER"))?;
636 record.group()?
637 .set_u32("^+1", self.faction)?
638 .set_u32("^+2", self.kind.level())?
639 .set_bool("CAI_FACTION_LEARNT_PARAMETERS+1", self.kind != ManagerKind::Rebels)?
640 .set_u32("CAI_FACTION_LEARNT_PARAMETERS+2", 0)?
641 .set_u32("CAI_FACTION_LEARNT_PARAMETERS+3", 0)?;
642 record.build()
643 }
644}