Merge branch 'isse/shared-waypoints' into 'master'

Shared and persistent waypoints

See merge request veloren/veloren!3162
This commit is contained in:
Marcel 2022-02-20 10:10:18 +00:00
commit df7cd2da1a
34 changed files with 448 additions and 135 deletions

View File

@ -58,6 +58,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Convert giant trees to site2 - Convert giant trees to site2
- Add new upgraded travelers - Add new upgraded travelers
- Wallrunning - Wallrunning
- Waypoints saved between sessions and shared with group members.
### Changed ### Changed

Binary file not shown.

View File

@ -32,6 +32,7 @@
"hud.map.toggle_minimap_voxel": "Toggle Minimap Voxel View", "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.zoom_minimap_explanation": "Zoom in the Minimap to see\nthe area around you in higher detail",
"hud.map.gnarling": "Gnarling Fortification", "hud.map.gnarling": "Gnarling Fortification",
"hud.map.placed_by": "Placed by {name}",
}, },

View File

@ -30,7 +30,7 @@ use common::{
slot::{EquipSlot, InvSlotId, Slot}, slot::{EquipSlot, InvSlotId, Slot},
CharacterState, ChatMode, ControlAction, ControlEvent, Controller, ControllerInputs, CharacterState, ChatMode, ControlAction, ControlEvent, Controller, ControllerInputs,
GroupManip, InputKind, InventoryAction, InventoryEvent, InventoryUpdateEvent, GroupManip, InputKind, InventoryAction, InventoryEvent, InventoryUpdateEvent,
UtteranceKind, MapMarkerChange, UtteranceKind,
}, },
event::{EventBus, LocalEvent}, event::{EventBus, LocalEvent},
grid::Grid, grid::Grid,
@ -112,6 +112,7 @@ pub enum Event {
CharacterCreated(CharacterId), CharacterCreated(CharacterId),
CharacterEdited(CharacterId), CharacterEdited(CharacterId),
CharacterError(String), CharacterError(String),
MapMarker(comp::MapMarkerUpdate),
} }
pub struct WorldData { pub struct WorldData {
@ -759,7 +760,8 @@ impl Client {
| ClientGeneral::UnlockSkillGroup(_) | ClientGeneral::UnlockSkillGroup(_)
| ClientGeneral::RequestPlayerPhysics { .. } | ClientGeneral::RequestPlayerPhysics { .. }
| ClientGeneral::RequestLossyTerrainCompression { .. } | ClientGeneral::RequestLossyTerrainCompression { .. }
| ClientGeneral::AcknowledgePersistenceLoadError => { | ClientGeneral::AcknowledgePersistenceLoadError
| ClientGeneral::UpdateMapMarker(_) => {
#[cfg(feature = "tracy")] #[cfg(feature = "tracy")]
{ {
ingame = 1.0; 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 /// Checks whether a player can swap their weapon+ability `Loadout` settings
/// and sends the `ControlAction` event that signals to do the swap. /// and sends the `ControlAction` event that signals to do the swap.
pub fn swap_loadout(&mut self) { self.control_action(ControlAction::SwapEquippedWeapons) } 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()) 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() { if self.group_members.remove(&uid).is_none() {
warn!( warn!(
@ -1931,10 +1940,12 @@ impl Client {
if let Some(uid) = self.uid() { if let Some(uid) = self.uid() {
self.group_members.remove(&uid); self.group_members.remove(&uid);
} }
frontend_events.push(Event::MapMarker(comp::MapMarkerUpdate::ClearGroup));
}, },
NoGroup => { NoGroup => {
self.group_leader = None; self.group_leader = None;
self.group_members = HashMap::new(); self.group_members = HashMap::new();
frontend_events.push(Event::MapMarker(comp::MapMarkerUpdate::ClearGroup));
}, },
} }
}, },
@ -2032,6 +2043,9 @@ impl Client {
rich.economy = Some(economy); rich.economy = Some(economy);
} }
}, },
ServerGeneral::MapMarker(event) => {
frontend_events.push(Event::MapMarker(event));
},
_ => unreachable!("Not a in_game message"), _ => unreachable!("Not a in_game message"),
} }
Ok(()) Ok(())

View File

