1use crate::civ::CivilizationID;
2use crate::unit_type::UnitTypeID;
3use arrayvec::ArrayVec;
4use byteorder::{ReadBytesExt, WriteBytesExt, LE};
5use genie_support::{read_opt_u32, TechID};
6use std::convert::{TryFrom, TryInto};
7use std::io::{self, Read, Result, Write};
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub enum TechTreeStatus {
11 None,
12 AvailablePlayer,
14 NotAvailablePlayer,
16 Researching,
18 ResearchedCompleted,
20 AvailableTeam {
23 civilization_id: CivilizationID,
24 },
25}
26
27impl std::default::Default for TechTreeStatus {
28 fn default() -> Self {
29 Self::None
30 }
31}
32
33#[derive(Debug, Clone, Copy, thiserror::Error)]
34#[error("invalid tech tree node status {} (must be 1-5)", .0)]
35pub struct ParseTechTreeStatusError(u8);
36
37impl TryFrom<u8> for TechTreeStatus {
38 type Error = ParseTechTreeStatusError;
39
40 fn try_from(n: u8) -> std::result::Result<Self, Self::Error> {
41 match n {
42 1 => Ok(Self::None),
43 2 => Ok(Self::AvailablePlayer),
44 3 => Ok(Self::NotAvailablePlayer),
45 4 => Ok(Self::Researching),
46 5 => Ok(Self::ResearchedCompleted),
47 10..=255 => Ok(Self::AvailableTeam {
48 civilization_id: (n - 10).into(),
49 }),
50 n => Err(ParseTechTreeStatusError(n as u8)),
51 }
52 }
53}
54
55impl From<TechTreeStatus> for u8 {
56 fn from(status: TechTreeStatus) -> Self {
57 match status {
58 TechTreeStatus::None => 1,
59 TechTreeStatus::AvailablePlayer => 2,
60 TechTreeStatus::NotAvailablePlayer => 3,
61 TechTreeStatus::Researching => 4,
62 TechTreeStatus::ResearchedCompleted => 5,
63 TechTreeStatus::AvailableTeam { civilization_id } => u8::from(civilization_id) + 10,
64 }
65 }
66}
67
68#[derive(Debug, Clone, Copy, PartialEq, Eq)]
70pub enum TechTreeType {
71 None = 0,
72 Age = 1,
73 Unit = 2,
74 UnitUpgrade = 3,
75 Research = 4,
76 BuildingTech = 5,
77 BuildingNonTech = 6,
78 UniqueUnit = 7,
79}
80
81impl std::default::Default for TechTreeType {
82 fn default() -> Self {
83 Self::None
84 }
85}
86
87#[derive(Debug, Clone, Copy, thiserror::Error)]
88#[error("invalid tech tree node type {} (must be 0-7)", .0)]
89pub struct ParseTechTreeTypeError(i32);
90
91impl TryFrom<i32> for TechTreeType {
92 type Error = ParseTechTreeTypeError;
93
94 fn try_from(n: i32) -> std::result::Result<Self, Self::Error> {
95 match n {
96 0 => Ok(Self::None),
97 1 => Ok(Self::Age),
98 2 => Ok(Self::Unit),
99 3 => Ok(Self::UnitUpgrade),
100 4 => Ok(Self::Research),
101 5 => Ok(Self::BuildingTech),
102 6 => Ok(Self::BuildingNonTech),
103 7 => Ok(Self::UniqueUnit),
104 n => Err(ParseTechTreeTypeError(n)),
105 }
106 }
107}
108
109impl From<TechTreeType> for u32 {
110 fn from(ty: TechTreeType) -> Self {
111 ty as u32
112 }
113}
114
115impl From<TechTreeType> for i32 {
116 fn from(ty: TechTreeType) -> Self {
117 ty as i32
118 }
119}
120
121#[derive(Debug, Default, Clone)]
122pub struct TechTree {
123 pub ages: Vec<TechTreeAge>,
124 pub buildings: Vec<TechTreeBuilding>,
125 pub units: Vec<TechTreeUnit>,
126 pub techs: Vec<TechTreeTech>,
127 num_groups: i32,
128}
129
130#[derive(Debug, Clone, Copy, PartialEq, Eq)]
131pub enum TechTreeDependency {
132 Age(i32),
138 Building(UnitTypeID),
140 Unit(UnitTypeID),
142 Research(TechID),
144}
145
146impl TechTreeDependency {
147 fn dependency_type(&self) -> TechTreeDependencyType {
148 match self {
149 Self::Age(_) => TechTreeDependencyType::Age,
150 Self::Building(_) => TechTreeDependencyType::Building,
151 Self::Unit(_) => TechTreeDependencyType::Unit,
152 Self::Research(_) => TechTreeDependencyType::Research,
153 }
154 }
155
156 fn raw_id(&self) -> i32 {
157 match *self {
158 Self::Age(id) => id.into(),
159 Self::Building(id) => id.into(),
160 Self::Unit(id) => id.into(),
161 Self::Research(id) => {
162 let id: u16 = id.into();
163 id as i32
164 }
165 }
166 }
167}
168
169#[derive(Debug, Clone, Copy, PartialEq, Eq)]
170enum TechTreeDependencyType {
171 Age = 0,
172 Building = 1,
173 Unit = 2,
174 Research = 3,
175}
176
177#[derive(Debug, Clone, Copy, thiserror::Error)]
178#[error("invalid tech tree dependency type {} (must be 0-3)", .0)]
179pub struct ParseTechTreeDependencyTypeError(i32);
180
181impl TryFrom<i32> for TechTreeDependencyType {
182 type Error = ParseTechTreeDependencyTypeError;
183
184 fn try_from(n: i32) -> std::result::Result<Self, Self::Error> {
185 match n {
186 0 => Ok(Self::Age),
187 1 => Ok(Self::Building),
188 2 => Ok(Self::Unit),
189 3 => Ok(Self::Research),
190 n => Err(ParseTechTreeDependencyTypeError(n)),
191 }
192 }
193}
194
195impl Into<i32> for TechTreeDependencyType {
196 fn into(self) -> i32 {
197 self as i32
198 }
199}
200
201#[derive(Debug, Default, Clone)]
202pub struct TechTreeDependencies(ArrayVec<[TechTreeDependency; 10]>);
203
204#[derive(Debug, Default, Clone)]
205pub struct TechTreeAge {
206 age_id: i32,
207 status: TechTreeStatus,
208 node_type: TechTreeType,
209 pub dependent_buildings: Vec<UnitTypeID>,
211 pub dependent_units: Vec<UnitTypeID>,
213 pub dependent_techs: Vec<TechID>,
215 prerequisites: TechTreeDependencies,
216 building_levels: u8,
217 buildings_per_zone: [u8; 10],
218 group_length_per_zone: [u8; 10],
219 max_age_length: u8,
220}
221
222#[derive(Debug, Default, Clone)]
223pub struct TechTreeBuilding {
224 building_id: UnitTypeID,
225 status: TechTreeStatus,
226 node_type: TechTreeType,
227 pub depends_tech_id: Option<TechID>,
230 pub dependent_buildings: Vec<UnitTypeID>,
232 pub dependent_units: Vec<UnitTypeID>,
234 pub dependent_techs: Vec<TechID>,
236 prerequisites: TechTreeDependencies,
237 level_no: u8,
239 total_children_by_age: [u8; 5],
242 initial_children_by_age: [u8; 5],
245}
246
247#[derive(Debug, Default, Clone)]
248pub struct TechTreeUnit {
249 unit_id: UnitTypeID,
250 status: TechTreeStatus,
251 node_type: TechTreeType,
252 depends_tech_id: Option<TechID>,
253 building: UnitTypeID,
254 requires_tech_id: Option<TechID>,
255 dependent_units: Vec<UnitTypeID>,
256 prerequisites: TechTreeDependencies,
257 group_id: i32,
258 level_no: i32,
259}
260
261#[derive(Debug, Default, Clone)]
262pub struct TechTreeTech {
263 tech_id: TechID,
264 status: TechTreeStatus,
265 node_type: TechTreeType,
266 building: UnitTypeID,
267 dependent_buildings: Vec<UnitTypeID>,
268 dependent_units: Vec<UnitTypeID>,
269 dependent_techs: Vec<TechID>,
270 prerequisites: TechTreeDependencies,
271 group_id: i32,
272 level_no: i32,
273}
274
275impl TechTree {
276 pub fn read_from(mut input: impl Read) -> Result<Self> {
277 let num_ages = input.read_u8()?;
278 let num_buildings = input.read_u8()?;
279 let num_units = input.read_u8()?;
280 let num_techs = input.read_u8()?;
281 let num_groups = input.read_i32::<LE>()?;
282
283 let mut ages = vec![];
284 for _ in 0..num_ages {
285 ages.push(TechTreeAge::read_from(&mut input)?);
286 }
287
288 let mut buildings = vec![];
289 for _ in 0..num_buildings {
290 buildings.push(TechTreeBuilding::read_from(&mut input)?);
291 }
292
293 let mut units = vec![];
294 for _ in 0..num_units {
295 units.push(TechTreeUnit::read_from(&mut input)?);
296 }
297
298 let mut techs = vec![];
299 for _ in 0..num_techs {
300 techs.push(TechTreeTech::read_from(&mut input)?);
301 }
302
303 Ok(Self {
304 ages,
305 buildings,
306 units,
307 techs,
308 num_groups,
309 })
310 }
311
312 pub fn write_to(&self, mut output: impl Write) -> Result<()> {
313 output.write_u8(self.ages.len() as u8)?;
314 output.write_u8(self.buildings.len() as u8)?;
315 output.write_u8(self.units.len() as u8)?;
316 output.write_u8(self.techs.len() as u8)?;
317 output.write_i32::<LE>(self.num_groups)?;
318
319 for age in &self.ages {
320 age.write_to(&mut output)?;
321 }
322 for building in &self.buildings {
323 building.write_to(&mut output)?;
324 }
325 for unit in &self.units {
326 unit.write_to(&mut output)?;
327 }
328 for tech in &self.techs {
329 tech.write_to(&mut output)?;
330 }
331
332 Ok(())
333 }
334}
335
336impl TechTreeDependencies {
337 pub fn read_from<R: Read>(input: &mut R) -> Result<Self> {
338 let mut deps = Self::default();
339 let num = input.read_u8()?;
340 let _padding = input.read_u8()?;
341 let _padding = input.read_u8()?;
342 let _padding = input.read_u8()?;
343
344 let mut ids = [-1i32; 10];
345 for id in ids.iter_mut() {
346 *id = input.read_i32::<LE>()?;
347 }
348 let mut types = [-1i32; 10];
349 for ty in types.iter_mut() {
350 *ty = input.read_i32::<LE>()?;
351 }
352
353 for (&id, &ty) in ids.iter().zip(types.iter()).take(num as usize) {
354 let dep_type: TechTreeDependencyType = ty.try_into().map_err(invalid_data)?;
355 deps.0.push(match dep_type {
356 TechTreeDependencyType::Age => {
357 TechTreeDependency::Age(id.try_into().map_err(invalid_data)?)
358 }
359 TechTreeDependencyType::Building => {
360 TechTreeDependency::Building(id.try_into().map_err(invalid_data)?)
361 }
362 TechTreeDependencyType::Unit => {
363 TechTreeDependency::Unit(id.try_into().map_err(invalid_data)?)
364 }
365 TechTreeDependencyType::Research => {
366 TechTreeDependency::Research(id.try_into().map_err(invalid_data)?)
367 }
368 });
369 }
370
371 Ok(deps)
372 }
373
374 pub fn write_to<W: Write>(&self, output: &mut W) -> Result<()> {
375 assert!(self.len() <= 10);
376 output.write_u8(self.len() as u8)?;
377 output.write_all(&[0, 0, 0])?;
378 for i in 0..10 {
379 output.write_i32::<LE>(self.0.get(i).map(TechTreeDependency::raw_id).unwrap_or(0))?;
380 }
381 for i in 0..10 {
382 output.write_i32::<LE>(
383 self.0
384 .get(i)
385 .map(TechTreeDependency::dependency_type)
386 .map(Into::into)
387 .unwrap_or(0),
388 )?;
389 }
390 Ok(())
391 }
392
393 pub fn len(&self) -> usize {
394 self.0.len()
395 }
396
397 pub fn is_empty(&self) -> bool {
398 self.0.is_empty()
399 }
400
401 pub fn iter(&self) -> impl Iterator<Item = &TechTreeDependency> {
402 self.0.iter()
403 }
404}
405
406fn read_dependents<R, T>(input: &mut R) -> Result<Vec<T>>
409where
410 R: Read,
411 T: TryFrom<i32>,
412 T::Error: std::error::Error + Send + Sync + 'static,
414{
415 let num = input.read_u8()?;
416 let mut list = vec![];
417 for _ in 0..num {
418 list.push(input.read_i32::<LE>()?.try_into().map_err(invalid_data)?);
419 }
420 Ok(list)
421}
422
423impl TechTreeAge {
424 pub fn read_from<R: Read>(input: &mut R) -> Result<Self> {
425 let mut age = Self::default();
426 age.age_id = input.read_i32::<LE>()?;
427 age.status = input.read_u8()?.try_into().map_err(invalid_data)?;
428 age.dependent_buildings = read_dependents(input)?;
429 age.dependent_units = read_dependents(input)?;
430 age.dependent_techs = read_dependents(input)?;
431 age.prerequisites = TechTreeDependencies::read_from(input)?;
432 age.building_levels = input.read_u8()?;
433 assert!(age.building_levels <= 10);
434 for building in age.buildings_per_zone.iter_mut() {
435 *building = input.read_u8()?;
436 }
437 for group_length in age.group_length_per_zone.iter_mut() {
438 *group_length = input.read_u8()?;
439 }
440 age.max_age_length = input.read_u8()?;
441 age.node_type = input.read_i32::<LE>()?.try_into().map_err(invalid_data)?;
442 assert_eq!(age.node_type, TechTreeType::Age);
443 Ok(age)
444 }
445
446 pub fn write_to(&self, mut output: impl Write) -> Result<()> {
447 output.write_i32::<LE>(self.age_id)?;
448 output.write_u8(self.status.into())?;
449 output.write_u8(self.dependent_buildings.len() as u8)?;
450 for dependent in &self.dependent_buildings {
451 output.write_u32::<LE>((*dependent).into())?;
452 }
453 output.write_u8(self.dependent_units.len() as u8)?;
454 for dependent in &self.dependent_units {
455 output.write_u32::<LE>((*dependent).into())?;
456 }
457 output.write_u8(self.dependent_techs.len() as u8)?;
458 for dependent in &self.dependent_techs {
459 output.write_u32::<LE>(u16::from(*dependent) as u32)?;
460 }
461 self.prerequisites.write_to(&mut output)?;
462 output.write_u8(self.building_levels)?;
463 output.write_all(&self.buildings_per_zone)?;
464 output.write_all(&self.group_length_per_zone)?;
465 output.write_u8(self.max_age_length)?;
466 output.write_u32::<LE>(self.node_type.into())?;
467 Ok(())
468 }
469}
470
471impl TechTreeBuilding {
472 pub fn read_from(mut input: impl Read) -> Result<Self> {
473 let mut building = Self::default();
474 building.building_id = input.read_i32::<LE>()?.try_into().map_err(invalid_data)?;
475 building.status = input.read_u8()?.try_into().map_err(invalid_data)?;
476 building.dependent_buildings = read_dependents(&mut input)?;
477 building.dependent_units = read_dependents(&mut input)?;
478 building.dependent_techs = read_dependents(&mut input)?;
479 building.prerequisites = TechTreeDependencies::read_from(&mut input)?;
480 building.level_no = input.read_u8()?;
481 for children in building.total_children_by_age.iter_mut() {
482 *children = input.read_u8()?;
483 }
484 for children in building.initial_children_by_age.iter_mut() {
485 *children = input.read_u8()?;
486 }
487 building.node_type = input.read_i32::<LE>()?.try_into().map_err(invalid_data)?;
488 building.depends_tech_id = read_opt_u32(&mut input)?;
489 Ok(building)
490 }
491
492 pub fn write_to(&self, mut output: impl Write) -> Result<()> {
493 output.write_u32::<LE>(self.building_id.into())?;
494 output.write_u8(self.status.into())?;
495 output.write_u8(self.dependent_buildings.len() as u8)?;
496 for dependent in &self.dependent_buildings {
497 output.write_u32::<LE>((*dependent).into())?;
498 }
499 output.write_u8(self.dependent_units.len() as u8)?;
500 for dependent in &self.dependent_units {
501 output.write_u32::<LE>((*dependent).into())?;
502 }
503 output.write_u8(self.dependent_techs.len() as u8)?;
504 for dependent in &self.dependent_techs {
505 output.write_u32::<LE>(u16::from(*dependent) as u32)?;
506 }
507 self.prerequisites.write_to(&mut output)?;
508 output.write_u8(self.level_no)?;
509 output.write_all(&self.total_children_by_age)?;
510 output.write_all(&self.initial_children_by_age)?;
511 output.write_u32::<LE>(self.node_type.into())?;
512 output.write_u32::<LE>(
513 self.depends_tech_id
514 .map(|tech_id| u16::from(tech_id) as u32)
515 .unwrap_or(0xFFFF_FFFF),
516 )?;
517 Ok(())
518 }
519}
520
521impl TechTreeUnit {
522 pub fn read_from(mut input: impl Read) -> Result<Self> {
523 let mut unit = Self::default();
524 unit.unit_id = input.read_i32::<LE>()?.try_into().map_err(invalid_data)?;
525 unit.status = input.read_u8()?.try_into().map_err(invalid_data)?;
526 unit.building = input.read_i32::<LE>()?.try_into().map_err(invalid_data)?;
527 unit.prerequisites = TechTreeDependencies::read_from(&mut input)?;
528 unit.group_id = input.read_i32::<LE>()?;
529 unit.dependent_units = read_dependents(&mut input)?;
530 unit.level_no = input.read_i32::<LE>()?;
531 unit.requires_tech_id = read_opt_u32(&mut input)?;
532 unit.node_type = input.read_i32::<LE>()?.try_into().map_err(invalid_data)?;
533 unit.depends_tech_id = read_opt_u32(&mut input)?;
534 Ok(unit)
535 }
536
537 pub fn write_to(&self, mut output: impl Write) -> Result<()> {
538 output.write_u32::<LE>(u16::from(self.unit_id).into())?;
539 output.write_u8(self.status.into())?;
540 output.write_u32::<LE>(self.building.into())?;
541 self.prerequisites.write_to(&mut output)?;
542 output.write_i32::<LE>(self.group_id)?;
543 output.write_u8(self.dependent_units.len() as u8)?;
544 for dependent in &self.dependent_units {
545 output.write_u32::<LE>((*dependent).into())?;
546 }
547 output.write_i32::<LE>(self.level_no)?;
548 output.write_u32::<LE>(
549 self.requires_tech_id
550 .map(|tech_id| u16::from(tech_id) as u32)
551 .unwrap_or(0xFFFF_FFFF),
552 )?;
553 output.write_u32::<LE>(self.node_type.into())?;
554 output.write_u32::<LE>(
555 self.depends_tech_id
556 .map(|tech_id| u16::from(tech_id) as u32)
557 .unwrap_or(0xFFFF_FFFF),
558 )?;
559 Ok(())
560 }
561}
562
563impl TechTreeTech {
564 pub fn read_from<R: Read>(input: &mut R) -> Result<Self> {
565 let mut tech = Self::default();
566 tech.tech_id = input.read_i32::<LE>()?.try_into().map_err(invalid_data)?;
567 tech.status = input.read_u8()?.try_into().map_err(invalid_data)?;
568 tech.building = input.read_i32::<LE>()?.try_into().map_err(invalid_data)?;
569 tech.dependent_buildings = read_dependents(input)?;
570 tech.dependent_units = read_dependents(input)?;
571 tech.dependent_techs = read_dependents(input)?;
572 tech.prerequisites = TechTreeDependencies::read_from(input)?;
573 tech.group_id = input.read_i32::<LE>()?;
574 tech.level_no = input.read_i32::<LE>()?;
575 tech.node_type = input.read_i32::<LE>()?.try_into().map_err(invalid_data)?;
576 Ok(tech)
577 }
578
579 pub fn write_to(&self, mut output: impl Write) -> Result<()> {
580 output.write_u32::<LE>(u16::from(self.tech_id).into())?;
581 output.write_u8(self.status.into())?;
582 output.write_u32::<LE>(self.building.into())?;
583 output.write_u8(self.dependent_buildings.len() as u8)?;
584 for dependent in &self.dependent_buildings {
585 output.write_u32::<LE>((*dependent).into())?;
586 }
587 output.write_u8(self.dependent_units.len() as u8)?;
588 for dependent in &self.dependent_units {
589 output.write_u32::<LE>((*dependent).into())?;
590 }
591 output.write_u8(self.dependent_techs.len() as u8)?;
592 for dependent in &self.dependent_techs {
593 output.write_u32::<LE>(u16::from(*dependent) as u32)?;
594 }
595 self.prerequisites.write_to(&mut output)?;
596 output.write_i32::<LE>(self.group_id)?;
597 output.write_i32::<LE>(self.level_no)?;
598 output.write_u32::<LE>(self.node_type.into())?;
599 Ok(())
600 }
601}
602
603fn invalid_data<E: std::error::Error + Sized + Send + Sync + 'static>(err: E) -> io::Error {
604 io::Error::new(io::ErrorKind::InvalidData, err)
605}