2025-05-10 09:25:22 -04:00
|
|
|
use icarus::TerritoryType::TerritoryTypeSheet;
|
2025-03-15 19:34:29 -04:00
|
|
|
use physis::{
|
2025-03-30 21:42:46 -04:00
|
|
|
common::Language,
|
2025-03-15 19:34:29 -04:00
|
|
|
gamedata::GameData,
|
|
|
|
layer::{
|
|
|
|
ExitRangeInstanceObject, InstanceObject, LayerEntryData, LayerGroup, PopRangeInstanceObject,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
/// Represents a loaded zone
|
2025-05-12 16:32:55 -04:00
|
|
|
#[derive(Default, Debug)]
|
2025-03-15 19:34:29 -04:00
|
|
|
pub struct Zone {
|
2025-03-15 20:49:07 -04:00
|
|
|
pub id: u16,
|
2025-03-28 22:34:34 -04:00
|
|
|
planevent: Option<LayerGroup>,
|
|
|
|
vfx: Option<LayerGroup>,
|
|
|
|
planmap: Option<LayerGroup>,
|
|
|
|
planner: Option<LayerGroup>,
|
|
|
|
bg: Option<LayerGroup>,
|
|
|
|
sound: Option<LayerGroup>,
|
|
|
|
planlive: Option<LayerGroup>,
|
2025-03-15 19:34:29 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Zone {
|
2025-03-30 19:13:24 -04:00
|
|
|
pub fn load(game_data: &mut GameData, id: u16) -> Self {
|
2025-03-23 16:31:30 -04:00
|
|
|
let mut zone = Self {
|
|
|
|
id,
|
2025-03-28 22:34:34 -04:00
|
|
|
..Default::default()
|
2025-03-23 16:31:30 -04:00
|
|
|
};
|
|
|
|
|
2025-05-09 18:35:44 -04:00
|
|
|
let sheet = TerritoryTypeSheet::read_from(game_data, Language::None).unwrap();
|
|
|
|
let row = sheet.get_row(id as u32).unwrap();
|
2025-03-15 21:41:39 -04:00
|
|
|
|
|
|
|
// e.g. ffxiv/fst_f1/fld/f1f3/level/f1f3
|
2025-05-09 18:35:44 -04:00
|
|
|
let bg_path = row.Bg().into_string().unwrap();
|
2025-03-15 21:41:39 -04:00
|
|
|
|
2025-03-23 16:31:30 -04:00
|
|
|
let Some(level_index) = bg_path.find("/level/") else {
|
|
|
|
return zone;
|
|
|
|
};
|
|
|
|
|
2025-03-28 22:34:34 -04:00
|
|
|
let mut load_lgb = |name: &str| -> Option<LayerGroup> {
|
|
|
|
let path = format!("bg/{}/level/{}.lgb", &bg_path[..level_index], name);
|
2025-06-21 09:27:12 -04:00
|
|
|
let lgb_file = game_data.extract(&path)?;
|
2025-03-28 22:34:34 -04:00
|
|
|
tracing::info!("Loading {path}");
|
2025-06-21 09:27:12 -04:00
|
|
|
let lgb = LayerGroup::from_existing(&lgb_file);
|
|
|
|
if lgb.is_none() {
|
|
|
|
tracing::warn!("Failed to parse {path}, this is most likely a bug in Physis and should be reported somewhere!")
|
|
|
|
}
|
|
|
|
lgb
|
2025-03-23 16:31:30 -04:00
|
|
|
};
|
2025-03-28 22:34:34 -04:00
|
|
|
|
|
|
|
zone.planevent = load_lgb("planevent");
|
|
|
|
zone.vfx = load_lgb("vfx");
|
|
|
|
zone.planmap = load_lgb("planmap");
|
|
|
|
zone.planner = load_lgb("planner");
|
|
|
|
zone.bg = load_lgb("bg");
|
|
|
|
zone.sound = load_lgb("sound");
|
|
|
|
zone.planlive = load_lgb("planlive");
|
2025-03-23 16:31:30 -04:00
|
|
|
|
|
|
|
zone
|
2025-03-15 19:34:29 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Search for an exit box matching an id.
|
|
|
|
pub fn find_exit_box(
|
|
|
|
&self,
|
|
|
|
instance_id: u32,
|
|
|
|
) -> Option<(&InstanceObject, &ExitRangeInstanceObject)> {
|
|
|
|
// TODO: also check position!
|
2025-04-13 18:36:43 -04:00
|
|
|
for group in &self.planmap.as_ref().unwrap().chunks[0].layers {
|
2025-03-15 19:34:29 -04:00
|
|
|
for object in &group.objects {
|
|
|
|
if let LayerEntryData::ExitRange(exit_range) = &object.data {
|
|
|
|
if object.instance_id == instance_id {
|
|
|
|
return Some((object, exit_range));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn find_pop_range(
|
|
|
|
&self,
|
|
|
|
instance_id: u32,
|
|
|
|
) -> Option<(&InstanceObject, &PopRangeInstanceObject)> {
|
|
|
|
// TODO: also check position!
|
2025-05-11 12:39:35 -04:00
|
|
|
if let Some(planmap) = self.planmap.as_ref() {
|
|
|
|
for group in &planmap.chunks[0].layers {
|
|
|
|
for object in &group.objects {
|
|
|
|
if let LayerEntryData::PopRange(pop_range) = &object.data {
|
|
|
|
if object.instance_id == instance_id {
|
|
|
|
return Some((object, pop_range));
|
|
|
|
}
|
2025-03-28 22:34:34 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-05-11 12:39:35 -04:00
|
|
|
if let Some(planevent) = self.planevent.as_ref() {
|
|
|
|
// For certain PopRanges (e.g. the starting position in the opening zones)
|
|
|
|
for group in &planevent.chunks[0].layers {
|
|
|
|
for object in &group.objects {
|
|
|
|
if let LayerEntryData::PopRange(pop_range) = &object.data {
|
|
|
|
if object.instance_id == instance_id {
|
|
|
|
return Some((object, pop_range));
|
|
|
|
}
|
2025-03-15 19:34:29 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|