@ -78,6 +78,7 @@ pub enum ClientGeneral {
UnlockSkill(Skill), UnlockSkill(Skill),
UnlockSkillGroup(SkillGroupKind), UnlockSkillGroup(SkillGroupKind),
RequestSiteInfo(SiteId), RequestSiteInfo(SiteId),
UpdateMapMarker(comp::MapMarkerChange),
//Only in Game, via terrain stream //Only in Game, via terrain stream
TerrainChunkRequest { TerrainChunkRequest {
key: Vec2<i32>, key: Vec2<i32>,
@ -132,7 +133,8 @@ impl ClientMsg {
| ClientGeneral::UnlockSkillGroup(_) | ClientGeneral::UnlockSkillGroup(_)
| ClientGeneral::RequestPlayerPhysics { .. } | ClientGeneral::RequestPlayerPhysics { .. }
| ClientGeneral::RequestLossyTerrainCompression { .. } | ClientGeneral::RequestLossyTerrainCompression { .. }
| ClientGeneral::AcknowledgePersistenceLoadError => { | ClientGeneral::AcknowledgePersistenceLoadError
| ClientGeneral::UpdateMapMarker(_) => {
c_type == ClientType::Game && presence.is_some() c_type == ClientType::Game && presence.is_some()
}, },
//Always possible //Always possible

View File

@ -189,6 +189,7 @@ pub enum ServerGeneral {
FinishedTrade(TradeResult), FinishedTrade(TradeResult),
/// Economic information about sites /// Economic information about sites
SiteEconomy(EconomyInfo), SiteEconomy(EconomyInfo),
MapMarker(comp::MapMarkerUpdate),
} }
impl ServerGeneral { impl ServerGeneral {
@ -298,7 +299,8 @@ impl ServerMsg {
| ServerGeneral::Knockback(_) | ServerGeneral::Knockback(_)
| ServerGeneral::UpdatePendingTrade(_, _, _) | ServerGeneral::UpdatePendingTrade(_, _, _)
| ServerGeneral::FinishedTrade(_) | ServerGeneral::FinishedTrade(_)
| ServerGeneral::SiteEconomy(_) => { | ServerGeneral::SiteEconomy(_)
| ServerGeneral::MapMarker(_) => {
c_type == ClientType::Game && presence.is_some() c_type == ClientType::Game && presence.is_some()
}, },
// Always possible // Always possible

View File

@ -62,9 +62,9 @@ pub enum ChangeNotification<E> {
// Also note when the same notification is sent to multiple destinations the // Also note when the same notification is sent to multiple destinations the
// mapping might be duplicated effort // mapping might be duplicated effort
impl<E> ChangeNotification<E> { impl<E> ChangeNotification<E> {
pub fn try_map<T>(self, f: impl Fn(E) -> Option<T>) -> Option<ChangeNotification<T>> { pub fn try_map_ref<T>(&self, f: impl Fn(&E) -> Option<T>) -> Option<ChangeNotification<T>> {
match self { 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::Removed(e) => f(e).map(ChangeNotification::Removed),
Self::NewLeader(e) => f(e).map(ChangeNotification::NewLeader), Self::NewLeader(e) => f(e).map(ChangeNotification::NewLeader),
// Note just discards members that fail map // Note just discards members that fail map
@ -72,8 +72,8 @@ impl<E> ChangeNotification<E> {
f(leader).map(|leader| ChangeNotification::NewGroup { f(leader).map(|leader| ChangeNotification::NewGroup {
leader, leader,
members: members members: members
.into_iter() .iter()
.filter_map(|(e, r)| f(e).map(|t| (t, r))) .filter_map(|(e, r)| f(e).map(|t| (t, *r)))
.collect(), .collect(),
}) })
}, },

View File

@ -1,4 +1,4 @@
use crate::resources::Time; use crate::{resources::Time, uid::Uid};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use specs::Component; use specs::Component;
use specs_idvs::IdvStorage; use specs_idvs::IdvStorage;
@ -45,3 +45,25 @@ impl Component for WaypointArea {
impl Default for WaypointArea { impl Default for WaypointArea {
fn default() -> Self { Self(5.0) } 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<i32>);
impl Component for MapMarker {
type Storage = IdvStorage<Self>;
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
pub enum MapMarkerChange {
Update(Vec2<i32>),
Remove,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum MapMarkerUpdate {
Owned(MapMarkerChange),
GroupMember(Uid, MapMarkerChange),
ClearGroup,
}

View File

@ -90,7 +90,7 @@ pub use self::{
slot, Inventory, InventoryUpdate, InventoryUpdateEvent, slot, Inventory, InventoryUpdate, InventoryUpdateEvent,
}, },
last::Last, last::Last,
location::{Waypoint, WaypointArea}, location::{MapMarker, MapMarkerChange, MapMarkerUpdate, Waypoint, WaypointArea},
melee::{Melee, MeleeConstructor}, melee::{Melee, MeleeConstructor},
misc::Object, misc::Object,
ori::Ori, ori::Ori,

View File

@ -115,6 +115,7 @@ pub enum ServerEvent {
Option<comp::Waypoint>, Option<comp::Waypoint>,
Vec<(comp::Pet, comp::Body, comp::Stats)>, Vec<(comp::Pet, comp::Body, comp::Stats)>,
comp::ActiveAbilities, comp::ActiveAbilities,
Option<comp::MapMarker>,
), ),
}, },
ExitIngame { ExitIngame {
@ -209,6 +210,10 @@ pub enum ServerEvent {
auxiliary_key: comp::ability::AuxiliaryKey, auxiliary_key: comp::ability::AuxiliaryKey,
new_ability: comp::ability::AuxiliaryAbility, new_ability: comp::ability::AuxiliaryAbility,
}, },
UpdateMapMarker {
entity: EcsEntity,
update: comp::MapMarkerChange,
},
} }
pub struct EventBus<E> { pub struct EventBus<E> {

View File

@ -191,6 +191,7 @@ impl State {
ecs.register::<comp::InventoryUpdate>(); ecs.register::<comp::InventoryUpdate>();
ecs.register::<comp::Admin>(); ecs.register::<comp::Admin>();
ecs.register::<comp::Waypoint>(); ecs.register::<comp::Waypoint>();
ecs.register::<comp::MapMarker>();
ecs.register::<comp::Projectile>(); ecs.register::<comp::Projectile>();
ecs.register::<comp::Melee>(); ecs.register::<comp::Melee>();
ecs.register::<comp::ItemDrop>(); ecs.register::<comp::ItemDrop>();

View File

@ -64,6 +64,7 @@ pub fn create_character(
.expect("Inventory has at least 1 slot left!"); .expect("Inventory has at least 1 slot left!");
let waypoint = None; let waypoint = None;
let map_marker = None;
character_updater.create_character(entity, player_uuid, character_alias, PersistedComponents { character_updater.create_character(entity, player_uuid, character_alias, PersistedComponents {
body, body,
@ -73,6 +74,7 @@ pub fn create_character(
waypoint, waypoint,
pets: Vec::new(), pets: Vec::new(),
active_abilities: Default::default(), active_abilities: Default::default(),
map_marker,
}); });
Ok(()) Ok(())
} }

View File

@ -185,7 +185,8 @@ impl Client {
| ServerGeneral::Knockback(_) | ServerGeneral::Knockback(_)
| ServerGeneral::SiteEconomy(_) | ServerGeneral::SiteEconomy(_)
| ServerGeneral::UpdatePendingTrade(_, _, _) | ServerGeneral::UpdatePendingTrade(_, _, _)
| ServerGeneral::FinishedTrade(_) => { | ServerGeneral::FinishedTrade(_)
| ServerGeneral::MapMarker(_) => {
PreparedMsg::new(2, &g, &self.in_game_stream_params) PreparedMsg::new(2, &g, &self.in_game_stream_params)
}, },
//Ingame related, terrain //Ingame related, terrain

View File

@ -22,6 +22,8 @@ use specs::{Builder, Entity as EcsEntity, WorldExt};
use std::time::Duration; use std::time::Duration;
use vek::{Rgb, Vec3}; use vek::{Rgb, Vec3};
use super::group_manip::update_map_markers;
pub fn handle_initialize_character( pub fn handle_initialize_character(
server: &mut Server, server: &mut Server,
entity: EcsEntity, entity: EcsEntity,
@ -35,6 +37,14 @@ pub fn handle_loaded_character_data(
entity: EcsEntity, entity: EcsEntity,
loaded_components: PersistedComponents, 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 server
.state .state
.update_character_data(entity, loaded_components); .update_character_data(entity, loaded_components);
@ -103,6 +113,7 @@ pub fn handle_create_npc(
let uids = state.ecs().read_storage::<Uid>(); let uids = state.ecs().read_storage::<Uid>();
let mut group_manager = state.ecs().write_resource::<comp::group::GroupManager>(); let mut group_manager = state.ecs().write_resource::<comp::group::GroupManager>();
if let Some(owner) = state.ecs().entity_from_uid(owner_uid.into()) { if let Some(owner) = state.ecs().entity_from_uid(owner_uid.into()) {
let map_markers = state.ecs().read_storage::<comp::MapMarker>();
group_manager.new_pet( group_manager.new_pet(
new_entity, new_entity,
owner, owner,
@ -115,10 +126,13 @@ pub fn handle_create_npc(
.get(entity) .get(entity)
.and_then(|c| { .and_then(|c| {
group_change group_change
.try_map(|e| uids.get(e).copied()) .try_map_ref(|e| uids.get(*e).copied())
.map(|g| (g, c)) .map(|g| (g, c))
}) })
.map(|(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)); c.send_fallible(ServerGeneral::GroupUpdate(g));
}); });
}, },

View File

@ -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::<MapMarker>(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::<Client>();
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),
));
}
}
}

View File

@ -2,7 +2,7 @@ use crate::{client::Client, Server};
use common::{ use common::{
comp::{ comp::{
self, self,
group::{self, Group, GroupManager}, group::{self, ChangeNotification, Group, GroupManager},
invite::{InviteKind, PendingInvites}, invite::{InviteKind, PendingInvites},
ChatType, GroupManip, ChatType, GroupManip,
}, },
@ -81,6 +81,37 @@ pub fn can_invite(
true true
} }
pub fn update_map_markers<'a>(
map_markers: &ReadStorage<'a, comp::MapMarker>,
uids: &ReadStorage<'a, Uid>,
client: &Client,
change: &ChangeNotification<Entity>,
) {
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 // TODO: turn chat messages into enums
pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupManip) { pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupManip) {
match manip { match manip {
@ -89,6 +120,7 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani
let clients = state.ecs().read_storage::<Client>(); let clients = state.ecs().read_storage::<Client>();
let uids = state.ecs().read_storage::<Uid>(); let uids = state.ecs().read_storage::<Uid>();
let mut group_manager = state.ecs().write_resource::<GroupManager>(); let mut group_manager = state.ecs().write_resource::<GroupManager>();
let map_markers = state.ecs().read_storage::<comp::MapMarker>();
group_manager.leave_group( group_manager.leave_group(
entity, entity,
&mut state.ecs().write_storage(), &mut state.ecs().write_storage(),
@ -100,10 +132,13 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani
.get(entity) .get(entity)
.and_then(|c| { .and_then(|c| {
group_change group_change
.try_map(|e| uids.get(e).copied()) .try_map_ref(|e| uids.get(*e).copied())
.map(|g| (g, c)) .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::<group::Group>(); let mut groups = state.ecs().write_storage::<group::Group>();
let mut group_manager = state.ecs().write_resource::<GroupManager>(); let mut group_manager = state.ecs().write_resource::<GroupManager>();
let map_markers = state.ecs().read_storage::<comp::MapMarker>();
// Make sure kicker is the group leader // Make sure kicker is the group leader
match groups match groups
.get(target) .get(target)
@ -169,10 +205,13 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani
.get(entity) .get(entity)
.and_then(|c| { .and_then(|c| {
group_change group_change
.try_map(|e| uids.get(e).copied()) .try_map_ref(|e| uids.get(*e).copied())
.map(|g| (g, c)) .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::<group::Group>(); let groups = state.ecs().read_storage::<group::Group>();
let mut group_manager = state.ecs().write_resource::<GroupManager>(); let mut group_manager = state.ecs().write_resource::<GroupManager>();
let map_markers = state.ecs().read_storage::<comp::MapMarker>();
// Make sure assigner is the group leader // Make sure assigner is the group leader
match groups match groups
.get(target) .get(target)
@ -248,10 +288,13 @@ pub fn handle_group(server: &mut Server, entity: specs::Entity, manip: GroupMani
.get(entity) .get(entity)
.and_then(|c| { .and_then(|c| {
group_change group_change
.try_map(|e| uids.get(e).copied()) .try_map_ref(|e| uids.get(*e).copied())
.map(|g| (g, c)) .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 // Tell them they are the leader

View File

@ -1,4 +1,4 @@
use super::group_manip; use super::group_manip::{self, update_map_markers};
use crate::{client::Client, Server}; use crate::{client::Client, Server};
use common::{ use common::{
comp::{ comp::{
@ -202,6 +202,7 @@ pub fn handle_invite_accept(server: &mut Server, entity: specs::Entity) {
match kind { match kind {
InviteKind::Group => { InviteKind::Group => {
let map_markers = state.ecs().read_storage::<comp::MapMarker>();
let mut group_manager = state.ecs().write_resource::<GroupManager>(); let mut group_manager = state.ecs().write_resource::<GroupManager>();
group_manager.add_group_member( group_manager.add_group_member(
inviter, inviter,
@ -215,10 +216,13 @@ pub fn handle_invite_accept(server: &mut Server, entity: specs::Entity) {
.get(entity) .get(entity)
.and_then(|c| { .and_then(|c| {
group_change group_change
.try_map(|e| uids.get(e).copied()) .try_map_ref(|e| uids.get(*e).copied())
.map(|g| (g, c)) .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));
});
}, },
); );
}, },

View File

@ -12,7 +12,7 @@ use entity_manipulation::{
handle_aura, handle_bonk, handle_buff, handle_change_ability, handle_combo_change, handle_aura, handle_bonk, handle_buff, handle_change_ability, handle_combo_change,
handle_delete, handle_destroy, handle_energy_change, handle_entity_attacked_hook, 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_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 group_manip::handle_group;
use information::handle_site_info; 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 specs::{Builder, Entity as EcsEntity, WorldExt};
use trade::{cancel_trade_for, handle_process_trade_action}; use trade::{cancel_trade_for, handle_process_trade_action};
pub use group_manip::update_map_markers;
mod entity_creation; mod entity_creation;
mod entity_manipulation; mod entity_manipulation;
mod group_manip; mod group_manip;
@ -132,8 +134,16 @@ impl Server {
character_id, character_id,
} => handle_initialize_character(self, entity, character_id), } => handle_initialize_character(self, entity, character_id),
ServerEvent::UpdateCharacterData { entity, components } => { ServerEvent::UpdateCharacterData { entity, components } => {
let (body, stats, skill_set, inventory, waypoint, pets, active_abilities) = let (
components; body,
stats,
skill_set,
inventory,
waypoint,
pets,
active_abilities,
map_marker,
) = components;
let components = PersistedComponents { let components = PersistedComponents {
body, body,
stats, stats,
@ -142,6 +152,7 @@ impl Server {
waypoint, waypoint,
pets, pets,
active_abilities, active_abilities,
map_marker,
}; };
handle_loaded_character_data(self, entity, components); handle_loaded_character_data(self, entity, components);
}, },
@ -253,6 +264,9 @@ impl Server {
auxiliary_key, auxiliary_key,
new_ability, new_ability,
} => handle_change_ability(self, entity, slot, 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)
},
} }
} }

View File

@ -226,6 +226,11 @@ fn persist_entity(state: &mut State, entity: EcsEntity) -> EcsEntity {
.read_storage::<common::comp::Waypoint>() .read_storage::<common::comp::Waypoint>()
.get(entity) .get(entity)
.cloned(); .cloned();
let map_marker = state
.ecs()
.read_storage::<common::comp::MapMarker>()
.get(entity)
.cloned();
// Store last battle mode change // Store last battle mode change
if let Some(change) = player_info.last_battlemode_change { if let Some(change) = player_info.last_battlemode_change {
let mode = player_info.battle_mode; let mode = player_info.battle_mode;
@ -261,6 +266,7 @@ fn persist_entity(state: &mut State, entity: EcsEntity) -> EcsEntity {
pets, pets,
waypoint, waypoint,
active_abilities.clone(), active_abilities.clone(),
map_marker,
), ),
); );
}, },

View File

@ -887,6 +887,7 @@ impl Server {
waypoint, waypoint,
pets, pets,
active_abilities, active_abilities,
map_marker,
} = character_data; } = character_data;
let character_data = ( let character_data = (
body, body,
@ -896,6 +897,7 @@ impl Server {
waypoint, waypoint,
pets, pets,
active_abilities, active_abilities,
map_marker,
); );
ServerEvent::UpdateCharacterData { ServerEvent::UpdateCharacterData {
entity: query_result.entity, entity: query_result.entity,

View File

@ -148,19 +148,22 @@ pub fn load_character_data(
}, },
)?; )?;
let char_waypoint = character_data.waypoint.as_ref().and_then(|x| { let (char_waypoint, char_map_marker) = match character_data
match convert_waypoint_from_database_json(x) { .waypoint
Ok(w) => Some(w), .as_ref()
Err(e) => { .map(|x| convert_waypoint_from_database_json(x))
warn!( {
"Error reading waypoint from database for character ID Some(Ok(w)) => w,
Some(Err(e)) => {
warn!(
"Error reading waypoint from database for character ID
{}, error: {}", {}, error: {}",
char_id, e char_id, e
); );
None (None, None)
}, },
} None => (None, None),
}); };
let mut stmt = connection.prepare_cached( let mut stmt = connection.prepare_cached(
" "
@ -261,6 +264,7 @@ pub fn load_character_data(
waypoint: char_waypoint, waypoint: char_waypoint,
pets, pets,
active_abilities: convert_active_abilities_from_database(&ability_set_data), active_abilities: convert_active_abilities_from_database(&ability_set_data),
map_marker: char_map_marker,
}) })
} }
@ -354,6 +358,7 @@ pub fn create_character(
waypoint, waypoint,
pets: _, pets: _,
active_abilities, active_abilities,
map_marker,
} = persisted_components; } = persisted_components;
// Fetch new entity IDs for character, inventory and loadout // Fetch new entity IDs for character, inventory and loadout
@ -438,7 +443,7 @@ pub fn create_character(
&character_id as &dyn ToSql, &character_id as &dyn ToSql,
&uuid, &uuid,
&character_alias, &character_alias,
&convert_waypoint_to_database_json(waypoint), &convert_waypoint_to_database_json(waypoint, map_marker),
])?; ])?;
drop(stmt); drop(stmt);
@ -935,6 +940,8 @@ fn delete_pets(
Ok(()) Ok(())
} }
#[allow(clippy::too_many_arguments)]
pub fn update( pub fn update(
char_id: CharacterId, char_id: CharacterId,
char_skill_set: comp::SkillSet, char_skill_set: comp::SkillSet,
@ -942,6 +949,7 @@ pub fn update(
pets: Vec<PetPersistenceData>, pets: Vec<PetPersistenceData>,
char_waypoint: Option<comp::Waypoint>, char_waypoint: Option<comp::Waypoint>,
active_abilities: comp::ability::ActiveAbilities, active_abilities: comp::ability::ActiveAbilities,
map_marker: Option<comp::MapMarker>,
transaction: &mut Transaction, transaction: &mut Transaction,
) -> Result<(), PersistenceError> { ) -> Result<(), PersistenceError> {
// Run pet persistence // 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( let mut stmt = transaction.prepare_cached(
" "
@ -1103,7 +1111,7 @@ pub fn update(
if ability_sets_count != 1 { if ability_sets_count != 1 {
return Err(PersistenceError::OtherError(format!( return Err(PersistenceError::OtherError(format!(
"Error updating ability_set table for char_id {}", "Error updating ability_set table for char_id {}",
char_id char_id,
))); )));
} }

View File

@ -217,28 +217,30 @@ pub fn convert_body_to_database_json(
}) })
} }
pub fn convert_waypoint_to_database_json(waypoint: Option<Waypoint>) -> Option<String> { pub fn convert_waypoint_to_database_json(
match waypoint { waypoint: Option<Waypoint>,
Some(w) => { map_marker: Option<MapMarker>,
let charpos = CharacterPosition { ) -> Option<String> {
waypoint: w.get_pos(), if waypoint.is_some() || map_marker.is_some() {
}; let charpos = CharacterPosition {
Some( waypoint: waypoint.map(|w| w.get_pos()),
serde_json::to_string(&charpos) map_marker: map_marker.map(|m| m.0),
.map_err(|err| { };
PersistenceError::ConversionError(format!( Some(
"Error encoding waypoint: {:?}", serde_json::to_string(&charpos)
err .map_err(|err| {
)) PersistenceError::ConversionError(format!("Error encoding waypoint: {:?}", err))
}) })
.ok()?, .ok()?,
) )
}, } else {
None => None, None
} }
} }
pub fn convert_waypoint_from_database_json(position: &str) -> Result<Waypoint, PersistenceError> { pub fn convert_waypoint_from_database_json(
position: &str,
) -> Result<(Option<Waypoint>, Option<MapMarker>), PersistenceError> {
let character_position = let character_position =
serde_json::de::from_str::<CharacterPosition>(position).map_err(|err| { serde_json::de::from_str::<CharacterPosition>(position).map_err(|err| {
PersistenceError::ConversionError(format!( PersistenceError::ConversionError(format!(
@ -246,7 +248,12 @@ pub fn convert_waypoint_from_database_json(position: &str) -> Result<Waypoint, P
position, err position, err
)) ))
})?; })?;
Ok(Waypoint::new(character_position.waypoint, Time(0.0))) Ok((
character_position
.waypoint
.map(|pos| Waypoint::new(pos, Time(0.0))),
character_position.map_marker.map(MapMarker),
))
} }
/// Properly-recursive items (currently modular weapons) occupy the same /// Properly-recursive items (currently modular weapons) occupy the same

View File

@ -25,6 +25,7 @@ pub type CharacterUpdateData = (
Vec<PetPersistenceData>, Vec<PetPersistenceData>,
Option<comp::Waypoint>, Option<comp::Waypoint>,
comp::ability::ActiveAbilities, comp::ability::ActiveAbilities,
Option<comp::MapMarker>,
); );
pub type PetPersistenceData = (comp::Pet, comp::Body, comp::Stats); pub type PetPersistenceData = (comp::Pet, comp::Body, comp::Stats);
@ -332,12 +333,21 @@ impl CharacterUpdater {
Vec<PetPersistenceData>, Vec<PetPersistenceData>,
Option<&'a comp::Waypoint>, Option<&'a comp::Waypoint>,
&'a comp::ability::ActiveAbilities, &'a comp::ability::ActiveAbilities,
Option<&'a comp::MapMarker>,
), ),
>, >,
) { ) {
let updates = updates let updates = updates
.map( .map(
|(character_id, skill_set, inventory, pets, waypoint, active_abilities)| { |(
character_id,
skill_set,
inventory,
pets,
waypoint,
active_abilities,
map_marker,
)| {
( (
character_id, character_id,
( (
@ -346,6 +356,7 @@ impl CharacterUpdater {
pets, pets,
waypoint.cloned(), waypoint.cloned(),
active_abilities.clone(), active_abilities.clone(),
map_marker.cloned(),
), ),
) )
}, },
@ -388,7 +399,7 @@ fn execute_batch_update(
transaction.set_drop_behavior(DropBehavior::Rollback); transaction.set_drop_behavior(DropBehavior::Rollback);
trace!("Transaction started for character batch update"); trace!("Transaction started for character batch update");
updates.into_iter().try_for_each( 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( super::character::update(
character_id, character_id,
stats, stats,
@ -396,6 +407,7 @@ fn execute_batch_update(
pets, pets,
waypoint, waypoint,
active_abilities, active_abilities,
map_marker,
&mut transaction, &mut transaction,
) )
}, },

View File

@ -3,7 +3,7 @@ use common_base::dev_panic;
use hashbrown::HashMap; use hashbrown::HashMap;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::string::ToString; use std::string::ToString;
use vek::Vec3; use vek::{Vec2, Vec3};
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct HumanoidBody { pub struct HumanoidBody {
@ -62,7 +62,8 @@ generic_body_from_impl!(comp::quadruped_small::Body);
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct CharacterPosition { pub struct CharacterPosition {
pub waypoint: Vec3<f32>, pub waypoint: Option<Vec3<f32>>,
pub map_marker: Option<Vec2<i32>>,
} }
pub fn skill_group_to_db_string(skill_group: comp::skillset::SkillGroupKind) -> String { pub fn skill_group_to_db_string(skill_group: comp::skillset::SkillGroupKind) -> String {

View File

@ -31,6 +31,7 @@ pub struct PersistedComponents {
pub waypoint: Option<comp::Waypoint>, pub waypoint: Option<comp::Waypoint>,
pub pets: Vec<PetPersistenceData>, pub pets: Vec<PetPersistenceData>,
pub active_abilities: comp::ActiveAbilities, pub active_abilities: comp::ActiveAbilities,
pub map_marker: Option<comp::MapMarker>,
} }
pub type EditableComponents = (comp::Body,); pub type EditableComponents = (comp::Body,);

View File

@ -1,6 +1,6 @@
use crate::client::Client; use crate::{client::Client, events::update_map_markers};
use common::{ use common::{
comp::{anchor::Anchor, group::GroupManager, Agent, Alignment, Pet}, comp::{self, anchor::Anchor, group::GroupManager, Agent, Alignment, Pet},
uid::Uid, uid::Uid,
}; };
use common_net::msg::ServerGeneral; 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 // Add to group system
let clients = ecs.read_storage::<Client>(); let clients = ecs.read_storage::<Client>();
let mut group_manager = ecs.write_resource::<GroupManager>(); let mut group_manager = ecs.write_resource::<GroupManager>();
let map_markers = ecs.read_storage::<comp::MapMarker>();
group_manager.new_pet( group_manager.new_pet(
pet_entity, pet_entity,
owner, owner,
@ -71,10 +72,15 @@ fn tame_pet_internal(ecs: &specs::World, pet_entity: Entity, owner: Entity, pet:
.get(entity) .get(entity)
.and_then(|c| { .and_then(|c| {
group_change group_change
.try_map(|e| uids.get(e).copied()) .try_map_ref(|e| uids.get(*e).copied())
.map(|g| (g, c)) .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));
});
}, },
); );
} }

View File

@ -1,5 +1,6 @@
use crate::{ use crate::{
client::Client, client::Client,
events::update_map_markers,
persistence::PersistedComponents, persistence::PersistedComponents,
pet::restore_pet, pet::restore_pet,
presence::{Presence, RepositionOnChunkLoad}, presence::{Presence, RepositionOnChunkLoad},
@ -526,6 +527,7 @@ impl StateExt for State {
waypoint, waypoint,
pets, pets,
active_abilities, active_abilities,
map_marker,
} = components; } = components;
if let Some(player_uid) = self.read_component_copied::<Uid>(entity) { if let Some(player_uid) = self.read_component_copied::<Uid>(entity) {
@ -572,6 +574,10 @@ impl StateExt for State {
self.write_component_ignore_entity_dead(entity, comp::ForceUpdate); 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::<comp::Pos>().get(entity).copied(); let player_pos = self.ecs().read_storage::<comp::Pos>().get(entity).copied();
if let Some(player_pos) = player_pos { if let Some(player_pos) = player_pos {
trace!( trace!(
@ -885,6 +891,7 @@ impl StateExt for State {
let clients = self.ecs().read_storage::<Client>(); let clients = self.ecs().read_storage::<Client>();
let uids = self.ecs().read_storage::<Uid>(); let uids = self.ecs().read_storage::<Uid>();
let mut group_manager = self.ecs().write_resource::<comp::group::GroupManager>(); let mut group_manager = self.ecs().write_resource::<comp::group::GroupManager>();
let map_markers = self.ecs().read_storage::<comp::MapMarker>();
group_manager.entity_deleted( group_manager.entity_deleted(
entity, entity,
&mut self.ecs().write_storage(), &mut self.ecs().write_storage(),
@ -896,10 +903,13 @@ impl StateExt for State {
.get(entity) .get(entity)
.and_then(|c| { .and_then(|c| {
group_change group_change
.try_map(|e| uids.get(e).copied()) .try_map_ref(|e| uids.get(*e).copied())
.map(|g| (g, c)) .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));
});
}, },
); );
} }

View File

@ -285,6 +285,9 @@ impl Sys {
.get_mut(entity) .get_mut(entity)
.map(|mut skill_set| skill_set.persistence_load_error = None); .map(|mut skill_set| skill_set.persistence_load_error = None);
}, },
ClientGeneral::UpdateMapMarker(update) => {
server_emitter.emit(ServerEvent::UpdateMapMarker { entity, update });
},
ClientGeneral::RequestCharacterList ClientGeneral::RequestCharacterList
| ClientGeneral::CreateCharacter { .. } | ClientGeneral::CreateCharacter { .. }
| ClientGeneral::EditCharacter { .. } | ClientGeneral::EditCharacter { .. }

View File

@ -2,7 +2,7 @@ use crate::{persistence::character_updater, presence::Presence, sys::SysSchedule
use common::{ use common::{
comp::{ comp::{
pet::{is_tameable, Pet}, pet::{is_tameable, Pet},
ActiveAbilities, Alignment, Body, Inventory, SkillSet, Stats, Waypoint, ActiveAbilities, Alignment, Body, Inventory, MapMarker, SkillSet, Stats, Waypoint,
}, },
uid::Uid, uid::Uid,
}; };
@ -22,6 +22,7 @@ impl<'a> System<'a> for Sys {
ReadStorage<'a, Inventory>, ReadStorage<'a, Inventory>,
ReadStorage<'a, Uid>, ReadStorage<'a, Uid>,
ReadStorage<'a, Waypoint>, ReadStorage<'a, Waypoint>,
ReadStorage<'a, MapMarker>,
ReadStorage<'a, Pet>, ReadStorage<'a, Pet>,
ReadStorage<'a, Stats>, ReadStorage<'a, Stats>,
ReadStorage<'a, ActiveAbilities>, ReadStorage<'a, ActiveAbilities>,
@ -43,6 +44,7 @@ impl<'a> System<'a> for Sys {
player_inventories, player_inventories,
uids, uids,
player_waypoints, player_waypoints,
map_markers,
pets, pets,
stats, stats,
active_abilities, active_abilities,
@ -59,6 +61,7 @@ impl<'a> System<'a> for Sys {
&uids, &uids,
player_waypoints.maybe(), player_waypoints.maybe(),
&active_abilities, &active_abilities,
map_markers.maybe(),
) )
.join() .join()
.filter_map( .filter_map(
@ -69,6 +72,7 @@ impl<'a> System<'a> for Sys {
player_uid, player_uid,
waypoint, waypoint,
active_abilities, active_abilities,
map_marker,
)| match presence.kind { )| match presence.kind {
PresenceKind::Character(id) => { PresenceKind::Character(id) => {
let pets = (&alignments, &bodies, &stats, &pets) let pets = (&alignments, &bodies, &stats, &pets)
@ -86,7 +90,15 @@ impl<'a> System<'a> for Sys {
}) })
.collect(); .collect();
Some((id, skill_set, inventory, pets, waypoint, active_abilities)) Some((
id,
skill_set,
inventory,
pets,
waypoint,
active_abilities,
map_marker,
))
}, },
PresenceKind::Spectator => None, PresenceKind::Spectator => None,
}, },

View File

@ -368,6 +368,7 @@ image_ids! {
indicator_group_up: "voxygen.element.ui.map.buttons.group_indicator_arrow_up", indicator_group_up: "voxygen.element.ui.map.buttons.group_indicator_arrow_up",
indicator_group_down: "voxygen.element.ui.map.buttons.group_indicator_arrow_down", indicator_group_down: "voxygen.element.ui.map.buttons.group_indicator_arrow_down",
location_marker: "voxygen.element.ui.map.buttons.location_marker", 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", map_mode_overlay: "voxygen.element.ui.map.buttons.map_modes",
minimap_mode_overlay: "voxygen.element.ui.map.buttons.minimap_modes", minimap_mode_overlay: "voxygen.element.ui.map.buttons.minimap_modes",

View File

@ -1,6 +1,6 @@
use super::{ use super::{
img_ids::{Imgs, ImgsRot}, 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, TEXT_BLUE_COLOR, TEXT_COLOR, TEXT_GRAY_COLOR, TEXT_VELORITE, UI_HIGHLIGHT_0, UI_MAIN,
}; };
use crate::{ use crate::{
@ -51,6 +51,7 @@ widget_ids! {
member_indicators[], member_indicators[],
member_height_indicators[], member_height_indicators[],
location_marker, location_marker,
location_marker_group[],
map_settings_align, map_settings_align,
show_towns_img, show_towns_img,
show_towns_box, show_towns_box,
@ -98,7 +99,6 @@ const SHOW_ECONOMY: bool = false; // turn this display off (for 0.9) until we ha
#[derive(WidgetCommon)] #[derive(WidgetCommon)]
pub struct Map<'a> { pub struct Map<'a> {
show: &'a Show,
client: &'a Client, client: &'a Client,
world_map: &'a (Vec<img_ids::Rotations>, Vec2<u32>), world_map: &'a (Vec<img_ids::Rotations>, Vec2<u32>),
imgs: &'a Imgs, imgs: &'a Imgs,
@ -110,12 +110,11 @@ pub struct Map<'a> {
global_state: &'a GlobalState, global_state: &'a GlobalState,
rot_imgs: &'a ImgsRot, rot_imgs: &'a ImgsRot,
tooltip_manager: &'a mut TooltipManager, tooltip_manager: &'a mut TooltipManager,
location_marker: Option<Vec2<f32>>, location_markers: &'a MapMarkers,
map_drag: Vec2<f64>, map_drag: Vec2<f64>,
} }
impl<'a> Map<'a> { impl<'a> Map<'a> {
pub fn new( pub fn new(
show: &'a Show,
client: &'a Client, client: &'a Client,
imgs: &'a Imgs, imgs: &'a Imgs,
rot_imgs: &'a ImgsRot, rot_imgs: &'a ImgsRot,
@ -125,11 +124,10 @@ impl<'a> Map<'a> {
localized_strings: &'a Localization, localized_strings: &'a Localization,
global_state: &'a GlobalState, global_state: &'a GlobalState,
tooltip_manager: &'a mut TooltipManager, tooltip_manager: &'a mut TooltipManager,
location_marker: Option<Vec2<f32>>, location_markers: &'a MapMarkers,
map_drag: Vec2<f64>, map_drag: Vec2<f64>,
) -> Self { ) -> Self {
Self { Self {
show,
imgs, imgs,
rot_imgs, rot_imgs,
world_map, world_map,
@ -140,7 +138,7 @@ impl<'a> Map<'a> {
localized_strings, localized_strings,
global_state, global_state,
tooltip_manager, tooltip_manager,
location_marker, location_markers,
map_drag, map_drag,
} }
} }
@ -154,9 +152,9 @@ pub enum Event {
SettingsChange(InterfaceChange), SettingsChange(InterfaceChange),
Close, Close,
RequestSiteInfo(SiteId), RequestSiteInfo(SiteId),
SetLocationMarker(Vec2<f32>), SetLocationMarker(Vec2<i32>),
MapDrag(Vec2<f64>), MapDrag(Vec2<f64>),
ToggleMarker, RemoveMarker,
} }
fn get_site_economy(site_rich: &SiteInfoRich) -> String { fn get_site_economy(site_rich: &SiteInfoRich) -> String {
@ -370,16 +368,15 @@ impl<'a> Widget for Map<'a> {
.next() .next()
{ {
match wpos { match wpos {
Some(ref wpos) => events.push(Event::SetLocationMarker(*wpos)), Some(ref wpos) => events.push(Event::SetLocationMarker(wpos.as_())),
None => { None => {
let tmp: Vec2<f64> = Vec2::<f64>::from(click.xy) / zoom - drag; let tmp: Vec2<f64> = Vec2::<f64>::from(click.xy) / zoom - drag;
let wpos = tmp let wpos = tmp
.map2(TerrainChunkSize::RECT_SIZE, |e, sz| e as f32 * sz as f32) .map2(TerrainChunkSize::RECT_SIZE, |e, sz| e as f32 * sz as f32)
+ player_pos; + player_pos;
events.push(Event::SetLocationMarker(wpos)); events.push(Event::SetLocationMarker(wpos.as_()));
}, },
} }
events.push(Event::ToggleMarker);
} }
// Handle zooming with the mousewheel // Handle zooming with the mousewheel
@ -1220,18 +1217,36 @@ impl<'a> Widget for Map<'a> {
} }
} }
// Location marker let factor = 1.4;
if self.show.map_marker { let side_length = 20.0 * factor;
let factor = 1.4; // Groups location markers
let side_length = 20.0 * factor; if state.ids.location_marker_group.len() < self.location_markers.group.len() {
if let Some((lm, (rpos, fade))) = self.location_marker.and_then(|lm| { state.update(|s| {
Some(lm).zip(wpos_to_rpos_fade( s.ids.location_marker_group.resize(
lm, self.location_markers.group.len(),
Vec2::from(side_length / 2.0), &mut ui.widget_id_generator(),
side_length / 2.0, )
)) })
}) { };
if Button::image(self.imgs.location_marker) 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( .x_y_position_relative_to(
state.ids.map_layers[0], state.ids.map_layers[0],
position::Relative::Scalar(rpos.x as f64), position::Relative::Scalar(rpos.x as f64),
@ -1247,26 +1262,58 @@ impl<'a> Widget for Map<'a> {
"X: {}, Y: {}\n\n{}", "X: {}, Y: {}\n\n{}",
lm.x as i32, lm.x as i32,
lm.y as i32, lm.y as i32,
i18n.get("hud.map.marked_location_remove") i18n.get("hud.map.placed_by").replace("{name}", name),
), ),
&site_tooltip, &site_tooltip,
TEXT_VELORITE, TEXT_VELORITE,
) )
.set(state.ids.location_marker, ui) .set(state.ids.location_marker_group[i], 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],
);
} }
} }
// 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 pos relative to playerpos and widget size
// Cursor stops moving on an axis as soon as it's position exceeds the maximum // Cursor stops moving on an axis as soon as it's position exceeds the maximum

View File

@ -1,7 +1,7 @@
use super::{ use super::{
img_ids::{Imgs, ImgsRot}, img_ids::{Imgs, ImgsRot},
Show, QUALITY_COMMON, QUALITY_DEBUG, QUALITY_EPIC, QUALITY_HIGH, QUALITY_LOW, QUALITY_MODERATE, MapMarkers, QUALITY_COMMON, QUALITY_DEBUG, QUALITY_EPIC, QUALITY_HIGH, QUALITY_LOW,
TEXT_COLOR, UI_HIGHLIGHT_0, UI_MAIN, QUALITY_MODERATE, TEXT_COLOR, UI_HIGHLIGHT_0, UI_MAIN,
}; };
use crate::{ use crate::{
hud::{Graphic, Ui}, hud::{Graphic, Ui},
@ -375,7 +375,6 @@ widget_ids! {
#[derive(WidgetCommon)] #[derive(WidgetCommon)]
pub struct MiniMap<'a> { pub struct MiniMap<'a> {
show: &'a Show,
client: &'a Client, client: &'a Client,
imgs: &'a Imgs, imgs: &'a Imgs,
rot_imgs: &'a ImgsRot, rot_imgs: &'a ImgsRot,
@ -385,13 +384,12 @@ pub struct MiniMap<'a> {
common: widget::CommonBuilder, common: widget::CommonBuilder,
ori: Vec3<f32>, ori: Vec3<f32>,
global_state: &'a GlobalState, global_state: &'a GlobalState,
location_marker: Option<Vec2<f32>>, location_markers: &'a MapMarkers,
voxel_minimap: &'a VoxelMinimap, voxel_minimap: &'a VoxelMinimap,
} }
impl<'a> MiniMap<'a> { impl<'a> MiniMap<'a> {
pub fn new( pub fn new(
show: &'a Show,
client: &'a Client, client: &'a Client,
imgs: &'a Imgs, imgs: &'a Imgs,
rot_imgs: &'a ImgsRot, rot_imgs: &'a ImgsRot,
@ -399,11 +397,10 @@ impl<'a> MiniMap<'a> {
fonts: &'a Fonts, fonts: &'a Fonts,
ori: Vec3<f32>, ori: Vec3<f32>,
global_state: &'a GlobalState, global_state: &'a GlobalState,
location_marker: Option<Vec2<f32>>, location_markers: &'a MapMarkers,
voxel_minimap: &'a VoxelMinimap, voxel_minimap: &'a VoxelMinimap,
) -> Self { ) -> Self {
Self { Self {
show,
client, client,
imgs, imgs,
rot_imgs, rot_imgs,
@ -412,7 +409,7 @@ impl<'a> MiniMap<'a> {
common: widget::CommonBuilder::default(), common: widget::CommonBuilder::default(),
ori, ori,
global_state, global_state,
location_marker, location_markers,
voxel_minimap, voxel_minimap,
} }
} }
@ -792,11 +789,14 @@ impl<'a> Widget for MiniMap<'a> {
} }
// Location marker // Location marker
if self.show.map_marker { if let Some(rpos) = self
if let Some(rpos) = self.location_marker.and_then(|lm| wpos_to_rpos(lm, true)) { .location_markers
let factor = 1.2; .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( .x_y_position_relative_to(
state.ids.map_layers[0], state.ids.map_layers[0],
position::Relative::Scalar(rpos.x as f64), 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)) //.image_color(Color::Rgba(1.0, 1.0, 1.0, 1.0))
.floating(true) .floating(true)
.set(state.ids.location_marker, ui); .set(state.ids.location_marker, ui);
}
} }
// Indicator // Indicator
let ind_scale = 0.4; let ind_scale = 0.4;

View File

@ -83,7 +83,7 @@ use common::{
inventory::{slot::InvSlotId, trade_pricing::TradePricing}, inventory::{slot::InvSlotId, trade_pricing::TradePricing},
item::{tool::ToolKind, ItemDesc, MaterialStatManifest, Quality}, item::{tool::ToolKind, ItemDesc, MaterialStatManifest, Quality},
skillset::{skills::Skill, SkillGroupKind}, skillset::{skills::Skill, SkillGroupKind},
BuffData, BuffKind, Item, BuffData, BuffKind, Item, MapMarkerChange,
}, },
consts::MAX_PICKUP_RANGE, consts::MAX_PICKUP_RANGE,
link::Is, link::Is,
@ -546,6 +546,7 @@ pub enum Event {
SettingsChange(SettingsChange), SettingsChange(SettingsChange),
AcknowledgePersistenceLoadError, AcknowledgePersistenceLoadError,
MapMarkerEvent(MapMarkerChange),
} }
// TODO: Are these the possible layouts we want? // TODO: Are these the possible layouts we want?
@ -638,6 +639,12 @@ impl PressBehavior {
} }
} }
#[derive(Default, Clone)]
pub struct MapMarkers {
owned: Option<Vec2<i32>>,
group: HashMap<Uid, Vec2<i32>>,
}
pub struct Show { pub struct Show {
ui: bool, ui: bool,
intro: bool, intro: bool,
@ -667,8 +674,7 @@ pub struct Show {
auto_walk: bool, auto_walk: bool,
camera_clamp: bool, camera_clamp: bool,
prompt_dialog: Option<PromptDialogSettings>, prompt_dialog: Option<PromptDialogSettings>,
location_marker: Option<Vec2<f32>>, location_markers: MapMarkers,
map_marker: bool,
salvage: bool, salvage: bool,
} }
impl Show { impl Show {
@ -889,6 +895,26 @@ impl Show {
global_state.window.center_cursor(); 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 { pub struct PromptDialogSettings {
@ -1060,8 +1086,7 @@ impl Hud {
auto_walk: false, auto_walk: false,
camera_clamp: false, camera_clamp: false,
prompt_dialog: None, prompt_dialog: None,
location_marker: None, location_markers: MapMarkers::default(),
map_marker: false,
salvage: false, salvage: false,
}, },
to_focus: None, to_focus: None,
@ -2756,7 +2781,6 @@ impl Hud {
// MiniMap // MiniMap
for event in MiniMap::new( for event in MiniMap::new(
&self.show,
client, client,
&self.imgs, &self.imgs,
&self.rot_imgs, &self.rot_imgs,
@ -2764,7 +2788,7 @@ impl Hud {
&self.fonts, &self.fonts,
camera.get_orientation(), camera.get_orientation(),
global_state, global_state,
self.show.location_marker, &self.show.location_markers,
&self.voxel_minimap, &self.voxel_minimap,
) )
.set(self.ids.minimap, ui_widgets) .set(self.ids.minimap, ui_widgets)
@ -3272,7 +3296,6 @@ impl Hud {
// Map // Map
if self.show.map { if self.show.map {
for event in Map::new( for event in Map::new(
&self.show,
client, client,
&self.imgs, &self.imgs,
&self.rot_imgs, &self.rot_imgs,
@ -3282,7 +3305,7 @@ impl Hud {
i18n, i18n,
global_state, global_state,
tooltip_manager, tooltip_manager,
self.show.location_marker, &self.show.location_markers,
self.map_drag, self.map_drag,
) )
.set(self.ids.map, ui_widgets) .set(self.ids.map, ui_widgets)
@ -3300,13 +3323,15 @@ impl Hud {
events.push(Event::RequestSiteInfo(id)); events.push(Event::RequestSiteInfo(id));
}, },
map::Event::SetLocationMarker(pos) => { 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) => { map::Event::MapDrag(new_drag) => {
self.map_drag = new_drag; self.map_drag = new_drag;
}, },
map::Event::ToggleMarker => { map::Event::RemoveMarker => {
self.show.map_marker = !self.show.map_marker; self.show.location_markers.owned = None;
events.push(Event::MapMarkerEvent(MapMarkerChange::Remove));
}, },
} }
} }

View File

@ -308,6 +308,9 @@ impl SessionState {
client::Event::CharacterError(error) => { client::Event::CharacterError(error) => {
global_state.client_error = Some(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() .borrow_mut()
.acknolwedge_persistence_load_error(); .acknolwedge_persistence_load_error();
}, },
HudEvent::MapMarkerEvent(event) => {
self.client.borrow_mut().map_marker_event(event);
},
} }
} }