diff --git a/CHANGELOG.md b/CHANGELOG.md index b39ce9ef61..9a0a696124 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -58,6 +58,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Convert giant trees to site2 - Add new upgraded travelers - Wallrunning +- Waypoints saved between sessions and shared with group members. ### Changed diff --git a/assets/voxygen/element/ui/map/buttons/location_marker_group.png b/assets/voxygen/element/ui/map/buttons/location_marker_group.png new file mode 100644 index 0000000000..2afd7eb4bb --- /dev/null +++ b/assets/voxygen/element/ui/map/buttons/location_marker_group.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:23f3a992e13961571073b4b7663b0ce5c6090fe20d64fb973992bcad27ed103b +size 666 diff --git a/assets/voxygen/i18n/en/hud/map.ron b/assets/voxygen/i18n/en/hud/map.ron index 17d9ecb948..efd688821f 100644 --- a/assets/voxygen/i18n/en/hud/map.ron +++ b/assets/voxygen/i18n/en/hud/map.ron @@ -32,6 +32,7 @@ "hud.map.toggle_minimap_voxel": "Toggle Minimap Voxel View", "hud.map.zoom_minimap_explanation": "Zoom in the Minimap to see\nthe area around you in higher detail", "hud.map.gnarling": "Gnarling Fortification", + "hud.map.placed_by": "Placed by {name}", }, diff --git a/client/src/lib.rs b/client/src/lib.rs index 27eace9afe..c26cbb52d2 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -30,7 +30,7 @@ use common::{ slot::{EquipSlot, InvSlotId, Slot}, CharacterState, ChatMode, ControlAction, ControlEvent, Controller, ControllerInputs, GroupManip, InputKind, InventoryAction, InventoryEvent, InventoryUpdateEvent, - UtteranceKind, + MapMarkerChange, UtteranceKind, }, event::{EventBus, LocalEvent}, grid::Grid, @@ -112,6 +112,7 @@ pub enum Event { CharacterCreated(CharacterId), CharacterEdited(CharacterId), CharacterError(String), + MapMarker(comp::MapMarkerUpdate), } pub struct WorldData { @@ -759,7 +760,8 @@ impl Client { | ClientGeneral::UnlockSkillGroup(_) | ClientGeneral::RequestPlayerPhysics { .. } | ClientGeneral::RequestLossyTerrainCompression { .. } - | ClientGeneral::AcknowledgePersistenceLoadError => { + | ClientGeneral::AcknowledgePersistenceLoadError + | ClientGeneral::UpdateMapMarker(_) => { #[cfg(feature = "tracy")] { ingame = 1.0; @@ -1190,6 +1192,10 @@ impl Client { } } + pub fn map_marker_event(&mut self, event: MapMarkerChange) { + self.send_msg(ClientGeneral::UpdateMapMarker(event)); + } + /// Checks whether a player can swap their weapon+ability `Loadout` settings /// and sends the `ControlAction` event that signals to do the swap. pub fn swap_loadout(&mut self) { self.control_action(ControlAction::SwapEquippedWeapons) } @@ -1909,6 +1915,9 @@ impl Client { self.personalize_alias(uid, player_info.player_alias.clone()) )), )); + frontend_events.push(Event::MapMarker( + comp::MapMarkerUpdate::GroupMember(uid, MapMarkerChange::Remove), + )); } if self.group_members.remove(&uid).is_none() { warn!( @@ -1931,10 +1940,12 @@ impl Client { if let Some(uid) = self.uid() { self.group_members.remove(&uid); } + frontend_events.push(Event::MapMarker(comp::MapMarkerUpdate::ClearGroup)); }, NoGroup => { self.group_leader = None; self.group_members = HashMap::new(); + frontend_events.push(Event::MapMarker(comp::MapMarkerUpdate::ClearGroup)); }, } }, @@ -2032,6 +2043,9 @@ impl Client { rich.economy = Some(economy); } }, + ServerGeneral::MapMarker(event) => { + frontend_events.push(Event::MapMarker(event)); + }, _ => unreachable!("Not a in_game message"), } Ok(()) diff --git a/common/net/src/msg/client.rs b/common/net/src/msg/client.rs index a4030044d3..9a7b9495b0 100644 --- a/common/net/src/msg/client.rs +++ b/common/net/src/msg/client.rs @@ -78,6 +78,7 @@ pub enum ClientGeneral { UnlockSkill(Skill), UnlockSkillGroup(SkillGroupKind), RequestSiteInfo(SiteId), + UpdateMapMarker(comp::MapMarkerChange), //Only in Game, via terrain stream TerrainChunkRequest { key: Vec2, @@ -132,7 +133,8 @@ impl ClientMsg { | ClientGeneral::UnlockSkillGroup(_) | ClientGeneral::RequestPlayerPhysics { .. } | ClientGeneral::RequestLossyTerrainCompression { .. } - | ClientGeneral::AcknowledgePersistenceLoadError => { + | ClientGeneral::AcknowledgePersistenceLoadError + | ClientGeneral::UpdateMapMarker(_) => { c_type == ClientType::Game && presence.is_some() }, //Always possible diff --git a/common/net/src/msg/server.rs b/common/net/src/msg/server.rs index 3238762f41..04d413b633 100644 --- a/common/net/src/msg/server.rs +++ b/common/net/src/msg/server.rs @@ -189,6 +189,7 @@ pub enum ServerGeneral { FinishedTrade(TradeResult), /// Economic information about sites SiteEconomy(EconomyInfo), + MapMarker(comp::MapMarkerUpdate), } impl ServerGeneral { @@ -298,7 +299,8 @@ impl ServerMsg { | ServerGeneral::Knockback(_) | ServerGeneral::UpdatePendingTrade(_, _, _) | ServerGeneral::FinishedTrade(_) - | ServerGeneral::SiteEconomy(_) => { + | ServerGeneral::SiteEconomy(_) + | ServerGeneral::MapMarker(_) => { c_type == ClientType::Game && presence.is_some() }, // Always possible diff --git a/common/src/comp/group.rs b/common/src/comp/group.rs index 813fb827e1..b9fa5e218c 100644 --- a/common/src/comp/group.rs +++ b/common/src/comp/group.rs @@ -62,9 +62,9 @@ pub enum ChangeNotification { // Also note when the same notification is sent to multiple destinations the // mapping might be duplicated effort impl ChangeNotification { - pub fn try_map(self, f: impl Fn(E) -> Option) -> Option> { + pub fn try_map_ref(&self, f: impl Fn(&E) -> Option) -> Option> { match self { - Self::Added(e, r) => f(e).map(|t| ChangeNotification::Added(t, r)), + Self::Added(e, r) => f(e).map(|t| ChangeNotification::Added(t, *r)), Self::Removed(e) => f(e).map(ChangeNotification::Removed), Self::NewLeader(e) => f(e).map(ChangeNotification::NewLeader), // Note just discards members that fail map @@ -72,8 +72,8 @@ impl ChangeNotification { f(leader).map(|leader| ChangeNotification::NewGroup { leader, members: members - .into_iter() - .filter_map(|(e, r)| f(e).map(|t| (t, r))) + .iter() + .filter_map(|(e, r)| f(e).map(|t| (t, *r))) .collect(), }) }, diff --git a/common/src/comp/location.rs b/common/src/comp/location.rs index 7ecef35163..222cc2247d 100644 --- a/common/src/comp/location.rs +++ b/common/src/comp/location.rs @@ -1,4 +1,4 @@ -use crate::resources::Time; +use crate::{resources::Time, uid::Uid}; use serde::{Deserialize, Serialize}; use specs::Component; use specs_idvs::IdvStorage; @@ -45,3 +45,25 @@ impl Component for WaypointArea { impl Default for WaypointArea { fn default() -> Self { Self(5.0) } } + +/// Marker on the map, used for sharing waypoint with group and +/// persisting it server side. +#[derive(Copy, Clone, Debug, Serialize, Deserialize)] +pub struct MapMarker(pub Vec2); + +impl Component for MapMarker { + type Storage = IdvStorage; +} + +#[derive(Copy, Clone, Debug, Serialize, Deserialize)] +pub enum MapMarkerChange { + Update(Vec2), + Remove, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub enum MapMarkerUpdate { + Owned(MapMarkerChange), + GroupMember(Uid, MapMarkerChange), + ClearGroup, +} diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs index 2fa066f0d3..1640fe0c1a 100644 --- a/common/src/comp/mod.rs +++ b/common/src/comp/mod.rs @@ -90,7 +90,7 @@ pub use self::{ slot, Inventory, InventoryUpdate, InventoryUpdateEvent, }, last::Last, - location::{Waypoint, WaypointArea}, + location::{MapMarker, MapMarkerChange, MapMarkerUpdate, Waypoint, WaypointArea}, melee::{Melee, MeleeConstructor}, misc::Object, ori::Ori, diff --git a/common/src/event.rs b/common/src/event.rs index cea01301a5..035cf94b92 100644 --- a/common/src/event.rs +++ b/common/src/event.rs @@ -115,6 +115,7 @@ pub enum ServerEvent { Option, Vec<(comp::Pet, comp::Body, comp::Stats)>, comp::ActiveAbilities, + Option, ), }, ExitIngame { @@ -209,6 +210,10 @@ pub enum ServerEvent { auxiliary_key: comp::ability::AuxiliaryKey, new_ability: comp::ability::AuxiliaryAbility, }, + UpdateMapMarker { + entity: EcsEntity, + update: comp::MapMarkerChange, + }, } pub struct EventBus { diff --git a/common/state/src/state.rs b/common/state/src/state.rs index 09bc81f754..29a3bf36d6 100644 --- a/common/state/src/state.rs +++ b/common/state/src/state.rs @@ -191,6 +191,7 @@ impl State { ecs.register::(); ecs.register::(); ecs.register::(); + ecs.register::(); ecs.register::(); ecs.register::(); ecs.register::(); diff --git a/server/src/character_creator.rs b/server/src/character_creator.rs index a93c7a3ea2..e1cfbf0541 100644 --- a/server/src/character_creator.rs +++ b/server/src/character_creator.rs @@ -64,6 +64,7 @@ pub fn create_character( .expect("Inventory has at least 1 slot left!"); let waypoint = None; + let map_marker = None; character_updater.create_character(entity, player_uuid, character_alias, PersistedComponents { body, @@ -73,6 +74,7 @@ pub fn create_character( waypoint, pets: Vec::new(), active_abilities: Default::default(), + map_marker, }); Ok(()) } diff --git a/server/src/client.rs b/server/src/client.rs index 19bd322049..f917b0fa46 100644 --- a/server/src/client.rs +++ b/server/src/client.rs @@ -185,7 +185,8 @@ impl Client { | ServerGeneral::Knockback(_) | ServerGeneral::SiteEconomy(_) | ServerGeneral::UpdatePendingTrade(_, _, _) - | ServerGeneral::FinishedTrade(_) => { + | ServerGeneral::FinishedTrade(_) + | ServerGeneral::MapMarker(_) => { PreparedMsg::new(2, &g, &self.in_game_stream_params) }, //Ingame related, terrain diff --git a/server/src/events/entity_creation.rs b/server/src/events/entity_creation.rs index 3b49e2ca20..4aaa872aa7 100644 --- a/server/src/events/entity_creation.rs +++ b/server/src/events/entity_creation.rs @@ -22,6 +22,8 @@ use specs::{Builder, Entity as EcsEntity, WorldExt}; use std::time::Duration; use vek::{Rgb, Vec3}; +use super::group_manip::update_map_markers; + pub fn handle_initialize_character( server: &mut Server, entity: EcsEntity, @@ -35,6 +37,14 @@ pub fn handle_loaded_character_data( entity: EcsEntity, loaded_components: PersistedComponents, ) { + if let Some(marker) = loaded_components.map_marker { + server.notify_client( + entity, + ServerGeneral::MapMarker(comp::MapMarkerUpdate::Owned(comp::MapMarkerChange::Update( + marker.0, + ))), + ); + } server .state .update_character_data(entity, loaded_components); @@ -103,6 +113,7 @@ pub fn handle_create_npc( let uids = state.ecs().read_storage::(); let mut group_manager = state.ecs().write_resource::(); if let Some(owner) = state.ecs().entity_from_uid(owner_uid.into()) { + let map_markers = state.ecs().read_storage::(); group_manager.new_pet( new_entity, owner, @@ -115,10 +126,13 @@ pub fn handle_create_npc( .get(entity) .and_then(|c| { group_change - .try_map(|e| uids.get(e).copied()) + .try_map_ref(|e| uids.get(*e).copied()) .map(|g| (g, c)) }) .map(|(g, c)| { + // Might be unneccessary, but maybe pets can somehow have map + // markers in the future + update_map_markers(&map_markers, &uids, c, &group_change); c.send_fallible(ServerGeneral::GroupUpdate(g)); }); }, diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index a975731c14..9f799aa8fd 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -1267,3 +1267,41 @@ pub fn handle_change_ability( ); } } + +pub fn handle_update_map_marker( + server: &mut Server, + entity: EcsEntity, + update: comp::MapMarkerChange, +) { + use comp::{MapMarker, MapMarkerChange::*}; + match update { + Update(waypoint) => { + server + .state + .write_component_ignore_entity_dead(entity, MapMarker(waypoint)); + }, + Remove => { + server.state.delete_component::(entity); + }, + } + let ecs = server.state.ecs_mut(); + // Send updated waypoint to group members + let groups = ecs.read_storage(); + let uids = ecs.read_storage(); + if let Some((group_id, uid)) = groups.get(entity).zip(uids.get(entity)) { + let clients = ecs.read_storage::(); + for client in comp::group::members( + *group_id, + &groups, + &ecs.entities(), + &ecs.read_storage(), + &uids, + ) + .filter_map(|(e, _)| if e != entity { clients.get(e) } else { None }) + { + client.send_fallible(ServerGeneral::MapMarker( + comp::MapMarkerUpdate::GroupMember(*uid, update), + )); + } + } +} diff --git a/server/src/events/group_manip.rs b/server/src/events/group_manip.rs index 3d175fd26f..754762d0d9 100644 --- a/server/src/events/group_manip.rs +++ b/server/src/events/group_manip.rs @@ -2,7 +2,7 @@ use crate::{client::Client, Server}; use common::{ comp::{ self, - group::{self, Group, GroupManager}, + group::{self, ChangeNotification, Group, GroupManager}, invite::{InviteKind, PendingInvites}, ChatType, GroupManip, }, @@ -81,6 +81,37 @@ pub fn can_invite( true } +pub fn update_map_markers<'a>( + map_markers: &ReadStorage<'a, comp::MapMarker>, + uids: &ReadStorage<'a, Uid>, + client: &Client, + change: &ChangeNotification, +) { + use comp::group::ChangeNotification::*; + let send_update = |entity| { + if let (Some(map_marker), Some(uid)) = (map_markers.get(entity), uids.get(entity)) { + client.send_fallible(ServerGeneral::MapMarker( + comp::MapMarkerUpdate::GroupMember( + *uid, + comp::MapMarkerChange::Update(map_marker.0), + ), + )); + } + }; + match change { + &Added(entity, _) => { + send_update(entity); + }, + NewGroup { leader: _, members } => { + for (entity, _) in members { + send_update(*entity); + } + }, + // Removed and NoGroup can be inferred by the client, NewLeader does not affect map markers + Removed(_) | NoGroup | NewLeader(_) => {}, + } +} + // TODO: turn chat messages into enums pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupManip) { match manip { @@ -89,6 +120,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani let clients = state.ecs().read_storage::(); let uids = state.ecs().read_storage::(); let mut group_manager = state.ecs().write_resource::(); + let map_markers = state.ecs().read_storage::(); group_manager.leave_group( entity, &mut state.ecs().write_storage(), @@ -100,10 +132,13 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani .get(entity) .and_then(|c| { group_change - .try_map(|e| uids.get(e).copied()) + .try_map_ref(|e| uids.get(*e).copied()) .map(|g| (g, c)) }) - .map(|(g, c)| c.send(ServerGeneral::GroupUpdate(g))); + .map(|(g, c)| { + update_map_markers(&map_markers, &uids, c, &group_change); + c.send_fallible(ServerGeneral::GroupUpdate(g)); + }); }, ); }, @@ -151,6 +186,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani let mut groups = state.ecs().write_storage::(); let mut group_manager = state.ecs().write_resource::(); + let map_markers = state.ecs().read_storage::(); // Make sure kicker is the group leader match groups .get(target) @@ -169,10 +205,13 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani .get(entity) .and_then(|c| { group_change - .try_map(|e| uids.get(e).copied()) + .try_map_ref(|e| uids.get(*e).copied()) .map(|g| (g, c)) }) - .map(|(g, c)| c.send(ServerGeneral::GroupUpdate(g))); + .map(|(g, c)| { + update_map_markers(&map_markers, &uids, c, &group_change); + c.send_fallible(ServerGeneral::GroupUpdate(g)); + }); }, ); @@ -230,6 +269,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani }; let groups = state.ecs().read_storage::(); let mut group_manager = state.ecs().write_resource::(); + let map_markers = state.ecs().read_storage::(); // Make sure assigner is the group leader match groups .get(target) @@ -248,10 +288,13 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani .get(entity) .and_then(|c| { group_change - .try_map(|e| uids.get(e).copied()) + .try_map_ref(|e| uids.get(*e).copied()) .map(|g| (g, c)) }) - .map(|(g, c)| c.send(ServerGeneral::GroupUpdate(g))); + .map(|(g, c)| { + update_map_markers(&map_markers, &uids, c, &group_change); + c.send_fallible(ServerGeneral::GroupUpdate(g)); + }); }, ); // Tell them they are the leader diff --git a/server/src/events/invite.rs b/server/src/events/invite.rs index 47b01728bd..0f68063414 100644 --- a/server/src/events/invite.rs +++ b/server/src/events/invite.rs @@ -1,4 +1,4 @@ -use super::group_manip; +use super::group_manip::{self, update_map_markers}; use crate::{client::Client, Server}; use common::{ comp::{ @@ -202,6 +202,7 @@ pub fn handle_invite_accept(server: &mut Server, entity: specs::Entity) { match kind { InviteKind::Group => { + let map_markers = state.ecs().read_storage::(); let mut group_manager = state.ecs().write_resource::(); group_manager.add_group_member( inviter, @@ -215,10 +216,13 @@ pub fn handle_invite_accept(server: &mut Server, entity: specs::Entity) { .get(entity) .and_then(|c| { group_change - .try_map(|e| uids.get(e).copied()) + .try_map_ref(|e| uids.get(*e).copied()) .map(|g| (g, c)) }) - .map(|(g, c)| c.send(ServerGeneral::GroupUpdate(g))); + .map(|(g, c)| { + update_map_markers(&map_markers, &uids, c, &group_change); + c.send_fallible(ServerGeneral::GroupUpdate(g)); + }); }, ); }, diff --git a/server/src/events/mod.rs b/server/src/events/mod.rs index 56ad177b26..6bc384a1f4 100644 --- a/server/src/events/mod.rs +++ b/server/src/events/mod.rs @@ -12,7 +12,7 @@ use entity_manipulation::{ handle_aura, handle_bonk, handle_buff, handle_change_ability, handle_combo_change, handle_delete, handle_destroy, handle_energy_change, handle_entity_attacked_hook, handle_explosion, handle_health_change, handle_knockback, handle_land_on_ground, handle_parry, - handle_poise, handle_respawn, handle_teleport_to, + handle_poise, handle_respawn, handle_teleport_to, handle_update_map_marker, }; use group_manip::handle_group; use information::handle_site_info; @@ -26,6 +26,8 @@ use player::{handle_client_disconnect, handle_exit_ingame}; use specs::{Builder, Entity as EcsEntity, WorldExt}; use trade::{cancel_trade_for, handle_process_trade_action}; +pub use group_manip::update_map_markers; + mod entity_creation; mod entity_manipulation; mod group_manip; @@ -132,8 +134,16 @@ impl Server { character_id, } => handle_initialize_character(self, entity, character_id), ServerEvent::UpdateCharacterData { entity, components } => { - let (body, stats, skill_set, inventory, waypoint, pets, active_abilities) = - components; + let ( + body, + stats, + skill_set, + inventory, + waypoint, + pets, + active_abilities, + map_marker, + ) = components; let components = PersistedComponents { body, stats, @@ -142,6 +152,7 @@ impl Server { waypoint, pets, active_abilities, + map_marker, }; handle_loaded_character_data(self, entity, components); }, @@ -253,6 +264,9 @@ impl Server { auxiliary_key, new_ability, } => handle_change_ability(self, entity, slot, auxiliary_key, new_ability), + ServerEvent::UpdateMapMarker { entity, update } => { + handle_update_map_marker(self, entity, update) + }, } } diff --git a/server/src/events/player.rs b/server/src/events/player.rs index 55113c75a4..e06e8300e1 100644 --- a/server/src/events/player.rs +++ b/server/src/events/player.rs @@ -226,6 +226,11 @@ fn persist_entity(state: &mut State, entity: EcsEntity) -> EcsEntity { .read_storage::() .get(entity) .cloned(); + let map_marker = state + .ecs() + .read_storage::() + .get(entity) + .cloned(); // Store last battle mode change if let Some(change) = player_info.last_battlemode_change { let mode = player_info.battle_mode; @@ -261,6 +266,7 @@ fn persist_entity(state: &mut State, entity: EcsEntity) -> EcsEntity { pets, waypoint, active_abilities.clone(), + map_marker, ), ); }, diff --git a/server/src/lib.rs b/server/src/lib.rs index d77cc6c68a..94a552b5c0 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -887,6 +887,7 @@ impl Server { waypoint, pets, active_abilities, + map_marker, } = character_data; let character_data = ( body, @@ -896,6 +897,7 @@ impl Server { waypoint, pets, active_abilities, + map_marker, ); ServerEvent::UpdateCharacterData { entity: query_result.entity, diff --git a/server/src/persistence/character.rs b/server/src/persistence/character.rs index 6517d49d5b..996aee1262 100644 --- a/server/src/persistence/character.rs +++ b/server/src/persistence/character.rs @@ -148,19 +148,22 @@ pub fn load_character_data( }, )?; - let char_waypoint = character_data.waypoint.as_ref().and_then(|x| { - match convert_waypoint_from_database_json(x) { - Ok(w) => Some(w), - Err(e) => { - warn!( - "Error reading waypoint from database for character ID + let (char_waypoint, char_map_marker) = match character_data + .waypoint + .as_ref() + .map(|x| convert_waypoint_from_database_json(x)) + { + Some(Ok(w)) => w, + Some(Err(e)) => { + warn!( + "Error reading waypoint from database for character ID {}, error: {}", - char_id, e - ); - None - }, - } - }); + char_id, e + ); + (None, None) + }, + None => (None, None), + }; let mut stmt = connection.prepare_cached( " @@ -261,6 +264,7 @@ pub fn load_character_data( waypoint: char_waypoint, pets, active_abilities: convert_active_abilities_from_database(&ability_set_data), + map_marker: char_map_marker, }) } @@ -354,6 +358,7 @@ pub fn create_character( waypoint, pets: _, active_abilities, + map_marker, } = persisted_components; // Fetch new entity IDs for character, inventory and loadout @@ -438,7 +443,7 @@ pub fn create_character( &character_id as &dyn ToSql, &uuid, &character_alias, - &convert_waypoint_to_database_json(waypoint), + &convert_waypoint_to_database_json(waypoint, map_marker), ])?; drop(stmt); @@ -935,6 +940,8 @@ fn delete_pets( Ok(()) } + +#[allow(clippy::too_many_arguments)] pub fn update( char_id: CharacterId, char_skill_set: comp::SkillSet, @@ -942,6 +949,7 @@ pub fn update( pets: Vec, char_waypoint: Option, active_abilities: comp::ability::ActiveAbilities, + map_marker: Option, transaction: &mut Transaction, ) -> Result<(), PersistenceError> { // Run pet persistence @@ -1066,7 +1074,7 @@ pub fn update( ])?; } - let db_waypoint = convert_waypoint_to_database_json(char_waypoint); + let db_waypoint = convert_waypoint_to_database_json(char_waypoint, map_marker); let mut stmt = transaction.prepare_cached( " @@ -1103,7 +1111,7 @@ pub fn update( if ability_sets_count != 1 { return Err(PersistenceError::OtherError(format!( "Error updating ability_set table for char_id {}", - char_id + char_id, ))); } diff --git a/server/src/persistence/character/conversions.rs b/server/src/persistence/character/conversions.rs index 0b73a2a046..b562e740a0 100644 --- a/server/src/persistence/character/conversions.rs +++ b/server/src/persistence/character/conversions.rs @@ -217,28 +217,30 @@ pub fn convert_body_to_database_json( }) } -pub fn convert_waypoint_to_database_json(waypoint: Option) -> Option { - match waypoint { - Some(w) => { - let charpos = CharacterPosition { - waypoint: w.get_pos(), - }; - Some( - serde_json::to_string(&charpos) - .map_err(|err| { - PersistenceError::ConversionError(format!( - "Error encoding waypoint: {:?}", - err - )) - }) - .ok()?, - ) - }, - None => None, +pub fn convert_waypoint_to_database_json( + waypoint: Option, + map_marker: Option, +) -> Option { + if waypoint.is_some() || map_marker.is_some() { + let charpos = CharacterPosition { + waypoint: waypoint.map(|w| w.get_pos()), + map_marker: map_marker.map(|m| m.0), + }; + Some( + serde_json::to_string(&charpos) + .map_err(|err| { + PersistenceError::ConversionError(format!("Error encoding waypoint: {:?}", err)) + }) + .ok()?, + ) + } else { + None } } -pub fn convert_waypoint_from_database_json(position: &str) -> Result { +pub fn convert_waypoint_from_database_json( + position: &str, +) -> Result<(Option, Option), PersistenceError> { let character_position = serde_json::de::from_str::(position).map_err(|err| { PersistenceError::ConversionError(format!( @@ -246,7 +248,12 @@ pub fn convert_waypoint_from_database_json(position: &str) -> Result, Option, comp::ability::ActiveAbilities, + Option, ); pub type PetPersistenceData = (comp::Pet, comp::Body, comp::Stats); @@ -332,12 +333,21 @@ impl CharacterUpdater { Vec, Option<&'a comp::Waypoint>, &'a comp::ability::ActiveAbilities, + Option<&'a comp::MapMarker>, ), >, ) { let updates = updates .map( - |(character_id, skill_set, inventory, pets, waypoint, active_abilities)| { + |( + character_id, + skill_set, + inventory, + pets, + waypoint, + active_abilities, + map_marker, + )| { ( character_id, ( @@ -346,6 +356,7 @@ impl CharacterUpdater { pets, waypoint.cloned(), active_abilities.clone(), + map_marker.cloned(), ), ) }, @@ -388,7 +399,7 @@ fn execute_batch_update( transaction.set_drop_behavior(DropBehavior::Rollback); trace!("Transaction started for character batch update"); updates.into_iter().try_for_each( - |(character_id, (stats, inventory, pets, waypoint, active_abilities))| { + |(character_id, (stats, inventory, pets, waypoint, active_abilities, map_marker))| { super::character::update( character_id, stats, @@ -396,6 +407,7 @@ fn execute_batch_update( pets, waypoint, active_abilities, + map_marker, &mut transaction, ) }, diff --git a/server/src/persistence/json_models.rs b/server/src/persistence/json_models.rs index 9522fb2d9c..610ecccc7a 100644 --- a/server/src/persistence/json_models.rs +++ b/server/src/persistence/json_models.rs @@ -3,7 +3,7 @@ use common_base::dev_panic; use hashbrown::HashMap; use serde::{Deserialize, Serialize}; use std::string::ToString; -use vek::Vec3; +use vek::{Vec2, Vec3}; #[derive(Serialize, Deserialize)] pub struct HumanoidBody { @@ -62,7 +62,8 @@ generic_body_from_impl!(comp::quadruped_small::Body); #[derive(Serialize, Deserialize)] pub struct CharacterPosition { - pub waypoint: Vec3, + pub waypoint: Option>, + pub map_marker: Option>, } pub fn skill_group_to_db_string(skill_group: comp::skillset::SkillGroupKind) -> String { diff --git a/server/src/persistence/mod.rs b/server/src/persistence/mod.rs index 782dcbbca1..1c0c9e4773 100644 --- a/server/src/persistence/mod.rs +++ b/server/src/persistence/mod.rs @@ -31,6 +31,7 @@ pub struct PersistedComponents { pub waypoint: Option, pub pets: Vec, pub active_abilities: comp::ActiveAbilities, + pub map_marker: Option, } pub type EditableComponents = (comp::Body,); diff --git a/server/src/pet.rs b/server/src/pet.rs index cead1eb894..73b612386b 100644 --- a/server/src/pet.rs +++ b/server/src/pet.rs @@ -1,6 +1,6 @@ -use crate::client::Client; +use crate::{client::Client, events::update_map_markers}; use common::{ - comp::{anchor::Anchor, group::GroupManager, Agent, Alignment, Pet}, + comp::{self, anchor::Anchor, group::GroupManager, Agent, Alignment, Pet}, uid::Uid, }; use common_net::msg::ServerGeneral; @@ -59,6 +59,7 @@ fn tame_pet_internal(ecs: &specs::World, pet_entity: Entity, owner: Entity, pet: // Add to group system let clients = ecs.read_storage::(); let mut group_manager = ecs.write_resource::(); + let map_markers = ecs.read_storage::(); group_manager.new_pet( pet_entity, owner, @@ -71,10 +72,15 @@ fn tame_pet_internal(ecs: &specs::World, pet_entity: Entity, owner: Entity, pet: .get(entity) .and_then(|c| { group_change - .try_map(|e| uids.get(e).copied()) + .try_map_ref(|e| uids.get(*e).copied()) .map(|g| (g, c)) }) - .map(|(g, c)| c.send_fallible(ServerGeneral::GroupUpdate(g))); + .map(|(g, c)| { + // Might be unneccessary, but maybe pets can somehow have map + // markers in the future + update_map_markers(&map_markers, &uids, c, &group_change); + c.send_fallible(ServerGeneral::GroupUpdate(g)); + }); }, ); } diff --git a/server/src/state_ext.rs b/server/src/state_ext.rs index a9e47fe283..ff999e8a2a 100644 --- a/server/src/state_ext.rs +++ b/server/src/state_ext.rs @@ -1,5 +1,6 @@ use crate::{ client::Client, + events::update_map_markers, persistence::PersistedComponents, pet::restore_pet, presence::{Presence, RepositionOnChunkLoad}, @@ -526,6 +527,7 @@ impl StateExt for State { waypoint, pets, active_abilities, + map_marker, } = components; if let Some(player_uid) = self.read_component_copied::(entity) { @@ -572,6 +574,10 @@ impl StateExt for State { self.write_component_ignore_entity_dead(entity, comp::ForceUpdate); } + if let Some(map_marker) = map_marker { + self.write_component_ignore_entity_dead(entity, map_marker); + } + let player_pos = self.ecs().read_storage::().get(entity).copied(); if let Some(player_pos) = player_pos { trace!( @@ -885,6 +891,7 @@ impl StateExt for State { let clients = self.ecs().read_storage::(); let uids = self.ecs().read_storage::(); let mut group_manager = self.ecs().write_resource::(); + let map_markers = self.ecs().read_storage::(); group_manager.entity_deleted( entity, &mut self.ecs().write_storage(), @@ -896,10 +903,13 @@ impl StateExt for State { .get(entity) .and_then(|c| { group_change - .try_map(|e| uids.get(e).copied()) + .try_map_ref(|e| uids.get(*e).copied()) .map(|g| (g, c)) }) - .map(|(g, c)| c.send(ServerGeneral::GroupUpdate(g))); + .map(|(g, c)| { + update_map_markers(&map_markers, &uids, c, &group_change); + c.send_fallible(ServerGeneral::GroupUpdate(g)); + }); }, ); } diff --git a/server/src/sys/msg/in_game.rs b/server/src/sys/msg/in_game.rs index 1cfde3744c..bef7448b4d 100644 --- a/server/src/sys/msg/in_game.rs +++ b/server/src/sys/msg/in_game.rs @@ -285,6 +285,9 @@ impl Sys { .get_mut(entity) .map(|mut skill_set| skill_set.persistence_load_error = None); }, + ClientGeneral::UpdateMapMarker(update) => { + server_emitter.emit(ServerEvent::UpdateMapMarker { entity, update }); + }, ClientGeneral::RequestCharacterList | ClientGeneral::CreateCharacter { .. } | ClientGeneral::EditCharacter { .. } diff --git a/server/src/sys/persistence.rs b/server/src/sys/persistence.rs index ed3ecc9495..270520147f 100644 --- a/server/src/sys/persistence.rs +++ b/server/src/sys/persistence.rs @@ -2,7 +2,7 @@ use crate::{persistence::character_updater, presence::Presence, sys::SysSchedule use common::{ comp::{ pet::{is_tameable, Pet}, - ActiveAbilities, Alignment, Body, Inventory, SkillSet, Stats, Waypoint, + ActiveAbilities, Alignment, Body, Inventory, MapMarker, SkillSet, Stats, Waypoint, }, uid::Uid, }; @@ -22,6 +22,7 @@ impl<'a> System<'a> for Sys { ReadStorage<'a, Inventory>, ReadStorage<'a, Uid>, ReadStorage<'a, Waypoint>, + ReadStorage<'a, MapMarker>, ReadStorage<'a, Pet>, ReadStorage<'a, Stats>, ReadStorage<'a, ActiveAbilities>, @@ -43,6 +44,7 @@ impl<'a> System<'a> for Sys { player_inventories, uids, player_waypoints, + map_markers, pets, stats, active_abilities, @@ -59,6 +61,7 @@ impl<'a> System<'a> for Sys { &uids, player_waypoints.maybe(), &active_abilities, + map_markers.maybe(), ) .join() .filter_map( @@ -69,6 +72,7 @@ impl<'a> System<'a> for Sys { player_uid, waypoint, active_abilities, + map_marker, )| match presence.kind { PresenceKind::Character(id) => { let pets = (&alignments, &bodies, &stats, &pets) @@ -86,7 +90,15 @@ impl<'a> System<'a> for Sys { }) .collect(); - Some((id, skill_set, inventory, pets, waypoint, active_abilities)) + Some(( + id, + skill_set, + inventory, + pets, + waypoint, + active_abilities, + map_marker, + )) }, PresenceKind::Spectator => None, }, diff --git a/voxygen/src/hud/img_ids.rs b/voxygen/src/hud/img_ids.rs index 325e3262de..6193e28dc7 100644 --- a/voxygen/src/hud/img_ids.rs +++ b/voxygen/src/hud/img_ids.rs @@ -368,6 +368,7 @@ image_ids! { indicator_group_up: "voxygen.element.ui.map.buttons.group_indicator_arrow_up", indicator_group_down: "voxygen.element.ui.map.buttons.group_indicator_arrow_down", location_marker: "voxygen.element.ui.map.buttons.location_marker", + location_marker_group: "voxygen.element.ui.map.buttons.location_marker_group", map_mode_overlay: "voxygen.element.ui.map.buttons.map_modes", minimap_mode_overlay: "voxygen.element.ui.map.buttons.minimap_modes", diff --git a/voxygen/src/hud/map.rs b/voxygen/src/hud/map.rs index f4cfeb1c4f..27334f1874 100644 --- a/voxygen/src/hud/map.rs +++ b/voxygen/src/hud/map.rs @@ -1,6 +1,6 @@ use super::{ img_ids::{Imgs, ImgsRot}, - Show, QUALITY_COMMON, QUALITY_EPIC, QUALITY_HIGH, QUALITY_LOW, QUALITY_MODERATE, TEXT_BG, + MapMarkers, QUALITY_COMMON, QUALITY_EPIC, QUALITY_HIGH, QUALITY_LOW, QUALITY_MODERATE, TEXT_BG, TEXT_BLUE_COLOR, TEXT_COLOR, TEXT_GRAY_COLOR, TEXT_VELORITE, UI_HIGHLIGHT_0, UI_MAIN, }; use crate::{ @@ -51,6 +51,7 @@ widget_ids! { member_indicators[], member_height_indicators[], location_marker, + location_marker_group[], map_settings_align, show_towns_img, show_towns_box, @@ -98,7 +99,6 @@ const SHOW_ECONOMY: bool = false; // turn this display off (for 0.9) until we ha #[derive(WidgetCommon)] pub struct Map<'a> { - show: &'a Show, client: &'a Client, world_map: &'a (Vec, Vec2), imgs: &'a Imgs, @@ -110,12 +110,11 @@ pub struct Map<'a> { global_state: &'a GlobalState, rot_imgs: &'a ImgsRot, tooltip_manager: &'a mut TooltipManager, - location_marker: Option>, + location_markers: &'a MapMarkers, map_drag: Vec2, } impl<'a> Map<'a> { pub fn new( - show: &'a Show, client: &'a Client, imgs: &'a Imgs, rot_imgs: &'a ImgsRot, @@ -125,11 +124,10 @@ impl<'a> Map<'a> { localized_strings: &'a Localization, global_state: &'a GlobalState, tooltip_manager: &'a mut TooltipManager, - location_marker: Option>, + location_markers: &'a MapMarkers, map_drag: Vec2, ) -> Self { Self { - show, imgs, rot_imgs, world_map, @@ -140,7 +138,7 @@ impl<'a> Map<'a> { localized_strings, global_state, tooltip_manager, - location_marker, + location_markers, map_drag, } } @@ -154,9 +152,9 @@ pub enum Event { SettingsChange(InterfaceChange), Close, RequestSiteInfo(SiteId), - SetLocationMarker(Vec2), + SetLocationMarker(Vec2), MapDrag(Vec2), - ToggleMarker, + RemoveMarker, } fn get_site_economy(site_rich: &SiteInfoRich) -> String { @@ -370,16 +368,15 @@ impl<'a> Widget for Map<'a> { .next() { match wpos { - Some(ref wpos) => events.push(Event::SetLocationMarker(*wpos)), + Some(ref wpos) => events.push(Event::SetLocationMarker(wpos.as_())), None => { let tmp: Vec2 = Vec2::::from(click.xy) / zoom - drag; let wpos = tmp .map2(TerrainChunkSize::RECT_SIZE, |e, sz| e as f32 * sz as f32) + player_pos; - events.push(Event::SetLocationMarker(wpos)); + events.push(Event::SetLocationMarker(wpos.as_())); }, } - events.push(Event::ToggleMarker); } // Handle zooming with the mousewheel @@ -1220,18 +1217,36 @@ impl<'a> Widget for Map<'a> { } } - // Location marker - if self.show.map_marker { - let factor = 1.4; - let side_length = 20.0 * factor; - if let Some((lm, (rpos, fade))) = self.location_marker.and_then(|lm| { - Some(lm).zip(wpos_to_rpos_fade( - lm, - Vec2::from(side_length / 2.0), - side_length / 2.0, - )) - }) { - if Button::image(self.imgs.location_marker) + let factor = 1.4; + let side_length = 20.0 * factor; + // Groups location markers + if state.ids.location_marker_group.len() < self.location_markers.group.len() { + state.update(|s| { + s.ids.location_marker_group.resize( + self.location_markers.group.len(), + &mut ui.widget_id_generator(), + ) + }) + }; + for (i, (&uid, &rpos)) in self.location_markers.group.iter().enumerate() { + let lm = rpos.as_(); + if let Some((rpos, fade)) = + wpos_to_rpos_fade(lm, Vec2::from(side_length / 2.0), side_length / 2.0) + { + let name = self + .client + .player_list() + .get(&uid) + .map(|info| info.player_alias.as_str()) + .or_else(|| { + uid_allocator + .retrieve_entity_internal(uid.into()) + .and_then(|entity| stats.get(entity)) + .map(|stats| stats.name.as_str()) + }) + .unwrap_or(""); + + Button::image(self.imgs.location_marker_group) .x_y_position_relative_to( state.ids.map_layers[0], position::Relative::Scalar(rpos.x as f64), @@ -1247,26 +1262,58 @@ impl<'a> Widget for Map<'a> { "X: {}, Y: {}\n\n{}", lm.x as i32, lm.y as i32, - i18n.get("hud.map.marked_location_remove") + i18n.get("hud.map.placed_by").replace("{name}", name), ), &site_tooltip, TEXT_VELORITE, ) - .set(state.ids.location_marker, ui) - .was_clicked() - { - events.push(Event::ToggleMarker); - } - - handle_widget_mouse_events( - state.ids.location_marker, - Some(Vec2::new(0.0, 0.0)), - ui, - &mut events, - state.ids.map_layers[0], - ); + .set(state.ids.location_marker_group[i], ui); } } + // Location marker + if let Some((lm, (rpos, fade))) = self.location_markers.owned.and_then(|lm| { + let lm = lm.as_(); + Some(lm).zip(wpos_to_rpos_fade( + lm, + Vec2::from(side_length / 2.0), + side_length / 2.0, + )) + }) { + if Button::image(self.imgs.location_marker) + .x_y_position_relative_to( + state.ids.map_layers[0], + position::Relative::Scalar(rpos.x as f64), + position::Relative::Scalar(rpos.y as f64 + 10.0 * factor as f64), + ) + .w_h(side_length as f64, side_length as f64) + .image_color(Color::Rgba(1.0, 1.0, 1.0, fade)) + .floating(true) + .with_tooltip( + self.tooltip_manager, + i18n.get("hud.map.marked_location"), + &format!( + "X: {}, Y: {}\n\n{}", + lm.x as i32, + lm.y as i32, + i18n.get("hud.map.marked_location_remove") + ), + &site_tooltip, + TEXT_VELORITE, + ) + .set(state.ids.location_marker, ui) + .was_clicked() + { + events.push(Event::RemoveMarker); + } + + handle_widget_mouse_events( + state.ids.location_marker, + Some(Vec2::new(0.0, 0.0)), + ui, + &mut events, + state.ids.map_layers[0], + ); + } // Cursor pos relative to playerpos and widget size // Cursor stops moving on an axis as soon as it's position exceeds the maximum diff --git a/voxygen/src/hud/minimap.rs b/voxygen/src/hud/minimap.rs index fb3dbf1ee7..e2d1c33ce0 100644 --- a/voxygen/src/hud/minimap.rs +++ b/voxygen/src/hud/minimap.rs @@ -1,7 +1,7 @@ use super::{ img_ids::{Imgs, ImgsRot}, - Show, QUALITY_COMMON, QUALITY_DEBUG, QUALITY_EPIC, QUALITY_HIGH, QUALITY_LOW, QUALITY_MODERATE, - TEXT_COLOR, UI_HIGHLIGHT_0, UI_MAIN, + MapMarkers, QUALITY_COMMON, QUALITY_DEBUG, QUALITY_EPIC, QUALITY_HIGH, QUALITY_LOW, + QUALITY_MODERATE, TEXT_COLOR, UI_HIGHLIGHT_0, UI_MAIN, }; use crate::{ hud::{Graphic, Ui}, @@ -375,7 +375,6 @@ widget_ids! { #[derive(WidgetCommon)] pub struct MiniMap<'a> { - show: &'a Show, client: &'a Client, imgs: &'a Imgs, rot_imgs: &'a ImgsRot, @@ -385,13 +384,12 @@ pub struct MiniMap<'a> { common: widget::CommonBuilder, ori: Vec3, global_state: &'a GlobalState, - location_marker: Option>, + location_markers: &'a MapMarkers, voxel_minimap: &'a VoxelMinimap, } impl<'a> MiniMap<'a> { pub fn new( - show: &'a Show, client: &'a Client, imgs: &'a Imgs, rot_imgs: &'a ImgsRot, @@ -399,11 +397,10 @@ impl<'a> MiniMap<'a> { fonts: &'a Fonts, ori: Vec3, global_state: &'a GlobalState, - location_marker: Option>, + location_markers: &'a MapMarkers, voxel_minimap: &'a VoxelMinimap, ) -> Self { Self { - show, client, imgs, rot_imgs, @@ -412,7 +409,7 @@ impl<'a> MiniMap<'a> { common: widget::CommonBuilder::default(), ori, global_state, - location_marker, + location_markers, voxel_minimap, } } @@ -792,11 +789,14 @@ impl<'a> Widget for MiniMap<'a> { } // Location marker - if self.show.map_marker { - if let Some(rpos) = self.location_marker.and_then(|lm| wpos_to_rpos(lm, true)) { - let factor = 1.2; + if let Some(rpos) = self + .location_markers + .owned + .and_then(|lm| wpos_to_rpos(lm.as_(), true)) + { + let factor = 1.2; - Button::image(self.imgs.location_marker) + Button::image(self.imgs.location_marker) .x_y_position_relative_to( state.ids.map_layers[0], position::Relative::Scalar(rpos.x as f64), @@ -806,7 +806,6 @@ impl<'a> Widget for MiniMap<'a> { //.image_color(Color::Rgba(1.0, 1.0, 1.0, 1.0)) .floating(true) .set(state.ids.location_marker, ui); - } } // Indicator let ind_scale = 0.4; diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index 94cefaa6b4..7a92a44a74 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -83,7 +83,7 @@ use common::{ inventory::{slot::InvSlotId, trade_pricing::TradePricing}, item::{tool::ToolKind, ItemDesc, MaterialStatManifest, Quality}, skillset::{skills::Skill, SkillGroupKind}, - BuffData, BuffKind, Item, + BuffData, BuffKind, Item, MapMarkerChange, }, consts::MAX_PICKUP_RANGE, link::Is, @@ -546,6 +546,7 @@ pub enum Event { SettingsChange(SettingsChange), AcknowledgePersistenceLoadError, + MapMarkerEvent(MapMarkerChange), } // TODO: Are these the possible layouts we want? @@ -638,6 +639,12 @@ impl PressBehavior { } } +#[derive(Default, Clone)] +pub struct MapMarkers { + owned: Option>, + group: HashMap>, +} + pub struct Show { ui: bool, intro: bool, @@ -667,8 +674,7 @@ pub struct Show { auto_walk: bool, camera_clamp: bool, prompt_dialog: Option, - location_marker: Option>, - map_marker: bool, + location_markers: MapMarkers, salvage: bool, } impl Show { @@ -889,6 +895,26 @@ impl Show { global_state.window.center_cursor(); } } + + pub fn update_map_markers(&mut self, event: comp::MapMarkerUpdate) { + match event { + comp::MapMarkerUpdate::Owned(event) => match event { + MapMarkerChange::Update(waypoint) => self.location_markers.owned = Some(waypoint), + MapMarkerChange::Remove => self.location_markers.owned = None, + }, + comp::MapMarkerUpdate::GroupMember(user, event) => match event { + MapMarkerChange::Update(waypoint) => { + self.location_markers.group.insert(user, waypoint); + }, + MapMarkerChange::Remove => { + self.location_markers.group.remove(&user); + }, + }, + comp::MapMarkerUpdate::ClearGroup => { + self.location_markers.group.clear(); + }, + } + } } pub struct PromptDialogSettings { @@ -1060,8 +1086,7 @@ impl Hud { auto_walk: false, camera_clamp: false, prompt_dialog: None, - location_marker: None, - map_marker: false, + location_markers: MapMarkers::default(), salvage: false, }, to_focus: None, @@ -2756,7 +2781,6 @@ impl Hud { // MiniMap for event in MiniMap::new( - &self.show, client, &self.imgs, &self.rot_imgs, @@ -2764,7 +2788,7 @@ impl Hud { &self.fonts, camera.get_orientation(), global_state, - self.show.location_marker, + &self.show.location_markers, &self.voxel_minimap, ) .set(self.ids.minimap, ui_widgets) @@ -3272,7 +3296,6 @@ impl Hud { // Map if self.show.map { for event in Map::new( - &self.show, client, &self.imgs, &self.rot_imgs, @@ -3282,7 +3305,7 @@ impl Hud { i18n, global_state, tooltip_manager, - self.show.location_marker, + &self.show.location_markers, self.map_drag, ) .set(self.ids.map, ui_widgets) @@ -3300,13 +3323,15 @@ impl Hud { events.push(Event::RequestSiteInfo(id)); }, map::Event::SetLocationMarker(pos) => { - self.show.location_marker = Some(pos); + events.push(Event::MapMarkerEvent(MapMarkerChange::Update(pos))); + self.show.location_markers.owned = Some(pos); }, map::Event::MapDrag(new_drag) => { self.map_drag = new_drag; }, - map::Event::ToggleMarker => { - self.show.map_marker = !self.show.map_marker; + map::Event::RemoveMarker => { + self.show.location_markers.owned = None; + events.push(Event::MapMarkerEvent(MapMarkerChange::Remove)); }, } } diff --git a/voxygen/src/session/mod.rs b/voxygen/src/session/mod.rs index ebe17239b5..ccbd8f93e4 100644 --- a/voxygen/src/session/mod.rs +++ b/voxygen/src/session/mod.rs @@ -308,6 +308,9 @@ impl SessionState { client::Event::CharacterError(error) => { global_state.client_error = Some(error); }, + client::Event::MapMarker(event) => { + self.hud.show.update_map_markers(event); + }, } } @@ -1446,6 +1449,9 @@ impl PlayState for SessionState { .borrow_mut() .acknolwedge_persistence_load_error(); }, + HudEvent::MapMarkerEvent(event) => { + self.client.borrow_mut().map_marker_event(event); + }, } }