1
Fork 0
mirror of https://github.com/redstrate/Kawari.git synced 2025-04-20 14:47:45 +00:00

Start informing the player of the consequences of their actions

This doesn't work 100% reliably yet, but I do see the action appear in
my battle log.
This commit is contained in:
Joshua Goins 2025-03-29 14:14:03 -04:00
parent ff3313a0f9
commit d54b4c945e
6 changed files with 180 additions and 23 deletions

View file

@ -144,6 +144,11 @@
"name": "UpdateHpMpTp", "name": "UpdateHpMpTp",
"opcode": 325, "opcode": 325,
"size": 8 "size": 8
},
{
"name": "ActionResult",
"opcode": 785,
"size": 124
} }
], ],
"ClientZoneIpcType": [ "ClientZoneIpcType": [

View file

@ -2,8 +2,8 @@ use std::sync::{Arc, Mutex};
use kawari::common::custom_ipc::{CustomIpcData, CustomIpcSegment, CustomIpcType}; use kawari::common::custom_ipc::{CustomIpcData, CustomIpcSegment, CustomIpcType};
use kawari::common::{ use kawari::common::{
INVALID_OBJECT_ID, ObjectId, Position, determine_initial_starting_zone, get_citystate, INVALID_OBJECT_ID, ObjectId, ObjectTypeId, Position, determine_initial_starting_zone,
get_world_name, get_citystate, get_world_name,
}; };
use kawari::common::{get_racial_base_attributes, timestamp_secs}; use kawari::common::{get_racial_base_attributes, timestamp_secs};
use kawari::config::get_config; use kawari::config::get_config;
@ -15,8 +15,9 @@ use kawari::packet::{
send_packet, send_packet,
}; };
use kawari::world::ipc::{ use kawari::world::ipc::{
ClientZoneIpcData, CommonSpawn, DisplayFlag, GameMasterCommandType, GameMasterRank, ObjectKind, ActionEffect, ActionResult, ClientZoneIpcData, CommonSpawn, DisplayFlag, GameMasterCommandType,
OnlineStatus, PlayerSubKind, ServerZoneIpcData, ServerZoneIpcSegment, SocialListRequestType, GameMasterRank, ObjectKind, OnlineStatus, PlayerSubKind, ServerZoneIpcData,
ServerZoneIpcSegment, SocialListRequestType,
}; };
use kawari::world::{ use kawari::world::{
ChatHandler, Inventory, Zone, ZoneConnection, ChatHandler, Inventory, Zone, ZoneConnection,
@ -362,7 +363,7 @@ async fn main() {
gm_rank: GameMasterRank::Debug, gm_rank: GameMasterRank::Debug,
online_status: OnlineStatus::GameMasterBlue, online_status: OnlineStatus::GameMasterBlue,
common: CommonSpawn { common: CommonSpawn {
class_job: 35, class_job: 1,
name: chara_details.name, name: chara_details.name,
hp_curr: 100, hp_curr: 100,
hp_max: 100, hp_max: 100,
@ -782,7 +783,7 @@ async fn main() {
println!("Found action: {:#?}", action_row); println!("Found action: {:#?}", action_row);
//if request.target.object_id == INVALID_OBJECT_ID { // placeholder for now
if let Some(actor) = if let Some(actor) =
connection.get_actor(ObjectId(0x106ad804)) connection.get_actor(ObjectId(0x106ad804))
{ {
@ -793,7 +794,8 @@ async fn main() {
.update_hp_mp(actor.id, actor.hp, 10000) .update_hp_mp(actor.id, actor.hp, 10000)
.await; .await;
} }
//} else {
{
let lua = lua.lock().unwrap(); let lua = lua.lock().unwrap();
lua.scope(|scope| { lua.scope(|scope| {
let connection_data = scope let connection_data = scope
@ -808,7 +810,45 @@ async fn main() {
Ok(()) Ok(())
}) })
.unwrap(); .unwrap();
//} }
// tell them the action results
{
let ipc = ServerZoneIpcSegment {
op_code: ServerZoneIpcType::ActionResult,
timestamp: timestamp_secs(),
data: ServerZoneIpcData::ActionResult(
ActionResult {
main_target: ObjectTypeId {
object_id: ObjectId(0x106ad804),
object_type: 0,
},
action_id: 31,
animation_lock_time: 0.6,
rotation: connection.player_data.rotation,
action_animation_id: 31,
flag: 1,
effect_count: 1,
effects: [ActionEffect {
action_type: 3,
value: 50,
..Default::default()
};
8],
..Default::default()
},
),
..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;
}
} }
ClientZoneIpcData::Unk16 { .. } => { ClientZoneIpcData::Unk16 { .. } => {
tracing::info!("Recieved Unk16!"); tracing::info!("Recieved Unk16!");

View file

@ -132,7 +132,7 @@ impl ZoneConnection {
op_code: ServerZoneIpcType::UpdateClassInfo, op_code: ServerZoneIpcType::UpdateClassInfo,
timestamp: timestamp_secs(), timestamp: timestamp_secs(),
data: ServerZoneIpcData::UpdateClassInfo(UpdateClassInfo { data: ServerZoneIpcData::UpdateClassInfo(UpdateClassInfo {
class_id: 35, class_id: 1,
unknown: 1, unknown: 1,
synced_level: 90, synced_level: 90,
class_level: 90, class_level: 90,
@ -348,7 +348,7 @@ impl ZoneConnection {
}; };
self.send_segment(PacketSegment { self.send_segment(PacketSegment {
source_actor: actor_id.0, source_actor: self.player_data.actor_id,
target_actor: actor_id.0, target_actor: actor_id.0,
segment_type: SegmentType::Ipc { data: ipc }, segment_type: SegmentType::Ipc { data: ipc },
}) })

View file

@ -1,6 +1,6 @@
use binrw::binrw; use binrw::binrw;
use crate::common::ObjectTypeId; use crate::common::{ObjectTypeId, read_quantized_rotation, write_quantized_rotation};
#[binrw] #[binrw]
#[derive(Debug, Eq, PartialEq, Clone, Default)] #[derive(Debug, Eq, PartialEq, Clone, Default)]
@ -18,10 +18,38 @@ pub struct ActionRequest {
pub action_kind: ActionKind, pub action_kind: ActionKind,
#[brw(pad_before = 2)] // this ISNT empty #[brw(pad_before = 2)] // this ISNT empty
pub action_id: u32, // See Action Excel sheet pub action_id: u32, // See Action Excel sheet
pub request_id: u32, pub request_id: u16,
#[br(map = read_quantized_rotation)]
#[bw(map = write_quantized_rotation)]
pub rotation: f32,
pub dir: u16, pub dir: u16,
pub dir_target: u16, pub dir_target: u16,
pub target: ObjectTypeId, pub target: ObjectTypeId,
pub arg: u32, pub arg: u32,
pub padding_prob: u32, pub padding_prob: u32,
} }
#[cfg(test)]
mod tests {
use std::{fs::read, io::Cursor, path::PathBuf};
use binrw::BinRead;
use crate::common::ObjectId;
use super::*;
#[test]
fn read_actionrequest() {
let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
d.push("resources/tests/action_request.bin");
let buffer = read(d).unwrap();
let mut buffer = Cursor::new(&buffer);
let action_request = ActionRequest::read_le(&mut buffer).unwrap();
assert_eq!(action_request.target.object_id, ObjectId(0x400097d0));
assert_eq!(action_request.request_id, 0x2);
assert_eq!(action_request.rotation, 1.9694216);
}
}

View file

@ -0,0 +1,75 @@
use binrw::binrw;
use crate::common::{ObjectTypeId, read_quantized_rotation, write_quantized_rotation};
#[binrw]
#[brw(little)]
#[derive(Debug, Clone, Copy, Default)]
pub struct ActionEffect {
pub action_type: u8,
pub param0: u8,
pub param1: u8,
pub param2: u8,
pub param3: u8,
pub param4: u8,
pub value: u16,
}
#[binrw]
#[brw(little)]
#[derive(Debug, Clone, Default)]
pub struct ActionResult {
pub main_target: ObjectTypeId,
pub action_id: u32,
pub unk1: u32,
pub animation_lock_time: f32,
pub unk2: u32,
pub hidden_animation: u16,
#[br(map = read_quantized_rotation)]
#[bw(map = write_quantized_rotation)]
pub rotation: f32,
pub action_animation_id: u16,
pub variation: u8,
pub flag: u8,
pub unk3: u8,
pub effect_count: u8,
pub unk4: u16,
pub unk5: [u8; 6],
#[brw(pad_after = 18)] // idk, target is here too?
pub effects: [ActionEffect; 8],
}
#[cfg(test)]
mod tests {
use std::{fs::read, io::Cursor, path::PathBuf};
use binrw::BinRead;
use crate::common::ObjectId;
use super::*;
#[test]
fn read_actionresult() {
let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
d.push("resources/tests/action_result.bin");
let buffer = read(d).unwrap();
let mut buffer = Cursor::new(&buffer);
let action_result = ActionResult::read_le(&mut buffer).unwrap();
assert_eq!(action_result.main_target.object_id, ObjectId(0x400097d0));
assert_eq!(action_result.action_id, 31);
assert_eq!(action_result.animation_lock_time, 0.6);
assert_eq!(action_result.rotation, 1.9694216);
assert_eq!(action_result.action_animation_id, 31);
assert_eq!(action_result.flag, 1);
assert_eq!(action_result.effect_count, 1);
// effect 0: attack
assert_eq!(action_result.effects[0].action_type, 3);
// effect 1: start action combo
assert_eq!(action_result.effects[1].action_type, 27);
}
}

View file

@ -59,6 +59,9 @@ pub use event_play::EventPlay;
mod event_start; mod event_start;
pub use event_start::EventStart; pub use event_start::EventStart;
mod action_result;
pub use action_result::{ActionEffect, ActionResult};
use crate::common::Position; use crate::common::Position;
use crate::common::read_string; use crate::common::read_string;
use crate::common::write_string; use crate::common::write_string;
@ -222,6 +225,8 @@ pub enum ServerZoneIpcData {
mp: u16, mp: u16,
unk: u16, // it's filled with... something unk: u16, // it's filled with... something
}, },
/// Sent to inform the client the consequences of their actions
ActionResult(ActionResult),
} }
#[binrw] #[binrw]
@ -437,6 +442,10 @@ mod tests {
ServerZoneIpcType::EventStart, ServerZoneIpcType::EventStart,
ServerZoneIpcData::EventStart(EventStart::default()), ServerZoneIpcData::EventStart(EventStart::default()),
), ),
(
ServerZoneIpcType::ActionResult,
ServerZoneIpcData::ActionResult(ActionResult::default()),
),
]; ];
for (opcode, data) in &ipc_types { for (opcode, data) in &ipc_types {