1
Fork 0
mirror of https://github.com/redstrate/Kawari.git synced 2025-04-26 16:37:46 +00:00

Add basic Event scripting capaibilities

This implements the first event script, going from the first Ul'dah opening
cutscene to the next.
This commit is contained in:
Joshua Goins 2025-03-28 21:28:30 -04:00
parent bcbe62af06
commit c4b9ad060b
8 changed files with 142 additions and 38 deletions

View file

@ -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

View file

@ -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);
}
}
}
}

View file

@ -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,
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.event = Some(Event::new("opening/OpeningUldah.lua"));
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;
}
.event
.as_mut()
.unwrap()
.enter_territory(lua_player);
}
_ => tracing::info!("Unrecognized debug command!"),
}

View file

@ -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<Event>,
}
impl ZoneConnection {

52
src/world/event.rs Normal file
View file

@ -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();
}
}

View file

@ -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,
},

View file

@ -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(())
},
);
}
}

View file

@ -17,3 +17,6 @@ pub use inventory::{EquippedContainer, Inventory, Item};
mod lua;
pub use lua::LuaPlayer;
mod event;
pub use event::Event;