1
Fork 0
mirror of https://github.com/redstrate/Kawari.git synced 2025-06-30 11:47:45 +00:00
kawari/src/world/lua.rs

256 lines
7.9 KiB
Rust
Raw Normal View History

2025-05-05 22:28:45 -04:00
use mlua::{FromLua, Lua, LuaSerdeExt, UserData, UserDataFields, UserDataMethods, Value};
2025-03-28 00:26:31 -04:00
use crate::{
2025-05-05 22:28:45 -04:00
common::{ObjectId, ObjectTypeId, Position, timestamp_secs, workdefinitions::RemakeMode},
ipc::zone::{
ActionEffect, DamageElement, DamageKind, DamageType, EffectKind, EventScene,
ServerZoneIpcData, ServerZoneIpcSegment, Warp,
},
opcodes::ServerZoneIpcType,
packet::{PacketSegment, SegmentData, SegmentType},
2025-03-28 00:26:31 -04:00
};
use super::{PlayerData, StatusEffects, Zone};
pub enum Task {
ChangeTerritory { zone_id: u16 },
SetRemakeMode(RemakeMode),
Warp { warp_id: u32 },
BeginLogOut,
FinishEvent { handler_id: u32 },
}
2025-03-28 00:26:31 -04:00
#[derive(Default)]
pub struct LuaPlayer {
pub player_data: PlayerData,
pub status_effects: StatusEffects,
pub queued_segments: Vec<PacketSegment<ServerZoneIpcSegment>>,
pub queued_tasks: Vec<Task>,
2025-03-28 00:26:31 -04:00
}
impl LuaPlayer {
fn queue_segment(&mut self, segment: PacketSegment<ServerZoneIpcSegment>) {
self.queued_segments.push(segment);
}
fn send_message(&mut self, message: &str) {
let ipc = ServerZoneIpcSegment {
op_code: ServerZoneIpcType::ServerChatMessage,
timestamp: timestamp_secs(),
data: ServerZoneIpcData::ServerChatMessage {
message: message.to_string(),
unk: 0,
},
..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: SegmentData::Ipc { data: ipc },
2025-03-28 00:26:31 -04:00
});
}
fn give_status_effect(&mut self, effect_id: u16, duration: f32) {
self.status_effects.add(effect_id, duration);
}
fn play_scene(
&mut self,
target: ObjectTypeId,
event_id: u32,
scene: u16,
scene_flags: u32,
param: u8,
) {
let ipc = ServerZoneIpcSegment {
op_code: ServerZoneIpcType::EventScene,
timestamp: timestamp_secs(),
data: ServerZoneIpcData::EventScene(EventScene {
actor_id: target,
event_id,
scene,
scene_flags,
unk2: param,
..Default::default()
}),
..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: SegmentData::Ipc { data: ipc },
});
}
fn set_position(&mut self, position: Position) {
let ipc = ServerZoneIpcSegment {
op_code: ServerZoneIpcType::Warp,
timestamp: timestamp_secs(),
data: ServerZoneIpcData::Warp(Warp {
position,
..Default::default()
}),
..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: SegmentData::Ipc { data: ipc },
});
}
fn change_territory(&mut self, zone_id: u16) {
self.queued_tasks.push(Task::ChangeTerritory { zone_id });
}
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 });
}
fn begin_log_out(&mut self) {
self.queued_tasks.push(Task::BeginLogOut);
}
fn finish_event(&mut self, handler_id: u32) {
self.queued_tasks.push(Task::FinishEvent { handler_id });
}
2025-03-28 00:26:31 -04:00
}
impl UserData for LuaPlayer {
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
methods.add_method_mut("send_message", |_, this, message: String| {
this.send_message(&message);
Ok(())
});
methods.add_method_mut(
"give_status_effect",
|_, this, (effect_id, duration): (u16, f32)| {
this.give_status_effect(effect_id, duration);
Ok(())
},
);
methods.add_method_mut(
"play_scene",
|_, this, (target, event_id, scene, scene_flags, param): (ObjectTypeId, u32, u16, u32, u8)| {
this.play_scene(target, event_id, scene, scene_flags, param);
Ok(())
},
);
methods.add_method_mut("set_position", |lua, this, position: Value| {
let position: Position = lua.from_value(position).unwrap();
this.set_position(position);
Ok(())
});
methods.add_method_mut("change_territory", |_, this, zone_id: u16| {
this.change_territory(zone_id);
Ok(())
});
methods.add_method_mut("set_remake_mode", |lua, this, mode: Value| {
let mode: RemakeMode = lua.from_value(mode).unwrap();
this.set_remake_mode(mode);
Ok(())
});
methods.add_method_mut("warp", |_, this, warp_id: u32| {
this.warp(warp_id);
Ok(())
});
methods.add_method_mut("begin_log_out", |_, this, _: ()| {
this.begin_log_out();
Ok(())
});
methods.add_method_mut("finish_event", |_, this, handler_id: u32| {
this.finish_event(handler_id);
Ok(())
});
}
2025-05-05 22:28:45 -04:00
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
fields.add_field_method_get("id", |_, this| {
Ok(ObjectTypeId {
object_id: ObjectId(this.player_data.actor_id),
object_type: 0,
})
});
}
}
impl UserData for Position {}
impl UserData for ObjectTypeId {}
impl FromLua for ObjectTypeId {
fn from_lua(value: Value, _: &Lua) -> mlua::Result<Self> {
match value {
Value::UserData(ud) => Ok(*ud.borrow::<Self>()?),
_ => unreachable!(),
}
}
}
impl UserData for Zone {
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
methods.add_method(
"get_pop_range",
2025-03-29 08:34:55 -04:00
|lua: &Lua, this, id: u32| -> mlua::Result<mlua::Value> {
if let Some(pop_range) = this.find_pop_range(id) {
let trans = pop_range.0.transform.translation;
2025-03-29 08:34:55 -04:00
return lua.pack(Position {
x: trans[0],
y: trans[1],
z: trans[2],
});
}
2025-03-29 08:34:55 -04:00
Ok(mlua::Nil)
},
);
2025-03-28 00:26:31 -04:00
}
}
#[derive(Clone, Debug, Default)]
pub struct EffectsBuilder {
pub effects: Vec<ActionEffect>,
}
impl UserData for EffectsBuilder {
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
methods.add_method_mut("damage", |lua, this, (damage_kind, damage_type, damage_element, amount): (Value, Value, Value, u16)| {
let damage_kind: DamageKind = lua.from_value(damage_kind).unwrap();
let damage_type: DamageType = lua.from_value(damage_type).unwrap();
let damage_element: DamageElement = lua.from_value(damage_element).unwrap();
this.effects.push(ActionEffect {
kind: EffectKind::Damage {
damage_kind,
damage_type,
damage_element,
bonus_percent: 0,
unk3: 0,
unk4: 0,
amount,
},
..Default::default()
});
Ok(())
});
}
}
impl FromLua for EffectsBuilder {
fn from_lua(value: Value, _: &Lua) -> mlua::Result<Self> {
match value {
Value::UserData(ud) => Ok(ud.borrow::<Self>()?.clone()),
_ => unreachable!(),
}
}
}