use crate::photon_messages;
use super::convert;
use super::convert::EventList;
use super::events;
use super::id_cache;
use super::party::Party;
use super::player::{DynamicId, StaticId};
use super::unconsumed_messages::UnconsumedMessages;
#[derive(Debug, Default)]
pub struct World {
cache: id_cache::IdCache,
unconsumed_messages: UnconsumedMessages,
main_player_id: Option<StaticId>,
party: Party,
}
impl World {
pub fn new() -> Self {
Self {
..Default::default()
}
}
pub fn transform(&mut self, message: photon_messages::Message) -> Option<Vec<events::Event>> {
match message {
photon_messages::Message::NewCharacter(msg) => {
let mut result = vec![];
let dynamic_id = DynamicId::from(msg.source as u32);
self.assign_dynamic_id(dynamic_id, &msg.character_name);
result.append(&mut EventList::from(self.get_intermediate(msg)?).values()?);
if let Some(messages) = self.unconsumed_messages.get_for_id(dynamic_id) {
for message in messages {
result.append(&mut self.transform(message).unwrap_or(vec![]));
}
}
Some(result)
}
photon_messages::Message::Join(msg) => {
let mut result = vec![];
let dynamic_id = DynamicId::from(msg.source as u32);
self.assign_dynamic_id(dynamic_id, &msg.character_name);
let static_id = self.get_static_id(msg.source)?;
if self.main_player_id.is_none() {
result.push(events::Event::ZoneChange)
}
self.party.set_main_player_name(&msg.character_name);
result.append(&mut EventList::from(self.get_intermediate(msg)?).values()?);
self.main_player_id = Some(static_id);
if let Some(messages) = self.unconsumed_messages.get_for_id(dynamic_id) {
for message in messages {
result.append(&mut self.transform(message).unwrap_or(vec![]));
}
}
Some(result)
}
photon_messages::Message::Leave(msg) => {
let static_id = self.get_static_id(msg.source)?;
if let Some(main_player_id) = self.main_player_id {
if main_player_id == static_id {
return Some(vec![events::Event::ZoneChange]);
}
}
None
}
photon_messages::Message::HealthUpdate(msg) => {
EventList::from(self.get_intermediate(msg)?).values()
}
photon_messages::Message::RegenerationHealthChanged(msg) => {
EventList::from(self.get_intermediate(msg)?).values()
}
photon_messages::Message::KnockedDown(msg) => {
EventList::from(self.get_intermediate(msg)?).values()
}
photon_messages::Message::UpdateFame(msg) => {
EventList::from(self.get_intermediate(msg)?).values()
}
photon_messages::Message::CharacterEquipmentChanged(msg) => {
if let Some(_) = self.get_static_id(msg.source) {
return EventList::from(self.get_intermediate(msg)?).values();
}
let id = DynamicId::from(msg.source as u32);
self.unconsumed_messages.add(
photon_messages::messages::Message::CharacterEquipmentChanged(msg),
id,
);
None
}
photon_messages::Message::ChatSay(msg) => {
EventList::from(self.get_intermediate(msg)?).values()
},
photon_messages::Message::PartyInvitation(_) => None,
photon_messages::Message::PartyJoined(msg) => {
let evt = self.party.joined(msg)?;
Some(vec![evt])
}
photon_messages::Message::PartyDisbanded(_) => {
let evt = self.party.disbanded()?;
Some(vec![evt])
}
photon_messages::Message::PartyPlayerJoined(msg) => {
let evt = self.party.single_player_joined(msg)?;
Some(vec![evt])
}
photon_messages::Message::PartyChangedOrder(_) => None,
photon_messages::Message::PartyPlayerLeft(msg) => {
let evt = self.party.player_left(msg)?;
Some(vec![evt])
}
photon_messages::Message::PartyLeaderChanged(_) => None,
photon_messages::Message::PartyLootSettingChangedPlayer(_) => None,
photon_messages::Message::PartySilverGained(_) => None,
photon_messages::Message::PartyPlayerUpdated(_) => None,
photon_messages::Message::PartyInvitationPlayerBusy(_) => None,
photon_messages::Message::PartyMarkedObjectsUpdated(_) => None,
photon_messages::Message::PartyOnClusterPartyJoined(_) => None,
photon_messages::Message::PartySetRoleFlag(_) => None,
}
}
fn assign_dynamic_id(&mut self, id: DynamicId, name: &str) {
self.cache.save(id, name);
}
fn get_static_id(&self, id: usize) -> Option<StaticId> {
let dynamic_id = DynamicId::from(id as u32);
self.cache.get_static_id(dynamic_id) }
fn get_intermediate<Msg>(&self, msg: Msg) -> Option<convert::EventIntermediate<Msg>> {
Some(convert::EventIntermediate::new(
self.cache.get_dyn_id_to_static_id_map(),
self.cache.get_static_id_to_name_map(),
msg,
))
}
}
#[cfg(test)]
mod tests {
use super::*;
macro_rules! assert_contains {
($container:expr, $value:expr) => {
assert!(
format!("{:?}", $container).contains($value),
"{:?} does not contains {}", $container, $value
);
};
}
macro_rules! simulate_new_player {
($id:expr, $name:expr, $msg:ident) => {
photon_messages::Message::$msg(photon_messages::messages::$msg {
source: $id,
character_name: $name.to_string(),
..Default::default()
});
};
}
#[test]
fn test_player_appeared() {
let mut world = World::new();
let game_message = simulate_new_player!(1, "TestCharacter", NewCharacter);
assert!(world.transform(game_message.clone()).is_some());
assert_contains!(world.transform(game_message.clone()), "PlayerAppeared");
}
#[test]
fn test_main_player_appeared() {
let mut world = World::new();
let game_message = simulate_new_player!(1, "TestCharacter", Join);
assert!(world.transform(game_message.clone()).is_some());
assert_contains!(world.transform(game_message.clone()), "PlayerAppeared");
}
#[test]
fn test_damage_done() {
let mut world = World::new();
let game_message = simulate_new_player!(1, "TestCharacter", Join);
assert!(world.transform(game_message.clone()).is_some());
assert_contains!(world.transform(game_message.clone()), "PlayerAppeared");
let game_message = simulate_new_player!(2, "TestCharacter2", NewCharacter);
assert!(world.transform(game_message.clone()).is_some());
let target = 1;
let game_message =
photon_messages::Message::HealthUpdate(photon_messages::messages::HealthUpdate {
source: 2,
target,
value: -666.0,
..Default::default()
});
assert!(world.transform(game_message.clone()).is_some());
assert_contains!(world.transform(game_message.clone()), "DamageDone");
assert_contains!(world.transform(game_message.clone()), "666");
let target = 1;
let game_message =
photon_messages::Message::HealthUpdate(photon_messages::messages::HealthUpdate {
source: 2,
target,
value: 666.0,
..Default::default()
});
assert!(world.transform(game_message.clone()).is_some());
assert_contains!(world.transform(game_message.clone()), "HealthReceived");
assert_contains!(world.transform(game_message.clone()), "666");
}
#[test]
fn test_zone_change() {
let mut world = World::new();
let game_message = photon_messages::Message::Leave(photon_messages::messages::Leave {
source: 1,
..Default::default()
});
assert!(world.transform(game_message.clone()).is_none());
let game_message = simulate_new_player!(1, "TestCharacter", Join);
assert!(world.transform(game_message.clone()).is_some());
assert_contains!(world.transform(game_message.clone()), "PlayerAppeared");
let game_message = photon_messages::Message::Leave(photon_messages::messages::Leave {
source: 1,
..Default::default()
});
assert_contains!(world.transform(game_message.clone()), "ZoneChange");
let game_message = simulate_new_player!(2, "TestCharacter", NewCharacter);
assert!(world.transform(game_message.clone()).is_some());
let game_message = photon_messages::Message::Leave(photon_messages::messages::Leave {
source: 1,
..Default::default()
});
assert!(world.transform(game_message.clone()).is_none());
}
#[test]
fn test_combat_leave_via_regeneration_change() {
let mut world = World::new();
let game_message = simulate_new_player!(1, "TestCharacter", Join);
assert!(world.transform(game_message.clone()).is_some());
let game_message = photon_messages::Message::RegenerationHealthChanged(
photon_messages::messages::RegenerationHealthChanged {
source: 1,
regeneration_rate: Some(1.0),
..Default::default()
},
);
assert_contains!(world.transform(game_message.clone()), "LeaveCombat");
}
#[test]
fn test_combat_enter_via_regeneration_change() {
let mut world = World::new();
let game_message = simulate_new_player!(1, "TestCharacter", Join);
assert!(world.transform(game_message.clone()).is_some());
let game_message = photon_messages::Message::RegenerationHealthChanged(
photon_messages::messages::RegenerationHealthChanged {
source: 1,
regeneration_rate: None,
..Default::default()
},
);
assert_contains!(world.transform(game_message.clone()), "EnterCombat");
}
#[test]
fn test_combat_enter_via_knockout() {
let mut world = World::new();
let game_message = simulate_new_player!(1, "TestCharacter", Join);
assert!(world.transform(game_message.clone()).is_some());
let game_message =
photon_messages::Message::KnockedDown(photon_messages::messages::KnockedDown {
source: 1,
..Default::default()
});
assert_contains!(world.transform(game_message.clone()), "LeaveCombat");
}
#[test]
fn test_fame_update() {
let mut world = World::new();
let game_message = simulate_new_player!(1, "TestCharacter", Join);
assert!(world.transform(game_message.clone()).is_some());
let game_message =
photon_messages::Message::UpdateFame(photon_messages::messages::UpdateFame {
source: 1,
..Default::default()
});
assert_contains!(world.transform(game_message.clone()), "UpdateFame");
}
}