Implement /join_group and /join_faction commands

This commit is contained in:
CapsizeGlimmer
2020-06-04 05:40:05 -04:00
committed by Forest Anderson
parent 3742128186
commit 54a3f7e442
6 changed files with 207 additions and 43 deletions

View File

@ -43,6 +43,8 @@ pub enum ChatCommand {
Group, Group,
Health, Health,
Help, Help,
JoinFaction,
JoinGroup,
Jump, Jump,
Kill, Kill,
KillNpcs, KillNpcs,
@ -81,6 +83,8 @@ pub static CHAT_COMMANDS: &[ChatCommand] = &[
ChatCommand::Group, ChatCommand::Group,
ChatCommand::Health, ChatCommand::Health,
ChatCommand::Help, ChatCommand::Help,
ChatCommand::JoinFaction,
ChatCommand::JoinGroup,
ChatCommand::Jump, ChatCommand::Jump,
ChatCommand::Kill, ChatCommand::Kill,
ChatCommand::KillNpcs, ChatCommand::KillNpcs,
@ -218,6 +222,16 @@ impl ChatCommand {
"Display information about commands", "Display information about commands",
NoAdmin, NoAdmin,
), ),
ChatCommand::JoinFaction => ChatCommandData::new(
vec![Any("faction", Optional)],
"Join/leave the specified faction",
NoAdmin,
),
ChatCommand::JoinGroup => ChatCommandData::new(
vec![Any("group", Optional)],
"Join/leave the specified group",
NoAdmin,
),
ChatCommand::Jump => cmd( ChatCommand::Jump => cmd(
vec![ vec![
Float("x", 0.0, Required), Float("x", 0.0, Required),
@ -336,6 +350,8 @@ impl ChatCommand {
ChatCommand::Goto => "goto", ChatCommand::Goto => "goto",
ChatCommand::Group => "group", ChatCommand::Group => "group",
ChatCommand::Health => "health", ChatCommand::Health => "health",
ChatCommand::JoinFaction => "join_faction",
ChatCommand::JoinGroup => "join_group",
ChatCommand::Help => "help", ChatCommand::Help => "help",
ChatCommand::Jump => "jump", ChatCommand::Jump => "jump",
ChatCommand::Kill => "kill", ChatCommand::Kill => "kill",

View File

@ -4,7 +4,7 @@ use specs_idvs::IDVStorage;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
/// A player's current chat mode. /// A player's current chat mode.
#[derive(Copy, Clone, Debug)] #[derive(Clone, Debug)]
pub enum ChatMode { pub enum ChatMode {
/// Private message to another player (by uuid) /// Private message to another player (by uuid)
Tell(Uid), Tell(Uid),
@ -13,9 +13,9 @@ pub enum ChatMode {
/// Talk to players in your region of the world /// Talk to players in your region of the world
Region, Region,
/// Talk to your current group of players /// Talk to your current group of players
Group, Group(String),
/// Talk to your faction /// Talk to your faction
Faction, Faction(String),
/// Talk to every player on the server /// Talk to every player on the server
World, World,
} }
@ -31,18 +31,22 @@ impl ChatMode {
ChatMode::Tell(to) => ChatType::Tell(from, *to), ChatMode::Tell(to) => ChatType::Tell(from, *to),
ChatMode::Say => ChatType::Say(from), ChatMode::Say => ChatType::Say(from),
ChatMode::Region => ChatType::Region(from), ChatMode::Region => ChatType::Region(from),
ChatMode::Group => ChatType::Group(from), ChatMode::Group(name) => ChatType::Group(from, name.to_string()),
ChatMode::Faction => ChatType::Faction(from), ChatMode::Faction(name) => ChatType::Faction(from, name.to_string()),
ChatMode::World => ChatType::World(from), ChatMode::World => ChatType::World(from),
}; };
ChatMsg { chat_type, message } ChatMsg { chat_type, message }
} }
} }
impl Default for ChatMode {
fn default() -> Self { Self::World }
}
/// List of chat types. Note that this is a superset of `ChatMode`; this is /// List of chat types. Note that this is a superset of `ChatMode`; this is
/// because `ChatType::Kill`, `ChatType::Broadcast`, and `ChatType::Private` /// because `ChatType::Kill`, `ChatType::Broadcast`, and `ChatType::Private`
/// cannot be sent by players. /// cannot be sent by players.
#[derive(Copy, Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ChatType { pub enum ChatType {
/// Tell all players something (such as players connecting or alias changes) /// Tell all players something (such as players connecting or alias changes)
Broadcast, Broadcast,
@ -55,9 +59,9 @@ pub enum ChatType {
/// Chat with nearby players /// Chat with nearby players
Say(Uid), Say(Uid),
/// Group chat /// Group chat
Group(Uid), Group(Uid, String),
/// Factional chat /// Factional chat
Faction(Uid), Faction(Uid, String),
/// Regional chat /// Regional chat
Region(Uid), Region(Uid),
/// World chat /// World chat
@ -81,23 +85,23 @@ impl ChatMsg {
} }
pub fn to_bubble(&self) -> Option<(SpeechBubble, Uid)> { pub fn to_bubble(&self) -> Option<(SpeechBubble, Uid)> {
let tuple = match self.chat_type { let tuple = match &self.chat_type {
ChatType::Broadcast => None, ChatType::Broadcast => None,
ChatType::Private => None, ChatType::Private => None,
ChatType::Kill => None, ChatType::Kill => None,
ChatType::Tell(u, _) => Some((SpeechBubbleIcon::Tell, u, None)), ChatType::Tell(u, _) => Some((SpeechBubbleIcon::Tell, u, None)),
ChatType::Say(u) => Some((SpeechBubbleIcon::Say, u, None)), ChatType::Say(u) => Some((SpeechBubbleIcon::Say, u, None)),
ChatType::Group(u) => Some((SpeechBubbleIcon::Group, u, None)), ChatType::Group(u, _s) => Some((SpeechBubbleIcon::Group, u, None)),
ChatType::Faction(u) => Some((SpeechBubbleIcon::Faction, u, None)), ChatType::Faction(u, _s) => Some((SpeechBubbleIcon::Faction, u, None)),
ChatType::Region(u) => Some((SpeechBubbleIcon::Region, u, None)), ChatType::Region(u) => Some((SpeechBubbleIcon::Region, u, None)),
ChatType::World(u) => Some((SpeechBubbleIcon::World, u, None)), ChatType::World(u) => Some((SpeechBubbleIcon::World, u, None)),
ChatType::Npc(u, r) => Some((SpeechBubbleIcon::None, u, Some(r))), ChatType::Npc(u, r) => Some((SpeechBubbleIcon::None, u, Some(r))),
}; };
tuple.map(|(icon, from, npc_rand)| { tuple.map(|(icon, from, npc_rand)| {
if let Some(r) = npc_rand { if let Some(r) = npc_rand {
(SpeechBubble::npc_new(self.message.clone(), r, icon), from) (SpeechBubble::npc_new(self.message.clone(), *r, icon), *from)
} else { } else {
(SpeechBubble::player_new(self.message.clone(), icon), from) (SpeechBubble::player_new(self.message.clone(), icon), *from)
} }
}) })
} }
@ -107,19 +111,27 @@ impl ChatMsg {
/// gameplay. /// gameplay.
/// ///
/// Groups are currently just an associated String (the group's name) /// Groups are currently just an associated String (the group's name)
pub struct Group(String); #[derive(Clone, Debug)]
pub struct Group(pub String);
impl Component for Group { impl Component for Group {
type Storage = IDVStorage<Self>; type Storage = IDVStorage<Self>;
} }
impl From<String> for Group {
fn from(s: String) -> Self { Group(s) }
}
/// Player factions are used to coordinate pvp vs hostile factions or segment /// Player factions are used to coordinate pvp vs hostile factions or segment
/// chat from the world /// chat from the world
/// ///
/// Factions are currently just an associated String (the faction's name) /// Factions are currently just an associated String (the faction's name)
pub struct Faction(String); #[derive(Clone, Debug)]
pub struct Faction(pub String);
impl Component for Faction { impl Component for Faction {
type Storage = IDVStorage<Self>; type Storage = IDVStorage<Self>;
} }
impl From<String> for Faction {
fn from(s: String) -> Self { Faction(s) }
}
/// The contents of a speech bubble /// The contents of a speech bubble
pub enum SpeechBubbleMessage { pub enum SpeechBubbleMessage {

View File

@ -155,6 +155,8 @@ impl State {
ecs.register::<comp::Attacking>(); ecs.register::<comp::Attacking>();
ecs.register::<comp::ItemDrop>(); ecs.register::<comp::ItemDrop>();
ecs.register::<comp::ChatMode>(); ecs.register::<comp::ChatMode>();
ecs.register::<comp::Group>();
ecs.register::<comp::Faction>();
// Register synced resources used by the ECS. // Register synced resources used by the ECS.
ecs.insert(TimeOfDay(0.0)); ecs.insert(TimeOfDay(0.0));

View File

@ -8,6 +8,7 @@ use common::{
assets, assets,
cmd::{ChatCommand, CHAT_COMMANDS}, cmd::{ChatCommand, CHAT_COMMANDS},
comp, comp,
comp::Item,
event::{EventBus, ServerEvent}, event::{EventBus, ServerEvent},
msg::{Notification, PlayerListUpdate, ServerMsg}, msg::{Notification, PlayerListUpdate, ServerMsg},
npc::{self, get_npc_name}, npc::{self, get_npc_name},
@ -75,6 +76,8 @@ fn get_handler(cmd: &ChatCommand) -> CommandHandler {
ChatCommand::Group => handle_group, ChatCommand::Group => handle_group,
ChatCommand::Health => handle_health, ChatCommand::Health => handle_health,
ChatCommand::Help => handle_help, ChatCommand::Help => handle_help,
ChatCommand::JoinFaction => handle_join_faction,
ChatCommand::JoinGroup => handle_join_group,
ChatCommand::Jump => handle_jump, ChatCommand::Jump => handle_jump,
ChatCommand::Kill => handle_kill, ChatCommand::Kill => handle_kill,
ChatCommand::KillNpcs => handle_kill_npcs, ChatCommand::KillNpcs => handle_kill_npcs,
@ -1020,13 +1023,20 @@ fn handle_tell(
.get(player) .get(player)
.expect("Player must have uid"); .expect("Player must have uid");
let mode = comp::ChatMode::Tell(player_uid); let mode = comp::ChatMode::Tell(player_uid);
let _ = server.state.ecs().write_storage().insert(client, mode); let _ = server
.state
.ecs()
.write_storage()
.insert(client, mode.clone());
let msg = if msg.is_empty() { let msg = if msg.is_empty() {
format!("{} wants to talk to you.", alias) format!("{} wants to talk to you.", alias)
} else { } else {
msg.to_string() msg.to_string()
}; };
server.notify_client(player, ServerMsg::chat(mode, client_uid, msg.clone())); server.notify_client(
player,
ServerMsg::chat(mode.clone(), client_uid, msg.clone()),
);
server.notify_client(client, ServerMsg::chat(mode, client_uid, msg)); server.notify_client(client, ServerMsg::chat(mode, client_uid, msg));
} else { } else {
server.notify_client( server.notify_client(
@ -1057,14 +1067,24 @@ fn handle_faction(
); );
return; return;
} }
let mode = comp::ChatMode::Faction; let ecs = server.state.ecs();
let _ = server.state.ecs().write_storage().insert(client, mode); if let Some(comp::Faction(faction)) = ecs.read_storage().get(client) {
if !msg.is_empty() { let mode = comp::ChatMode::Faction(faction.to_string());
if let Some(uid) = server.state.ecs().read_storage().get(client) { let _ = ecs.write_storage().insert(client, mode.clone());
server if !msg.is_empty() {
.state if let Some(uid) = ecs.read_storage().get(client) {
.notify_registered_clients(ServerMsg::chat(mode, *uid, msg.to_string())); server.state.notify_registered_clients(ServerMsg::chat(
mode,
*uid,
msg.to_string(),
));
}
} }
} else {
server.notify_client(
client,
ServerMsg::private(String::from("Please join a faction with /join_faction")),
);
} }
} }
@ -1083,14 +1103,24 @@ fn handle_group(
); );
return; return;
} }
let mode = comp::ChatMode::Group; let ecs = server.state.ecs();
let _ = server.state.ecs().write_storage().insert(client, mode); if let Some(comp::Group(group)) = ecs.read_storage().get(client) {
if !msg.is_empty() { let mode = comp::ChatMode::Group(group.to_string());
if let Some(uid) = server.state.ecs().read_storage().get(client) { let _ = ecs.write_storage().insert(client, mode.clone());
server if !msg.is_empty() {
.state if let Some(uid) = ecs.read_storage().get(client) {
.notify_registered_clients(ServerMsg::chat(mode, *uid, msg.to_string())); server.state.notify_registered_clients(ServerMsg::chat(
mode,
*uid,
msg.to_string(),
));
}
} }
} else {
server.notify_client(
client,
ServerMsg::private(String::from("Please join a group with /join_group")),
);
} }
} }
@ -1110,7 +1140,11 @@ fn handle_region(
return; return;
} }
let mode = comp::ChatMode::Region; let mode = comp::ChatMode::Region;
let _ = server.state.ecs().write_storage().insert(client, mode); let _ = server
.state
.ecs()
.write_storage()
.insert(client, mode.clone());
if !msg.is_empty() { if !msg.is_empty() {
if let Some(uid) = server.state.ecs().read_storage().get(client) { if let Some(uid) = server.state.ecs().read_storage().get(client) {
server server
@ -1136,7 +1170,11 @@ fn handle_say(
return; return;
} }
let mode = comp::ChatMode::Say; let mode = comp::ChatMode::Say;
let _ = server.state.ecs().write_storage().insert(client, mode); let _ = server
.state
.ecs()
.write_storage()
.insert(client, mode.clone());
if !msg.is_empty() { if !msg.is_empty() {
if let Some(uid) = server.state.ecs().read_storage().get(client) { if let Some(uid) = server.state.ecs().read_storage().get(client) {
server server
@ -1162,7 +1200,11 @@ fn handle_world(
return; return;
} }
let mode = comp::ChatMode::World; let mode = comp::ChatMode::World;
let _ = server.state.ecs().write_storage().insert(client, mode); let _ = server
.state
.ecs()
.write_storage()
.insert(client, mode.clone());
if !msg.is_empty() { if !msg.is_empty() {
if let Some(uid) = server.state.ecs().read_storage().get(client) { if let Some(uid) = server.state.ecs().read_storage().get(client) {
server server
@ -1172,6 +1214,88 @@ fn handle_world(
} }
} }
fn handle_join_faction(
server: &mut Server,
client: EcsEntity,
target: EcsEntity,
args: String,
action: &ChatCommand,
) {
if client != target {
// This happens when [ab]using /sudo
server.notify_client(
client,
ServerMsg::private(String::from("It's rude to impersonate people")),
);
return;
}
if let Ok(faction) = scan_fmt!(&args, &action.arg_fmt(), String) {
let mode = comp::ChatMode::Faction(faction.clone());
let _ = server.state.ecs().write_storage().insert(client, mode);
let _ = server
.state
.ecs()
.write_storage()
.insert(client, comp::Faction(faction.clone()));
// TODO notify faction
server.notify_client(
client,
ServerMsg::private(format!("Joined faction {{{}}}", faction)),
);
} else {
let mode = comp::ChatMode::default();
let _ = server.state.ecs().write_storage().insert(client, mode);
if let Some(comp::Faction(faction)) = server.state.ecs().write_storage().remove(client) {
// TODO notify faction
server.notify_client(
client,
ServerMsg::private(format!("Left faction {{{}}}", faction)),
);
}
}
}
fn handle_join_group(
server: &mut Server,
client: EcsEntity,
target: EcsEntity,
args: String,
action: &ChatCommand,
) {
if client != target {
// This happens when [ab]using /sudo
server.notify_client(
client,
ServerMsg::private(String::from("It's rude to impersonate people")),
);
return;
}
if let Ok(group) = scan_fmt!(&args, &action.arg_fmt(), String) {
let mode = comp::ChatMode::Group(group.clone());
let _ = server.state.ecs().write_storage().insert(client, mode);
let _ = server
.state
.ecs()
.write_storage()
.insert(client, comp::Group(group.clone()));
// TODO notify group
server.notify_client(
client,
ServerMsg::private(format!("Joined group {{{}}}", group)),
);
} else {
let mode = comp::ChatMode::default();
let _ = server.state.ecs().write_storage().insert(client, mode);
if let Some(comp::Group(group)) = server.state.ecs().write_storage().remove(client) {
// TODO notify group
server.notify_client(
client,
ServerMsg::private(format!("Left group {{{}}}", group)),
);
}
}
}
#[cfg(not(feature = "worldgen"))] #[cfg(not(feature = "worldgen"))]
fn handle_debug_column( fn handle_debug_column(
server: &mut Server, server: &mut Server,
@ -1375,7 +1499,6 @@ fn handle_set_level(
} }
} }
use common::comp::Item;
fn handle_debug( fn handle_debug(
server: &mut Server, server: &mut Server,
client: EcsEntity, client: EcsEntity,

View File

@ -328,8 +328,11 @@ impl<'a> System<'a> for Sys {
| ClientState::Character => match validate_chat_msg(&message) { | ClientState::Character => match validate_chat_msg(&message) {
Ok(()) => { Ok(()) => {
if let Some(from) = uids.get(entity) { if let Some(from) = uids.get(entity) {
let mode = chat_modes.get(entity).unwrap_or(&ChatMode::World); let mode = chat_modes
let msg = ServerMsg::chat(*mode, *from, message); .get(entity)
.map(Clone::clone)
.unwrap_or(ChatMode::default());
let msg = ServerMsg::chat(mode, *from, message);
new_chat_msgs.push((Some(entity), msg)); new_chat_msgs.push((Some(entity), msg));
} else { } else {
tracing::error!("Could not send message. Missing player uid"); tracing::error!("Could not send message. Missing player uid");

View File

@ -321,7 +321,13 @@ impl<'a> Widget for Chat<'a> {
} }
}) })
}; };
let message_format = |uid, message| format!("[{}]: {}", alias_of_uid(uid), message); let message_format = |uid, message, group| {
if let Some(group) = group {
format!("{{{}}} [{}]: {}", group, alias_of_uid(uid), message)
} else {
format!("[{}]: {}", alias_of_uid(uid), message)
}
};
// Message box // Message box
Rectangle::fill([470.0, 174.0]) Rectangle::fill([470.0, 174.0])
.rgba(0.0, 0.0, 0.0, transp) .rgba(0.0, 0.0, 0.0, transp)
@ -362,11 +368,13 @@ impl<'a> Widget for Chat<'a> {
(TELL_COLOR, format!("From [{}]: {}", from_alias, message)) (TELL_COLOR, format!("From [{}]: {}", from_alias, message))
} }
}, },
ChatType::Say(uid) => (SAY_COLOR, message_format(uid, message)), ChatType::Say(uid) => (SAY_COLOR, message_format(uid, message, None)),
ChatType::Group(uid) => (GROUP_COLOR, message_format(uid, message)), ChatType::Group(uid, s) => (GROUP_COLOR, message_format(uid, message, Some(s))),
ChatType::Faction(uid) => (FACTION_COLOR, message_format(uid, message)), ChatType::Faction(uid, s) => {
ChatType::Region(uid) => (REGION_COLOR, message_format(uid, message)), (FACTION_COLOR, message_format(uid, message, Some(s)))
ChatType::World(uid) => (WORLD_COLOR, message_format(uid, message)), },
ChatType::Region(uid) => (REGION_COLOR, message_format(uid, message, None)),
ChatType::World(uid) => (WORLD_COLOR, message_format(uid, message, None)),
ChatType::Npc(_uid, _r) => continue, // Should be filtered by hud/mod.rs ChatType::Npc(_uid, _r) => continue, // Should be filtered by hud/mod.rs
}; };
let text = Text::new(&msg) let text = Text::new(&msg)