diff --git a/resources/scripts/opening/OpeningUldah.lua b/resources/scripts/opening/OpeningUldah.lua new file mode 100644 index 0000000..a7c30c7 --- /dev/null +++ b/resources/scripts/opening/OpeningUldah.lua @@ -0,0 +1,21 @@ +--- TODO: find a way to hardcode it this way +EVENT_ID = 1245187 + +function Scene00000(player) + player:play_scene(EVENT_ID, 00000, 4959237, 1) +end + +function Scene00001(player) + --- todo put player in correct position + player:play_scene(EVENT_ID, 00001, 4959237, 1) +end + +function onEnterTerritory(player) + Scene00000(player); +end + +function onSceneFinished(player, scene) + if scene == 0 then + Scene00001(player) + end +end diff --git a/src/bin/kawari-world.rs b/src/bin/kawari-world.rs index 73877a3..4c80d94 100644 --- a/src/bin/kawari-world.rs +++ b/src/bin/kawari-world.rs @@ -76,6 +76,7 @@ async fn main() { position: Position::default(), inventory: Inventory::new(), status_effects: StatusEffects::default(), + event: None, }; let mut lua_player = LuaPlayer::default(); @@ -570,6 +571,7 @@ async fn main() { ClientZoneIpcData::ChatMessage(chat_message) => { ChatHandler::handle_chat_message( &mut connection, + &mut lua_player, chat_message, ) .await @@ -754,6 +756,10 @@ async fn main() { tracing::info!( "Recieved EventRelatedUnk! {unk1} {unk2} {unk3} {unk4}" ); + + if let Some(event) = connection.event.as_mut() { + event.scene_finished(&mut lua_player, *unk2); + } } } } diff --git a/src/world/chat_handler.rs b/src/world/chat_handler.rs index d3ca5d0..60e9da2 100644 --- a/src/world/chat_handler.rs +++ b/src/world/chat_handler.rs @@ -3,14 +3,17 @@ use crate::{ config::get_config, opcodes::ServerZoneIpcType, packet::{PacketSegment, SegmentType}, - world::ipc::{ - ActorControl, ActorControlCategory, BattleNpcSubKind, CommonSpawn, DisplayFlag, EventPlay, - EventStart, NpcSpawn, ObjectKind, OnlineStatus, PlayerSpawn, PlayerSubKind, - ServerZoneIpcData, ServerZoneIpcSegment, + world::{ + Event, + ipc::{ + ActorControl, ActorControlCategory, BattleNpcSubKind, CommonSpawn, DisplayFlag, + EventPlay, EventStart, NpcSpawn, ObjectKind, OnlineStatus, PlayerSpawn, PlayerSubKind, + ServerZoneIpcData, ServerZoneIpcSegment, + }, }, }; -use super::{ZoneConnection, ipc::ChatMessage}; +use super::{LuaPlayer, ZoneConnection, ipc::ChatMessage}; pub const CUSTOMIZE_DATA: CustomizeData = CustomizeData { race: 4, @@ -44,7 +47,11 @@ pub const CUSTOMIZE_DATA: CustomizeData = CustomizeData { pub struct ChatHandler {} impl ChatHandler { - pub async fn handle_chat_message(connection: &mut ZoneConnection, chat_message: &ChatMessage) { + pub async fn handle_chat_message( + connection: &mut ZoneConnection, + lua_player: &mut LuaPlayer, + chat_message: &ChatMessage, + ) { tracing::info!("Client sent chat message: {}!", chat_message.message); let parts: Vec<&str> = chat_message.message.split(' ').collect(); @@ -322,34 +329,12 @@ impl ChatHandler { .await; } - // play the scene, bart - { - let ipc = ServerZoneIpcSegment { - unk1: 20, - unk2: 0, - op_code: ServerZoneIpcType::EventPlay, - server_id: 0, - timestamp: timestamp_secs(), - data: ServerZoneIpcData::EventPlay(EventPlay { - actor_id: ObjectTypeId { - object_id: ObjectId(connection.player_data.actor_id), - object_type: 0, - }, - event_id: 0x130003, - scene: 1, - scene_flags: 4959237, - ..Default::default() - }), - }; - - connection - .send_segment(PacketSegment { - source_actor: connection.player_data.actor_id, - target_actor: connection.player_data.actor_id, - segment_type: SegmentType::Ipc { data: ipc }, - }) - .await; - } + connection.event = Some(Event::new("opening/OpeningUldah.lua")); + connection + .event + .as_mut() + .unwrap() + .enter_territory(lua_player); } _ => tracing::info!("Unrecognized debug command!"), } diff --git a/src/world/connection.rs b/src/world/connection.rs index 564a464..7a199e4 100644 --- a/src/world/connection.rs +++ b/src/world/connection.rs @@ -10,7 +10,7 @@ use crate::{ }; use super::{ - Inventory, Item, LuaPlayer, Zone, + Event, Inventory, Item, LuaPlayer, Zone, ipc::{ ActorSetPos, ClientZoneIpcSegment, ContainerInfo, ContainerType, InitZone, ItemInfo, ServerZoneIpcData, ServerZoneIpcSegment, StatusEffect, StatusEffectList, UpdateClassInfo, @@ -69,6 +69,8 @@ pub struct ZoneConnection { pub position: Position, pub inventory: Inventory, pub status_effects: StatusEffects, + + pub event: Option, } impl ZoneConnection { diff --git a/src/world/event.rs b/src/world/event.rs new file mode 100644 index 0000000..98ee868 --- /dev/null +++ b/src/world/event.rs @@ -0,0 +1,52 @@ +use mlua::{Function, Lua}; + +use crate::config::get_config; + +use super::LuaPlayer; + +pub struct Event { + lua: Lua, +} + +impl Event { + pub fn new(path: &str) -> Self { + let lua = Lua::new(); + + let config = get_config(); + let file_name = format!("{}/{}", &config.world.scripts_location, path); + lua.load(std::fs::read(&file_name).expect("Failed to locate scripts directory!")) + .set_name("@".to_string() + &file_name) + .exec() + .unwrap(); + + Self { lua } + } + + pub fn enter_territory(&mut self, player: &mut LuaPlayer) { + self.lua + .scope(|scope| { + let player = scope.create_userdata_ref_mut(player).unwrap(); + + let func: Function = self.lua.globals().get("onEnterTerritory").unwrap(); + + func.call::<()>(player).unwrap(); + + Ok(()) + }) + .unwrap(); + } + + pub fn scene_finished(&mut self, player: &mut LuaPlayer, scene: u16) { + self.lua + .scope(|scope| { + let player = scope.create_userdata_ref_mut(player).unwrap(); + + let func: Function = self.lua.globals().get("onSceneFinished").unwrap(); + + func.call::<()>((player, scene)).unwrap(); + + Ok(()) + }) + .unwrap(); + } +} diff --git a/src/world/ipc/mod.rs b/src/world/ipc/mod.rs index 3f00f5b..c998fd1 100644 --- a/src/world/ipc/mod.rs +++ b/src/world/ipc/mod.rs @@ -337,7 +337,8 @@ pub enum ClientZoneIpcData { #[br(pre_assert(*magic == ClientZoneIpcType::EventRelatedUnk))] EventRelatedUnk { unk1: u32, - unk2: u32, + unk2: u16, + #[brw(pad_before = 2)] unk3: u32, unk4: u32, }, diff --git a/src/world/lua.rs b/src/world/lua.rs index 03040ed..f90dee0 100644 --- a/src/world/lua.rs +++ b/src/world/lua.rs @@ -1,14 +1,14 @@ use mlua::{UserData, UserDataMethods}; use crate::{ - common::timestamp_secs, + common::{ObjectId, ObjectTypeId, timestamp_secs}, opcodes::ServerZoneIpcType, packet::{PacketSegment, SegmentType}, }; use super::{ PlayerData, StatusEffects, - ipc::{ServerZoneIpcData, ServerZoneIpcSegment}, + ipc::{EventPlay, ServerZoneIpcData, ServerZoneIpcSegment}, }; #[derive(Default)] @@ -44,6 +44,33 @@ impl LuaPlayer { fn give_status_effect(&mut self, effect_id: u16, duration: f32) { self.status_effects.add(effect_id, duration); } + + fn play_scene(&mut self, event_id: u32, scene: u16, scene_flags: u32, param: u8) { + let ipc = ServerZoneIpcSegment { + unk1: 20, + unk2: 0, + op_code: ServerZoneIpcType::EventPlay, + server_id: 0, + timestamp: timestamp_secs(), + data: ServerZoneIpcData::EventPlay(EventPlay { + actor_id: ObjectTypeId { + object_id: ObjectId(self.player_data.actor_id), + object_type: 0, + }, + event_id, + scene, + scene_flags, + unk2: param, + ..Default::default() + }), + }; + + self.queue_segment(PacketSegment { + source_actor: self.player_data.actor_id, + target_actor: self.player_data.actor_id, + segment_type: SegmentType::Ipc { data: ipc }, + }); + } } impl UserData for LuaPlayer { @@ -59,5 +86,12 @@ impl UserData for LuaPlayer { Ok(()) }, ); + methods.add_method_mut( + "play_scene", + |_, this, (event_id, scene, scene_flags, param): (u32, u16, u32, u8)| { + this.play_scene(event_id, scene, scene_flags, param); + Ok(()) + }, + ); } } diff --git a/src/world/mod.rs b/src/world/mod.rs index 44d7d1c..6121f82 100644 --- a/src/world/mod.rs +++ b/src/world/mod.rs @@ -17,3 +17,6 @@ pub use inventory::{EquippedContainer, Inventory, Item}; mod lua; pub use lua::LuaPlayer; + +mod event; +pub use event::Event;