1use rpfm_lib::error::{RLibError, Result};
20use rpfm_lib::files::esf::{NodeType, RecordNode};
21
22use super::managers::{component_map_order, empty_analyser, pool_component, write_managers, ComponentFields, ManagerInputs, ManagerKind};
23use super::mobiles::mobile_cells;
24use super::pathfinder::Pathfinder;
25use super::stances::StartingStances;
26use super::world::{CaiContext, CaiInputs};
27use super::CAI_PATH;
28use crate::startpos::esf_nav::{as_record, child_record};
29use crate::startpos::layout::{child_path, variant_path, StartposLayout};
30use crate::startpos::writer::{bool_node, i32_node, u32_node};
31use crate::startpos::world::calendar::Calendar;
32
33#[cfg(test)] #[path = "interface_test.rs"] mod interface_test;
34
35pub const CENTRAL_DESIRES: [u32; 29] = [32, 33, 36, 35, 42, 31, 43, 30, 45, 47, 92, 39, 128, 130, 132, 136, 134, 46, 153, 155, 157, 162, 164, 166, 168, 178, 181, 187, 181];
37
38const DIRECTOR_TIME: u32 = 3;
40
41const DIRECTOR_WORLD: u32 = 2;
43
44pub const DIRECTOR_BELIEFS: [u32; 2] = [DIRECTOR_TIME, DIRECTOR_WORLD];
46
47const MEDIAN_DISTANCE_UNSET: u32 = u32::MAX;
49
50const CAMPAIGN_DIRECTOR_PATH: &str = "CAMPAIGN_STARTPOS/CAMPAIGN_ENV/CAMPAIGN_MODEL/CAMPAIGN_DIRECTOR";
52
53pub const DIRECTOR_DESIRES: [u32; 4] = [5, 21, 18, 34];
56
57const TOP_LEVEL_LINK: (f32, f32, u32) = (1000.0, 0.0, 0);
59
60const SPARSE_MAP_SIZE: (u32, u32) = (1024, 8);
62
63const NO_CELL: u16 = u16::MAX;
65
66impl CaiContext {
71
72 pub fn write_interface(&self, inputs: &CaiInputs) -> Result<RecordNode> {
82 let layout = inputs.layout;
83 let plan = self.plan();
84 let keys = self.world().factions().iter().map(|faction| faction.key().as_str()).collect::<Vec<_>>();
85 let stances = StartingStances::new(inputs.tables, inputs.index, &keys)?;
86 let characters = plan.characters().iter().map(|characters| characters.iter().map(|character| character.character()).collect()).collect::<Vec<_>>();
87 let mobiles = plan.characters().iter().map(|characters| characters.iter().filter_map(|character| character.mobile()).collect()).collect::<Vec<_>>();
88 let start_year = Calendar::new(inputs.tables, inputs.campaign)?.start_year();
89 let managers = write_managers(&ManagerInputs { layout, plan, stances: &stances, characters: &characters, mobiles: &mobiles, start_year })?;
90
91 let (world, influence) = self.with_mobiles(inputs, |mobile_inputs| {
92 let world = self.write_world_with(inputs, mobile_inputs)?;
93 let cells = mobile_cells(mobile_inputs).into_iter().map(|(mobile, cell)| (mobile, (cell.x(), cell.y()))).collect::<Vec<_>>();
94 Ok((world, self.write_influence_maps(layout, &cells)?))
95 })?;
96
97 let hlp_root = as_record(inputs.hlp_data.root_node()).ok_or_else(|| RLibError::StartposLayoutError("the pathfinder file has no root record".to_owned()))?;
98 let pathfinder = child_record(hlp_root, "CAI_HIGH_LEVEL_PATHFINDER").ok_or_else(|| RLibError::StartposLayoutError("the pathfinder file has no pathfinder".to_owned()))?;
99
100 let mut max_distance = layout.builder(&child_path(CAI_PATH, "CAI_INTERFACE_MAX_REGION_DISTANCE"))?;
101 max_distance.group()?.set_u32("^+1", Pathfinder::read(pathfinder)?.max_region_distance())?;
102
103 let mut median_distance = layout.builder(&child_path(CAI_PATH, "CAI_INTERFACE_MEDIAN_BORDERING_INTERREGION_DISTANCE"))?;
105 median_distance.group()?.set_u32("^+1", MEDIAN_DISTANCE_UNSET)?;
106
107 let levels = plan.factions().iter().zip(plan.manager_kinds())
108 .flat_map(|(ids, kind)| [ids.faction(), kind.level()])
109 .collect();
110
111 let mut central = layout.builder(&child_path(CAI_PATH, "CAI_CENTRAL_BDI_POOL"))?;
113 central.group()?.set_u32("^+1", plan.next_id())?;
114
115 let mut record = layout.builder(CAI_PATH)?;
116 record.group()?
117 .set_record("CAI_WORLD", Some(world))?
118 .set_record("CAI_BDI_POOL", Some(self.write_central_pool(layout)?))?
119 .set_record("CAI_CENTRAL_BDI_POOL", Some(central.build()?))?
120 .set_record("CAI_INTERFACE_MANAGERS", Some(managers))?
121 .set_u32s("CAI_INTERFACE_MANAGERS+1", levels)?
122 .set_record("CAI_INTERFACE_DIRECTOR_BDI_POOL", Some(self.write_director_pool(layout)?))?
123 .set_record("CAI_HIGH_LEVEL_PATHFINDER", Some(pathfinder.clone()))?
124 .set_record("CAI_INTERFACE_MAX_REGION_DISTANCE", Some(max_distance.build()?))?
125 .set_record("CAI_INTERFACE_MEDIAN_BORDERING_INTERREGION_DISTANCE", Some(median_distance.build()?))?
126 .set_record("CAI_INFLUENCE_MAP_MANAGER", Some(influence))?;
127 record.build()
128 }
129
130 pub fn write_campaign_director(&self, layout: &StartposLayout) -> Result<RecordNode> {
141 let plan = self.plan();
142 let world_belief = DIRECTOR_BELIEFS.iter().position(|code| *code == DIRECTOR_WORLD).map(|index| plan.director()[index]).unwrap_or_default();
143 let playable = plan.factions().iter().zip(plan.manager_kinds())
144 .filter(|(_, kind)| **kind == ManagerKind::Human)
145 .map(|(ids, _)| ids.faction())
146 .collect();
147
148 let mut interface = layout.builder(&child_path(CAMPAIGN_DIRECTOR_PATH, "CDIR_INTERFACE"))?;
149 interface.group()?
150 .set_u32("^+1", world_belief)?
151 .set_u32s("CDIR_INTERFACE_UBER_FACTIONS+2", playable)?;
152
153 let mut director = layout.builder(CAMPAIGN_DIRECTOR_PATH)?;
154 director.push_group()?.set_record("CDIR_INTERFACE", Some(interface.build()?))?;
155 director.build()
156 }
157
158 fn write_central_pool(&self, layout: &StartposLayout) -> Result<RecordNode> {
160 let path = child_path(CAI_PATH, "CAI_BDI_POOL");
161 let desires_path = child_path(&path, "CAI_BDI_POOL_DESIRES");
162 let ids = (0..CENTRAL_DESIRES.len() as u32).map(|index| self.plan().first_central_desire() + index).collect::<Vec<_>>();
163 let mut desires = layout.builder(&desires_path)?;
164 for index in component_map_order(&ids) {
165 let code = CENTRAL_DESIRES[index];
166 let variant = variant_path(&desires_path, code);
167 let component = pool_component(layout, &variant, ids[index], ComponentFields::default())?;
168 let group = desires.push_variant(code)?;
169 group.set_record("CAI_BDI_COMPONENT", Some(component))?;
170 if let Some(analyser) = empty_analyser(layout, &variant)? {
171 group.set_record("CAI_ANALYSER", Some(analyser))?;
172 }
173 }
174
175 let mut pool = layout.builder(&path)?;
176 pool.group()?.set_record("CAI_BDI_POOL_DESIRES", Some(desires.build()?))?;
177 pool.build()
178 }
179
180 fn write_director_pool(&self, layout: &StartposLayout) -> Result<RecordNode> {
183 let path = child_path(CAI_PATH, "CAI_INTERFACE_DIRECTOR_BDI_POOL");
184 let pool_path = child_path(&path, "CAI_BDI_POOL");
185 let ids = self.plan().director();
186 let (belief_ids, desire_ids) = ids.split_at(DIRECTOR_BELIEFS.len());
187 let (time, generator) = (belief_ids[0], desire_ids[3]);
188
189 let beliefs_path = child_path(&pool_path, "CAI_BDI_POOL_BELIEFS");
190 let mut beliefs = layout.builder(&beliefs_path)?;
191 for index in component_map_order(belief_ids) {
192 let (code, id) = (DIRECTOR_BELIEFS[index], belief_ids[index]);
193 let variant = variant_path(&beliefs_path, code);
194 let references = if id == time { vec![generator] } else { vec![] };
195 let group = beliefs.push_variant(code)?;
196 group.set_record("CAI_BDI_COMPONENT", Some(pool_component(layout, &variant, id, ComponentFields { references, ..Default::default() })?))?;
197 if id != time {
198 let mut world = layout.builder(&child_path(&variant, "CDIR_WORLD"))?;
199 world.group()?.set_u32("^+1", time)?;
200 group.set_record("CDIR_WORLD", Some(world.build()?))?;
201 }
202 }
203
204 let desires_path = child_path(&pool_path, "CAI_BDI_POOL_DESIRES");
205 let mut desires = layout.builder(&desires_path)?;
206 for index in component_map_order(desire_ids) {
207 let (code, id) = (DIRECTOR_DESIRES[index], desire_ids[index]);
208 let links = if index == 2 { vec![(generator, TOP_LEVEL_LINK)] } else { vec![] };
209 let variant = variant_path(&desires_path, code);
210 let component = pool_component(layout, &variant, id, ComponentFields { links, ..Default::default() })?;
211 let group = desires.push_variant(code)?;
212 group.set_record("CAI_BDI_COMPONENT", Some(component))?;
213 if let Some(analyser) = empty_analyser(layout, &variant)? {
214 group.set_record("CAI_ANALYSER", Some(analyser))?;
215 }
216 }
217
218 let mut pool = layout.builder(&pool_path)?;
219 pool.group()?
220 .set_record("CAI_BDI_POOL_BELIEFS", Some(beliefs.build()?))?
221 .set_record("CAI_BDI_POOL_DESIRES", Some(desires.build()?))?;
222
223 let mut analysers = layout.builder(&child_path(&path, "CDIR_BDI_POOL"))?;
224 analysers.group()?
225 .set_u32("^+1", desire_ids[0])?
226 .set_u32("^+2", desire_ids[1])?;
227
228 let mut director = layout.builder(&path)?;
229 director.group()?
230 .set_record("CAI_BDI_POOL", Some(pool.build()?))?
231 .set_record("CDIR_BDI_POOL", Some(analysers.build()?))?;
232 director.build()
233 }
234
235 fn write_influence_maps(&self, layout: &StartposLayout, cells: &[(u32, (u16, u16))]) -> Result<RecordNode> {
243 let path = child_path(CAI_PATH, "CAI_INFLUENCE_MAP_MANAGER");
244 let map_path = child_path(&path, "CAI_SPARSE_MAP");
245 let empty_map = || -> Result<NodeType> {
246 let record = layout.raw_record(&map_path, vec![u32_node(SPARSE_MAP_SIZE.0), u32_node(SPARSE_MAP_SIZE.1), u32_node(0)])?;
247 Ok(NodeType::Record(Box::new(record)))
248 };
249
250 let mut children = vec![u32_node(cells.len() as u32)];
251 let mobiles = cells.iter().map(|(mobile, _)| *mobile).collect::<Vec<_>>();
252 for index in component_map_order(&mobiles) {
253 let (mobile, (x, y)) = cells[index];
254 children.extend([u32_node(mobile), empty_map()?, bool_node(false), NodeType::U16(NO_CELL), NodeType::U16(NO_CELL), u32_node(0), u32_node(0), u32_node(0)]);
255 children.extend([empty_map()?, i32_node(0), bool_node(false), NodeType::U16(x), NodeType::U16(y), u32_node(0), u32_node(0), u32_node(0)]);
256 }
257
258 let factions = self.plan().factions();
259 children.push(u32_node(factions.len() as u32));
260 for ids in factions {
261 children.extend([u32_node(ids.faction()), empty_map()?]);
262 }
263 layout.raw_record(&path, children)
264 }
265}