From fec6665d8d5c0e539df1dfa09f4022d4873a7f76 Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Mon, 5 May 2025 22:04:39 -0400 Subject: [PATCH] Make Limsa Inn warp functional This was surprisingly easy, so now this works. You can't exit the inn yet, though! --- .../scripts/warp/WarpInnLimsaLominsa.lua | 7 +++++ src/bin/kawari-world.rs | 23 ++++++++------ src/common/gamedata.rs | 21 +++++++++++++ src/world/connection.rs | 31 +++++++++++++++++++ src/world/event.rs | 14 +++++++++ src/world/lua.rs | 9 ++++++ 6 files changed, 96 insertions(+), 9 deletions(-) diff --git a/resources/scripts/warp/WarpInnLimsaLominsa.lua b/resources/scripts/warp/WarpInnLimsaLominsa.lua index 2c40b4f..5517c33 100644 --- a/resources/scripts/warp/WarpInnLimsaLominsa.lua +++ b/resources/scripts/warp/WarpInnLimsaLominsa.lua @@ -8,3 +8,10 @@ function onTalk(target, player) -- doesn't have inn access --player:play_scene(actorId, EVENT_ID, 00002, 8192, 0) end + +function onReturn(results, player) + if results[1] == 1 then + -- get warp + player:warp(EVENT_ID) + end +end diff --git a/src/bin/kawari-world.rs b/src/bin/kawari-world.rs index 07a7fc2..337406e 100644 --- a/src/bin/kawari-world.rs +++ b/src/bin/kawari-world.rs @@ -135,9 +135,6 @@ async fn client_loop( let lua = connection.lua.clone(); let config = get_config(); - let mut exit_position = None; - let mut exit_rotation = None; - let mut lua_player = LuaPlayer::default(); let mut buf = vec![0; RECEIVE_BUFFER_SIZE]; @@ -162,8 +159,8 @@ async fn client_loop( // collect actor data connection.initialize(actor_id).await; - exit_position = Some(connection.player_data.position); - exit_rotation = Some(connection.player_data.rotation); + connection.exit_position = Some(connection.player_data.position); + connection.exit_rotation = Some(connection.player_data.rotation); // tell the server we exist, now that we confirmed we are a legitimate connection connection.handle.send(ToServer::NewClient(client_handle.clone())).await; @@ -318,7 +315,7 @@ async fn client_loop( // tell the server we loaded into the zone, so it can start sending us acors connection.handle.send(ToServer::ZoneLoaded(connection.id)).await; - let common = connection.get_player_common_spawn(exit_position, exit_rotation); + let common = connection.get_player_common_spawn(connection.exit_position, connection.exit_rotation); // send player spawn { @@ -370,8 +367,8 @@ async fn client_loop( } // wipe any exit position so it isn't accidentally reused - exit_position = None; - exit_rotation = None; + connection.exit_position = None; + connection.exit_rotation = None; // tell the other players we're here connection.handle.send(ToServer::ActorSpawned(connection.id, Actor { id: ObjectId(connection.player_data.actor_id), hp: 100, spawn_index: 0 }, common)).await; @@ -571,7 +568,7 @@ async fn client_loop( .unwrap(); // set the exit position - exit_position = Some(Position { + connection.exit_position = Some(Position { x: destination_object.transform.translation[0], y: destination_object.transform.translation[1], z: destination_object.transform.translation[2], @@ -769,6 +766,12 @@ async fn client_loop( ClientZoneIpcData::EventHandlerReturn { handler_id, scene, error_code, num_results, results } => { tracing::info!("Finishing this event... {handler_id} {scene} {error_code} {num_results} {results:#?}"); + connection + .event + .as_mut() + .unwrap() + .finish(results, &mut lua_player); + { // TODO: handle in lua script let ipc = ServerZoneIpcSegment { @@ -969,6 +972,8 @@ async fn main() { database: database.clone(), lua: lua.clone(), gamedata: game_data.clone(), + exit_position: None, + exit_rotation: None, }); } Some((mut socket, _)) = handle_rcon(&rcon_listener) => { diff --git a/src/common/gamedata.rs b/src/common/gamedata.rs index f8579bd..9fc131e 100644 --- a/src/common/gamedata.rs +++ b/src/common/gamedata.rs @@ -122,4 +122,25 @@ impl GameData { None } + + /// Returns the pop range object id that's associated with the warp id + pub fn get_warp(&mut self, warp_id: u32) -> (u32, u16) { + let exh = self.game_data.read_excel_sheet_header("Warp").unwrap(); + let exd = self + .game_data + .read_excel_sheet("Warp", &exh, Language::English, 0) + .unwrap(); + + let row = &exd.read_row(&exh, warp_id).unwrap()[0]; + + let physis::exd::ColumnData::UInt32(pop_range_id) = &row.data[0] else { + panic!("Unexpected type!"); + }; + + let physis::exd::ColumnData::UInt16(zone_id) = &row.data[1] else { + panic!("Unexpected type!"); + }; + + (*pop_range_id, *zone_id) + } } diff --git a/src/world/connection.rs b/src/world/connection.rs index 800a295..a2e8f20 100644 --- a/src/world/connection.rs +++ b/src/world/connection.rs @@ -152,6 +152,9 @@ pub struct ZoneConnection { pub database: Arc, pub lua: Arc>, pub gamedata: Arc>, + + pub exit_position: Option, + pub exit_rotation: Option, } impl ZoneConnection { @@ -422,6 +425,31 @@ impl ZoneConnection { } } + pub async fn warp(&mut self, warp_id: u32) { + let territory_type; + // find the pop range on the other side + { + let mut game_data = self.gamedata.lock().unwrap(); + let (pop_range_id, zone_id) = game_data.get_warp(warp_id); + + let new_zone = Zone::load(&mut game_data.game_data, zone_id); + + // find it on the other side + let (object, _) = new_zone.find_pop_range(pop_range_id).unwrap(); + + // set the exit position + self.exit_position = Some(Position { + x: object.transform.translation[0], + y: object.transform.translation[1], + z: object.transform.translation[2], + }); + + territory_type = zone_id; + } + + self.change_zone(territory_type as u16).await; + } + pub async fn change_weather(&mut self, new_weather_id: u16) { let ipc = ServerZoneIpcSegment { op_code: ServerZoneIpcType::WeatherId, @@ -569,6 +597,9 @@ impl ZoneConnection { Task::SetRemakeMode(remake_mode) => self .database .set_remake_mode(player.player_data.content_id, *remake_mode), + Task::Warp { warp_id } => { + self.warp(*warp_id).await; + } } } player.queued_tasks.clear(); diff --git a/src/world/event.rs b/src/world/event.rs index ce4c5d5..6ea1f80 100644 --- a/src/world/event.rs +++ b/src/world/event.rs @@ -64,4 +64,18 @@ impl Event { }) .unwrap(); } + + pub fn finish(&mut self, results: &[u32], player: &mut LuaPlayer) { + self.lua + .scope(|scope| { + let player = scope.create_userdata_ref_mut(player).unwrap(); + + let func: Function = self.lua.globals().get("onReturn").unwrap(); + + func.call::<()>((results, player)).unwrap(); + + Ok(()) + }) + .unwrap(); + } } diff --git a/src/world/lua.rs b/src/world/lua.rs index fc1bd5a..a47f533 100644 --- a/src/world/lua.rs +++ b/src/world/lua.rs @@ -15,6 +15,7 @@ use super::{PlayerData, StatusEffects, Zone}; pub enum Task { ChangeTerritory { zone_id: u16 }, SetRemakeMode(RemakeMode), + Warp { warp_id: u32 }, } #[derive(Default)] @@ -109,6 +110,10 @@ impl LuaPlayer { fn set_remake_mode(&mut self, mode: RemakeMode) { self.queued_tasks.push(Task::SetRemakeMode(mode)); } + + fn warp(&mut self, warp_id: u32) { + self.queued_tasks.push(Task::Warp { warp_id }); + } } impl UserData for LuaPlayer { @@ -144,6 +149,10 @@ impl UserData for LuaPlayer { this.set_remake_mode(mode); Ok(()) }); + methods.add_method_mut("warp", |_, this, warp_id: u32| { + this.warp(warp_id); + Ok(()) + }); } }