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:
parent
ff3313a0f9
commit
d54b4c945e
6 changed files with 180 additions and 23 deletions
|
@ -144,6 +144,11 @@
|
||||||
"name": "UpdateHpMpTp",
|
"name": "UpdateHpMpTp",
|
||||||
"opcode": 325,
|
"opcode": 325,
|
||||||
"size": 8
|
"size": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ActionResult",
|
||||||
|
"opcode": 785,
|
||||||
|
"size": 124
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"ClientZoneIpcType": [
|
"ClientZoneIpcType": [
|
||||||
|
|
|
@ -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!");
|
||||||
|
|
|
@ -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 },
|
||||||
})
|
})
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
75
src/world/ipc/action_result.rs
Normal file
75
src/world/ipc/action_result.rs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 {
|
||||||
|
|
Loading…
Add table
Reference in a new issue