mirror of
https://github.com/redstrate/Kawari.git
synced 2025-04-20 06:37:45 +00:00
Detect when players enter exit zones, and transistion them to the correct place
It's a hack and only really works going from New -> Old Gridania, but this does work! It also means Kawari now actually requires a valid game installation, since it has to read layer group information.
This commit is contained in:
parent
dbe1ef208c
commit
059becf55f
8 changed files with 363 additions and 8 deletions
56
Cargo.lock
generated
56
Cargo.lock
generated
|
@ -25,9 +25,9 @@ checksum = "3d62b7694a562cdf5a74227903507c56ab2cc8bdd1f781ed5cb4cf9c9f810bfc"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-trait"
|
name = "async-trait"
|
||||||
version = "0.1.87"
|
version = "0.1.88"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d556ec1359574147ec0c4fc5eb525f3f23263a592b1a9c07e0a75b427de55c97"
|
checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -170,6 +170,12 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crunchy"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crypto-common"
|
name = "crypto-common"
|
||||||
version = "0.1.6"
|
version = "0.1.6"
|
||||||
|
@ -271,6 +277,16 @@ version = "0.31.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "half"
|
||||||
|
version = "2.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7db2ff139bba50379da6aa0766b52fdcb62cb5b263009b09ed58ba604e14bbd1"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"crunchy",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "headers"
|
name = "headers"
|
||||||
version = "0.3.9"
|
version = "0.3.9"
|
||||||
|
@ -366,6 +382,7 @@ dependencies = [
|
||||||
"binrw",
|
"binrw",
|
||||||
"md5",
|
"md5",
|
||||||
"minijinja",
|
"minijinja",
|
||||||
|
"physis",
|
||||||
"rand",
|
"rand",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
@ -386,6 +403,15 @@ version = "0.2.171"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
|
checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libz-rs-sys"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "902bc563b5d65ad9bba616b490842ef0651066a1a1dc3ce1087113ffcb873c8d"
|
||||||
|
dependencies = [
|
||||||
|
"zlib-rs",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "matchit"
|
name = "matchit"
|
||||||
version = "0.7.3"
|
version = "0.7.3"
|
||||||
|
@ -450,9 +476,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
version = "1.21.0"
|
version = "1.21.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cde51589ab56b20a6f686b2c68f7a0bd6add753d697abf720d63f8db3ab7b1ad"
|
checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "percent-encoding"
|
name = "percent-encoding"
|
||||||
|
@ -460,6 +486,18 @@ version = "2.3.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "physis"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "git+https://github.com/redstrate/physis#3273070cef2057b9fe6454d3038a2488484e22fa"
|
||||||
|
dependencies = [
|
||||||
|
"binrw",
|
||||||
|
"bitflags",
|
||||||
|
"half",
|
||||||
|
"libz-rs-sys",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-project"
|
name = "pin-project"
|
||||||
version = "1.1.10"
|
version = "1.1.10"
|
||||||
|
@ -691,9 +729,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.44.0"
|
version = "1.44.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9975ea0f48b5aa3972bf2d888c238182458437cc2a19374b81b25cdf1023fb3a"
|
checksum = "f382da615b842244d4b8738c82ed1275e6c5dd90c459a30941cd07080b06c91a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"backtrace",
|
"backtrace",
|
||||||
"bytes",
|
"bytes",
|
||||||
|
@ -904,3 +942,9 @@ dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.100",
|
"syn 2.0.100",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zlib-rs"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8b20717f0917c908dc63de2e44e97f1e6b126ca58d0e391cee86d504eb8fbd05"
|
||||||
|
|
|
@ -47,3 +47,4 @@ rand = "0.8"
|
||||||
minijinja = "2.0"
|
minijinja = "2.0"
|
||||||
binrw = { version = "0.14", features = ["std"], default-features = false }
|
binrw = { version = "0.14", features = ["std"], default-features = false }
|
||||||
md5 = "0.7.0"
|
md5 = "0.7.0"
|
||||||
|
physis = { git = "https://github.com/redstrate/physis" }
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
|
use std::io::Cursor;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
|
use binrw::BinRead;
|
||||||
use kawari::client_select_data::ClientCustomizeData;
|
use kawari::client_select_data::ClientCustomizeData;
|
||||||
use kawari::ipc::{ActorSetPos, GameMasterCommandType, IPCOpCode, IPCSegment, IPCStructData};
|
use kawari::ipc::{ActorSetPos, GameMasterCommandType, IPCOpCode, IPCSegment, IPCStructData};
|
||||||
use kawari::oodle::FFXIVOodle;
|
use kawari::oodle::FFXIVOodle;
|
||||||
|
@ -8,7 +12,7 @@ use kawari::packet::{
|
||||||
};
|
};
|
||||||
use kawari::world::{
|
use kawari::world::{
|
||||||
ActorControlSelf, ActorControlType, InitZone, PlayerSetup, PlayerSpawn, PlayerStats, Position,
|
ActorControlSelf, ActorControlType, InitZone, PlayerSetup, PlayerSpawn, PlayerStats, Position,
|
||||||
UpdateClassInfo,
|
UpdateClassInfo, Zone,
|
||||||
};
|
};
|
||||||
use kawari::{CHAR_NAME, CONTENT_ID, CUSTOMIZE_DATA, WORLD_ID, ZONE_ID};
|
use kawari::{CHAR_NAME, CONTENT_ID, CUSTOMIZE_DATA, WORLD_ID, ZONE_ID};
|
||||||
use tokio::io::AsyncReadExt;
|
use tokio::io::AsyncReadExt;
|
||||||
|
@ -34,6 +38,10 @@ async fn main() {
|
||||||
player_id: None,
|
player_id: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let zone = Zone::load(010);
|
||||||
|
|
||||||
|
let mut exit_position = None;
|
||||||
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let mut buf = [0; 2056];
|
let mut buf = [0; 2056];
|
||||||
loop {
|
loop {
|
||||||
|
@ -523,6 +531,8 @@ async fn main() {
|
||||||
0, // left finger
|
0, // left finger
|
||||||
0, // right finger
|
0, // right finger
|
||||||
],
|
],
|
||||||
|
pos: exit_position
|
||||||
|
.unwrap_or(Position::default()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
@ -540,6 +550,36 @@ async fn main() {
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fade in?
|
||||||
|
{
|
||||||
|
let ipc = IPCSegment {
|
||||||
|
unk1: 0,
|
||||||
|
unk2: 0,
|
||||||
|
op_code: IPCOpCode::PrepareZoning,
|
||||||
|
server_id: 0,
|
||||||
|
timestamp: timestamp_secs(),
|
||||||
|
data: IPCStructData::PrepareZoning {
|
||||||
|
unk: [0, 0, 0, 0],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let response_packet = PacketSegment {
|
||||||
|
source_actor: state.player_id.unwrap(),
|
||||||
|
target_actor: state.player_id.unwrap(),
|
||||||
|
segment_type: SegmentType::Ipc { data: ipc },
|
||||||
|
};
|
||||||
|
send_packet(
|
||||||
|
&mut write,
|
||||||
|
&[response_packet],
|
||||||
|
&mut state,
|
||||||
|
CompressionType::Oodle,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
// wipe any exit position so it isn't accidentally reused
|
||||||
|
exit_position = None;
|
||||||
}
|
}
|
||||||
IPCStructData::Unk1 { .. } => {
|
IPCStructData::Unk1 { .. } => {
|
||||||
tracing::info!("Recieved Unk1!");
|
tracing::info!("Recieved Unk1!");
|
||||||
|
@ -720,6 +760,162 @@ async fn main() {
|
||||||
IPCStructData::Unk12 { .. } => {
|
IPCStructData::Unk12 { .. } => {
|
||||||
tracing::info!("Recieved Unk12!");
|
tracing::info!("Recieved Unk12!");
|
||||||
}
|
}
|
||||||
|
IPCStructData::EnterZoneLine {
|
||||||
|
exit_box_id,
|
||||||
|
position,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
tracing::info!(
|
||||||
|
"Character entered {exit_box_id} with a position of {position:#?}!"
|
||||||
|
);
|
||||||
|
|
||||||
|
// find the exit box id
|
||||||
|
let (_, exit_box) =
|
||||||
|
zone.find_exit_box(*exit_box_id).unwrap();
|
||||||
|
tracing::info!("exit box: {:#?}", exit_box);
|
||||||
|
|
||||||
|
// find the pop range on the other side
|
||||||
|
let new_zone = Zone::load(exit_box.territory_type);
|
||||||
|
let (destination_object, _) = new_zone
|
||||||
|
.find_pop_range(exit_box.destination_instance_id)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// set the exit position
|
||||||
|
exit_position = Some(Position {
|
||||||
|
x: destination_object.transform.translation[0],
|
||||||
|
y: destination_object.transform.translation[1],
|
||||||
|
z: destination_object.transform.translation[2],
|
||||||
|
});
|
||||||
|
|
||||||
|
// fade out?
|
||||||
|
{
|
||||||
|
let ipc = IPCSegment {
|
||||||
|
unk1: 0,
|
||||||
|
unk2: 0,
|
||||||
|
op_code: IPCOpCode::PrepareZoning,
|
||||||
|
server_id: 0,
|
||||||
|
timestamp: timestamp_secs(),
|
||||||
|
data: IPCStructData::PrepareZoning {
|
||||||
|
unk: [0x01000000, 0, 0, 0],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let response_packet = PacketSegment {
|
||||||
|
source_actor: state.player_id.unwrap(),
|
||||||
|
target_actor: state.player_id.unwrap(),
|
||||||
|
segment_type: SegmentType::Ipc { data: ipc },
|
||||||
|
};
|
||||||
|
send_packet(
|
||||||
|
&mut write,
|
||||||
|
&[response_packet],
|
||||||
|
&mut state,
|
||||||
|
CompressionType::Oodle,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
// fade out? x2
|
||||||
|
{
|
||||||
|
let ipc = IPCSegment {
|
||||||
|
unk1: 0,
|
||||||
|
unk2: 0,
|
||||||
|
op_code: IPCOpCode::PrepareZoning,
|
||||||
|
server_id: 0,
|
||||||
|
timestamp: timestamp_secs(),
|
||||||
|
data: IPCStructData::PrepareZoning {
|
||||||
|
unk: [0, 0x00000085, 0x00030000, 0x000008ff], // last thing is probably a float?
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let response_packet = PacketSegment {
|
||||||
|
source_actor: state.player_id.unwrap(),
|
||||||
|
target_actor: state.player_id.unwrap(),
|
||||||
|
segment_type: SegmentType::Ipc { data: ipc },
|
||||||
|
};
|
||||||
|
send_packet(
|
||||||
|
&mut write,
|
||||||
|
&[response_packet],
|
||||||
|
&mut state,
|
||||||
|
CompressionType::Oodle,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
tracing::info!(
|
||||||
|
"sending them to {:#?}",
|
||||||
|
exit_box.territory_type
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||||
|
d.push("resources/tests/init_zone.bin");
|
||||||
|
|
||||||
|
let buffer = std::fs::read(d).unwrap();
|
||||||
|
let mut buffer = Cursor::new(&buffer);
|
||||||
|
|
||||||
|
let init_zone = InitZone::read_le(&mut buffer).unwrap();
|
||||||
|
|
||||||
|
// Init Zone
|
||||||
|
{
|
||||||
|
let ipc = IPCSegment {
|
||||||
|
unk1: 0,
|
||||||
|
unk2: 0,
|
||||||
|
op_code: IPCOpCode::InitZone,
|
||||||
|
server_id: 0,
|
||||||
|
timestamp: timestamp_secs(),
|
||||||
|
data: IPCStructData::InitZone(InitZone {
|
||||||
|
server_id: WORLD_ID,
|
||||||
|
zone_id: exit_box.territory_type,
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
let response_packet = PacketSegment {
|
||||||
|
source_actor: state.player_id.unwrap(),
|
||||||
|
target_actor: state.player_id.unwrap(),
|
||||||
|
segment_type: SegmentType::Ipc { data: ipc },
|
||||||
|
};
|
||||||
|
send_packet(
|
||||||
|
&mut write,
|
||||||
|
&[response_packet],
|
||||||
|
&mut state,
|
||||||
|
CompressionType::Oodle,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
// idk
|
||||||
|
{
|
||||||
|
let ipc = IPCSegment {
|
||||||
|
unk1: 0,
|
||||||
|
unk2: 0,
|
||||||
|
op_code: IPCOpCode::PrepareZoning,
|
||||||
|
server_id: 0,
|
||||||
|
timestamp: timestamp_secs(),
|
||||||
|
data: IPCStructData::PrepareZoning {
|
||||||
|
unk: [0x01100000, 0, 0, 0],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let response_packet = PacketSegment {
|
||||||
|
source_actor: state.player_id.unwrap(),
|
||||||
|
target_actor: state.player_id.unwrap(),
|
||||||
|
segment_type: SegmentType::Ipc { data: ipc },
|
||||||
|
};
|
||||||
|
send_packet(
|
||||||
|
&mut write,
|
||||||
|
&[response_packet],
|
||||||
|
&mut state,
|
||||||
|
CompressionType::Oodle,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
IPCStructData::Unk13 { .. } => {
|
||||||
|
tracing::info!("Recieved Unk13!");
|
||||||
|
}
|
||||||
|
IPCStructData::Unk14 { .. } => {
|
||||||
|
tracing::info!("Recieved Unk14!");
|
||||||
|
}
|
||||||
_ => panic!(
|
_ => panic!(
|
||||||
"The server is recieving a IPC response or unknown packet!"
|
"The server is recieving a IPC response or unknown packet!"
|
||||||
),
|
),
|
||||||
|
|
|
@ -13,6 +13,9 @@ pub struct Config {
|
||||||
|
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub boot_patches_location: String,
|
pub boot_patches_location: String,
|
||||||
|
|
||||||
|
#[serde(default)]
|
||||||
|
pub game_location: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Config {
|
impl Default for Config {
|
||||||
|
@ -22,6 +25,7 @@ impl Default for Config {
|
||||||
login_open: false,
|
login_open: false,
|
||||||
boot_patches_location: String::new(),
|
boot_patches_location: String::new(),
|
||||||
supported_platforms: default_supported_platforms(),
|
supported_platforms: default_supported_platforms(),
|
||||||
|
game_location: String::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
32
src/ipc.rs
32
src/ipc.rs
|
@ -100,6 +100,15 @@ pub enum IPCOpCode {
|
||||||
CharacterCreated = 0xE,
|
CharacterCreated = 0xE,
|
||||||
// Unknown, client sends this for ???
|
// Unknown, client sends this for ???
|
||||||
Unk12 = 0x0E9,
|
Unk12 = 0x0E9,
|
||||||
|
// Sent by the client when the character walks into a zone transistion
|
||||||
|
EnterZoneLine = 0x205,
|
||||||
|
// Sent by the client after we sent a InitZone in TravelToZone??
|
||||||
|
// TODO: Actually, I don't think is real...
|
||||||
|
Unk13 = 0x2EE,
|
||||||
|
// Sent by the server when it wants the client to... prepare to zone?
|
||||||
|
PrepareZoning = 0x308,
|
||||||
|
// Sent by the client for unknown reasons
|
||||||
|
Unk14 = 0x87,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[binrw]
|
#[binrw]
|
||||||
|
@ -363,6 +372,23 @@ pub enum IPCStructData {
|
||||||
Unk12 {
|
Unk12 {
|
||||||
unk: [u8; 8], // TODO: unknown
|
unk: [u8; 8], // TODO: unknown
|
||||||
},
|
},
|
||||||
|
#[br(pre_assert(*magic == IPCOpCode::EnterZoneLine))]
|
||||||
|
EnterZoneLine {
|
||||||
|
exit_box_id: u32,
|
||||||
|
position: Position,
|
||||||
|
#[brw(pad_after = 4)] // empty
|
||||||
|
landset_index: i32,
|
||||||
|
},
|
||||||
|
#[br(pre_assert(*magic == IPCOpCode::Unk13))]
|
||||||
|
Unk13 {
|
||||||
|
#[br(dbg)]
|
||||||
|
unk: [u8; 16], // TODO: unknown
|
||||||
|
},
|
||||||
|
#[br(pre_assert(*magic == IPCOpCode::Unk14))]
|
||||||
|
Unk14 {
|
||||||
|
#[br(dbg)]
|
||||||
|
unk: [u8; 8], // TODO: unknown
|
||||||
|
},
|
||||||
|
|
||||||
// Server->Client IPC
|
// Server->Client IPC
|
||||||
#[br(pre_assert(false))]
|
#[br(pre_assert(false))]
|
||||||
|
@ -511,6 +537,8 @@ pub enum IPCStructData {
|
||||||
#[brw(pad_after = 1136)] // empty
|
#[brw(pad_after = 1136)] // empty
|
||||||
details: CharacterDetails,
|
details: CharacterDetails,
|
||||||
},
|
},
|
||||||
|
#[br(pre_assert(false))]
|
||||||
|
PrepareZoning { unk: [u32; 4] },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[binrw]
|
#[binrw]
|
||||||
|
@ -578,6 +606,10 @@ impl IPCSegment {
|
||||||
IPCStructData::NameRejection { .. } => 536,
|
IPCStructData::NameRejection { .. } => 536,
|
||||||
IPCStructData::CharacterCreated { .. } => 2568,
|
IPCStructData::CharacterCreated { .. } => 2568,
|
||||||
IPCStructData::Unk12 { .. } => todo!(),
|
IPCStructData::Unk12 { .. } => todo!(),
|
||||||
|
IPCStructData::EnterZoneLine { .. } => todo!(),
|
||||||
|
IPCStructData::Unk13 { .. } => todo!(),
|
||||||
|
IPCStructData::PrepareZoning { .. } => 16,
|
||||||
|
IPCStructData::Unk14 { .. } => todo!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ pub struct InitZone {
|
||||||
pub layer_set_id: u32,
|
pub layer_set_id: u32,
|
||||||
pub layout_id: u32,
|
pub layout_id: u32,
|
||||||
#[br(dbg)]
|
#[br(dbg)]
|
||||||
pub weather_id: u16,
|
pub weather_id: u16, // index into Weather sheet probably?
|
||||||
pub unk_really: u16,
|
pub unk_really: u16,
|
||||||
pub unk_bitmask1: u8,
|
pub unk_bitmask1: u8,
|
||||||
pub unk_bitmask2: u8,
|
pub unk_bitmask2: u8,
|
||||||
|
|
|
@ -22,3 +22,6 @@ pub use actor_control_self::ActorControlType;
|
||||||
|
|
||||||
mod init_zone;
|
mod init_zone;
|
||||||
pub use init_zone::InitZone;
|
pub use init_zone::InitZone;
|
||||||
|
|
||||||
|
mod zone;
|
||||||
|
pub use zone::Zone;
|
||||||
|
|
75
src/world/zone.rs
Normal file
75
src/world/zone.rs
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
use physis::{
|
||||||
|
common::Platform,
|
||||||
|
gamedata::GameData,
|
||||||
|
layer::{
|
||||||
|
ExitRangeInstanceObject, InstanceObject, LayerEntryData, LayerGroup, PopRangeInstanceObject,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{config::get_config, world::Position};
|
||||||
|
|
||||||
|
/// Represents a loaded zone
|
||||||
|
pub struct Zone {
|
||||||
|
id: u16,
|
||||||
|
layer_group: LayerGroup,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Zone {
|
||||||
|
pub fn load(id: u16) -> Self {
|
||||||
|
let config = get_config();
|
||||||
|
|
||||||
|
let mut game_data =
|
||||||
|
GameData::from_existing(Platform::Win32, &config.game_location).unwrap();
|
||||||
|
let mdl;
|
||||||
|
println!("loading {id}");
|
||||||
|
if id == 133 {
|
||||||
|
mdl = game_data
|
||||||
|
.extract("bg/ffxiv/fst_f1/twn/f1t2/level/planmap.lgb")
|
||||||
|
.unwrap();
|
||||||
|
} else {
|
||||||
|
mdl = game_data
|
||||||
|
.extract("bg/ffxiv/fst_f1/twn/f1t1/level/planmap.lgb")
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
let layer_group = LayerGroup::from_existing(&mdl).unwrap();
|
||||||
|
Self { id, layer_group }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Search for an exit box matching an id.
|
||||||
|
pub fn find_exit_box(
|
||||||
|
&self,
|
||||||
|
instance_id: u32,
|
||||||
|
) -> Option<(&InstanceObject, &ExitRangeInstanceObject)> {
|
||||||
|
// TODO: also check position!
|
||||||
|
for group in &self.layer_group.layers {
|
||||||
|
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!
|
||||||
|
for group in &self.layer_group.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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue