diff --git a/src/bin/kawari-world.rs b/src/bin/kawari-world.rs index 4de7ec5..07295e5 100644 --- a/src/bin/kawari-world.rs +++ b/src/bin/kawari-world.rs @@ -837,12 +837,13 @@ async fn client_loop( } msg = internal_recv.recv() => match msg { Some(msg) => match msg { - FromServer::Message(msg)=> connection.send_message(&msg).await, + FromServer::Message(msg) => connection.send_message(&msg).await, FromServer::ActorSpawn(actor, common) => connection.spawn_actor(actor, common).await, FromServer::ActorMove(actor_id, position, rotation) => connection.set_actor_position(actor_id, position, rotation).await, FromServer::ActorDespawn(actor_id) => connection.remove_actor(actor_id).await, FromServer::ActorControl(actor_id, actor_control) => connection.actor_control(actor_id, actor_control).await, FromServer::ActorControlTarget(actor_id, actor_control) => connection.actor_control_target(actor_id, actor_control).await, + FromServer::SpawnNPC(npc) => connection.send_npc(npc).await, }, None => break, } diff --git a/src/world/chat_handler.rs b/src/world/chat_handler.rs index 12bb5f7..23ee364 100644 --- a/src/world/chat_handler.rs +++ b/src/world/chat_handler.rs @@ -6,7 +6,7 @@ use crate::{ }, opcodes::ServerZoneIpcType, packet::{PacketSegment, SegmentData, SegmentType}, - world::{Actor, Event}, + world::{Event, ToServer}, }; use super::{LuaPlayer, ZoneConnection}; @@ -102,50 +102,10 @@ impl ChatHandler { .await; } "!spawnmonster" => { - let spawn_index = connection.get_free_spawn_index(); - - // spawn a tiny mandragora - { - let ipc = ServerZoneIpcSegment { - op_code: ServerZoneIpcType::NpcSpawn, - timestamp: timestamp_secs(), - data: ServerZoneIpcData::NpcSpawn(NpcSpawn { - aggression_mode: 1, - common: CommonSpawn { - hp_curr: 91, - hp_max: 91, - mp_curr: 100, - mp_max: 100, - spawn_index, - bnpc_base: 13498, // TODO: changing this prevents it from spawning... - bnpc_name: 405, - object_kind: ObjectKind::BattleNpc(BattleNpcSubKind::Enemy), - level: 1, - battalion: 4, - model_chara: 297, - pos: connection.player_data.position, - ..Default::default() - }, - ..Default::default() - }), - ..Default::default() - }; - - connection - .send_segment(PacketSegment { - source_actor: 0x106ad804, - target_actor: connection.player_data.actor_id, - segment_type: SegmentType::Ipc, - data: SegmentData::Ipc { data: ipc }, - }) - .await; - } - - connection.actors.push(Actor { - id: ObjectId(0x106ad804), - hp: 91, - spawn_index: spawn_index as u32, - }); + connection + .handle + .send(ToServer::DebugNewNpc(connection.id)) + .await; } "!playscene" => { let parts: Vec<&str> = chat_message.message.split(' ').collect(); diff --git a/src/world/common.rs b/src/world/common.rs index 79a6b25..d84f881 100644 --- a/src/world/common.rs +++ b/src/world/common.rs @@ -10,7 +10,7 @@ use tokio::sync::mpsc::Sender; use crate::{ common::Position, - ipc::zone::{ActorControl, ActorControlTarget, ClientTrigger, CommonSpawn}, + ipc::zone::{ActorControl, ActorControlTarget, ClientTrigger, CommonSpawn, NpcSpawn}, }; use super::Actor; @@ -31,6 +31,8 @@ pub enum FromServer { ActorControl(u32, ActorControl), /// We need to update an actor's target' ActorControlTarget(u32, ActorControlTarget), + /// Spawn an NPC + SpawnNPC(NpcSpawn), } #[derive(Debug, Clone)] @@ -82,6 +84,7 @@ pub enum ToServer { Disconnected(ClientId), /// A fatal error occured. FatalError(std::io::Error), + DebugNewNpc(ClientId), } #[derive(Clone, Debug)] diff --git a/src/world/connection.rs b/src/world/connection.rs index 878bb84..4e16633 100644 --- a/src/world/connection.rs +++ b/src/world/connection.rs @@ -770,4 +770,24 @@ impl ZoneConnection { }) .await; } + + pub async fn send_npc(&mut self, mut npc: NpcSpawn) { + // the one from the global state is useless, of course + npc.common.spawn_index = self.get_free_spawn_index(); + + let ipc = ServerZoneIpcSegment { + op_code: ServerZoneIpcType::NpcSpawn, + timestamp: timestamp_secs(), + data: ServerZoneIpcData::NpcSpawn(npc), + ..Default::default() + }; + + self.send_segment(PacketSegment { + source_actor: 0x106ad804, + target_actor: self.player_data.actor_id, + segment_type: SegmentType::Ipc, + data: SegmentData::Ipc { data: ipc }, + }) + .await; + } } diff --git a/src/world/server.rs b/src/world/server.rs index 3ba8a53..d7d8a04 100644 --- a/src/world/server.rs +++ b/src/world/server.rs @@ -2,9 +2,10 @@ use std::collections::HashMap; use tokio::sync::mpsc::Receiver; use crate::{ - common::ObjectId, + common::{ObjectId, Position}, ipc::zone::{ - ActorControl, ActorControlCategory, ActorControlTarget, ClientTriggerCommand, CommonSpawn, + ActorControl, ActorControlCategory, ActorControlTarget, BattleNpcSubKind, + ClientTriggerCommand, CommonSpawn, NpcSpawn, ObjectKind, }, }; @@ -263,6 +264,35 @@ pub async fn server_main_loop(mut recv: Receiver) -> Result<(), std::i } } } + ToServer::DebugNewNpc(_from_id) => { + for (id, (handle, _)) in &mut data.clients { + let id = *id; + + let msg = FromServer::SpawnNPC(NpcSpawn { + aggression_mode: 1, + common: CommonSpawn { + hp_curr: 91, + hp_max: 91, + mp_curr: 100, + mp_max: 100, + spawn_index: 0, // not needed at this level + bnpc_base: 13498, // TODO: changing this prevents it from spawning... + bnpc_name: 405, + object_kind: ObjectKind::BattleNpc(BattleNpcSubKind::Enemy), + level: 1, + battalion: 4, + model_chara: 297, + pos: Position::default(), + ..Default::default() + }, + ..Default::default() + }); + + if handle.send(msg).is_err() { + to_remove.push(id); + } + } + } ToServer::Disconnected(from_id) => { to_remove.push(from_id); }