1use crate::{
2 comp::{BuffKind, group::Group},
3 uid::Uid,
4};
5use common_i18n::Content;
6use serde::{Deserialize, Serialize};
7use specs::{Component, DenseVecStorage};
8use std::time::{Duration, Instant};
9
10#[derive(Clone, Debug, Serialize, Deserialize)]
13pub enum ChatMode {
14 Tell(Uid),
16 Say,
18 Region,
20 Group,
22 Faction(String),
24 World,
26}
27
28impl Component for ChatMode {
29 type Storage = DenseVecStorage<Self>;
30}
31
32impl ChatMode {
33 pub fn to_msg(
35 &self,
36 from: Uid,
37 content: Content,
38 group: Option<Group>,
39 ) -> Result<UnresolvedChatMsg, Content> {
40 let chat_type = match self {
41 ChatMode::Tell(to) => ChatType::Tell(from, *to),
42 ChatMode::Say => ChatType::Say(from),
43 ChatMode::Region => ChatType::Region(from),
44 ChatMode::Group => ChatType::Group(
45 from,
46 group.ok_or(Content::localized("command-message-group-missing"))?,
47 ),
48 ChatMode::Faction(faction) => ChatType::Faction(from, faction.clone()),
49 ChatMode::World => ChatType::World(from),
50 };
51
52 Ok(UnresolvedChatMsg { chat_type, content })
53 }
54}
55
56impl ChatMode {
57 pub const fn default() -> Self { Self::World }
58}
59
60#[derive(Debug, Clone, Serialize, Deserialize)]
66pub enum KillType {
67 Buff(BuffKind),
68 Melee,
69 Projectile,
70 Explosion,
71 Energy,
72 Other,
73 }
75
76#[derive(Debug, Clone, Serialize, Deserialize)]
80pub enum KillSource {
81 Player(Uid, KillType),
82 NonPlayer(String, KillType),
83 NonExistent(KillType),
84 FallDamage,
85 Suicide,
86 Other,
87}
88
89#[derive(Debug, Clone, Serialize, Deserialize)]
93pub enum ChatType<G> {
94 Online(Uid),
96 Offline(Uid),
98 CommandInfo,
100 CommandError,
102 Kill(KillSource, Uid),
105 GroupMeta(G),
107 FactionMeta(String),
109 Tell(Uid, Uid),
111 Say(Uid),
113 Group(Uid, G),
115 Faction(Uid, String),
117 Region(Uid),
119 World(Uid),
121 Npc(Uid),
123 NpcSay(Uid),
125 NpcTell(Uid, Uid),
128 Meta,
130}
131
132impl<G> ChatType<G> {
133 pub fn into_plain_msg(self, text: impl ToString) -> GenericChatMsg<G> {
134 GenericChatMsg {
135 chat_type: self,
136 content: Content::Plain(text.to_string()),
137 }
138 }
139
140 pub fn into_msg(self, content: Content) -> GenericChatMsg<G> {
141 GenericChatMsg {
142 chat_type: self,
143 content,
144 }
145 }
146
147 pub fn uid(&self) -> Option<Uid> {
148 match self {
149 ChatType::Online(_) => None,
150 ChatType::Offline(_) => None,
151 ChatType::CommandInfo => None,
152 ChatType::CommandError => None,
153 ChatType::FactionMeta(_) => None,
154 ChatType::GroupMeta(_) => None,
155 ChatType::Kill(_, _) => None,
156 ChatType::Tell(u, _t) => Some(*u),
157 ChatType::Say(u) => Some(*u),
158 ChatType::Group(u, _s) => Some(*u),
159 ChatType::Faction(u, _s) => Some(*u),
160 ChatType::Region(u) => Some(*u),
161 ChatType::World(u) => Some(*u),
162 ChatType::Npc(u) => Some(*u),
163 ChatType::NpcSay(u) => Some(*u),
164 ChatType::NpcTell(u, _t) => Some(*u),
165 ChatType::Meta => None,
166 }
167 }
168
169 pub fn is_private(&self) -> Option<bool> {
171 match self {
172 ChatType::Online(_)
173 | ChatType::Offline(_)
174 | ChatType::CommandInfo
175 | ChatType::CommandError
176 | ChatType::FactionMeta(_)
177 | ChatType::GroupMeta(_)
178 | ChatType::Npc(_)
179 | ChatType::NpcSay(_)
180 | ChatType::NpcTell(_, _)
181 | ChatType::Meta
182 | ChatType::Kill(_, _) => None,
183 ChatType::Tell(_, _) | ChatType::Group(_, _) | ChatType::Faction(_, _) => Some(true),
184 ChatType::Say(_) | ChatType::Region(_) | ChatType::World(_) => Some(false),
185 }
186 }
187}
188
189#[derive(Debug, Clone, Serialize, Deserialize)]
191pub struct GenericChatMsg<G> {
192 pub chat_type: ChatType<G>,
193 content: Content,
194}
195
196pub type ChatMsg = GenericChatMsg<String>;
197pub type UnresolvedChatMsg = GenericChatMsg<Group>;
198
199impl<G> GenericChatMsg<G> {
200 pub const MAX_BYTES_PLAYER_CHAT_MSG: usize = 256;
201 pub const NPC_DISTANCE: f32 = 100.0;
202 pub const NPC_SAY_DISTANCE: f32 = 30.0;
203 pub const REGION_DISTANCE: f32 = 1000.0;
204 pub const SAY_DISTANCE: f32 = 100.0;
205
206 pub fn npc(uid: Uid, content: Content) -> Self {
207 let chat_type = ChatType::Npc(uid);
208 Self { chat_type, content }
209 }
210
211 pub fn npc_say(uid: Uid, content: Content) -> Self {
212 let chat_type = ChatType::NpcSay(uid);
213 Self { chat_type, content }
214 }
215
216 pub fn npc_tell(from: Uid, to: Uid, content: Content) -> Self {
217 let chat_type = ChatType::NpcTell(from, to);
218 Self { chat_type, content }
219 }
220
221 pub fn death(kill_source: KillSource, victim: Uid) -> Self {
222 Self {
223 chat_type: ChatType::Kill(kill_source, victim),
224 content: Content::Plain(String::new()),
225 }
226 }
227
228 pub fn map_group<T>(self, mut f: impl FnMut(G) -> T) -> GenericChatMsg<T> {
229 let chat_type = match self.chat_type {
230 ChatType::Online(a) => ChatType::Online(a),
231 ChatType::Offline(a) => ChatType::Offline(a),
232 ChatType::CommandInfo => ChatType::CommandInfo,
233 ChatType::CommandError => ChatType::CommandError,
234 ChatType::FactionMeta(a) => ChatType::FactionMeta(a),
235 ChatType::GroupMeta(g) => ChatType::GroupMeta(f(g)),
236 ChatType::Kill(a, b) => ChatType::Kill(a, b),
237 ChatType::Tell(a, b) => ChatType::Tell(a, b),
238 ChatType::Say(a) => ChatType::Say(a),
239 ChatType::Group(a, g) => ChatType::Group(a, f(g)),
240 ChatType::Faction(a, b) => ChatType::Faction(a, b),
241 ChatType::Region(a) => ChatType::Region(a),
242 ChatType::World(a) => ChatType::World(a),
243 ChatType::Npc(a) => ChatType::Npc(a),
244 ChatType::NpcSay(a) => ChatType::NpcSay(a),
245 ChatType::NpcTell(a, b) => ChatType::NpcTell(a, b),
246 ChatType::Meta => ChatType::Meta,
247 };
248
249 GenericChatMsg {
250 chat_type,
251 content: self.content,
252 }
253 }
254
255 pub fn get_group(&self) -> Option<&G> {
256 match &self.chat_type {
257 ChatType::GroupMeta(g) => Some(g),
258 ChatType::Group(_, g) => Some(g),
259 _ => None,
260 }
261 }
262
263 pub fn to_bubble(&self) -> Option<(SpeechBubble, Uid)> {
264 self.uid()
265 .map(|from| (SpeechBubble::new(self.content.clone(), self.icon()), from))
266 }
267
268 pub fn icon(&self) -> SpeechBubbleType {
269 match &self.chat_type {
270 ChatType::Online(_) => SpeechBubbleType::None,
271 ChatType::Offline(_) => SpeechBubbleType::None,
272 ChatType::CommandInfo => SpeechBubbleType::None,
273 ChatType::CommandError => SpeechBubbleType::None,
274 ChatType::FactionMeta(_) => SpeechBubbleType::None,
275 ChatType::GroupMeta(_) => SpeechBubbleType::None,
276 ChatType::Kill(_, _) => SpeechBubbleType::None,
277 ChatType::Tell(_u, _) => SpeechBubbleType::Tell,
278 ChatType::Say(_u) => SpeechBubbleType::Say,
279 ChatType::Group(_u, _s) => SpeechBubbleType::Group,
280 ChatType::Faction(_u, _s) => SpeechBubbleType::Faction,
281 ChatType::Region(_u) => SpeechBubbleType::Region,
282 ChatType::World(_u) => SpeechBubbleType::World,
283 ChatType::Npc(_u) => SpeechBubbleType::None,
284 ChatType::NpcSay(_u) => SpeechBubbleType::Say,
285 ChatType::NpcTell(_f, _t) => SpeechBubbleType::Say,
286 ChatType::Meta => SpeechBubbleType::None,
287 }
288 }
289
290 pub fn uid(&self) -> Option<Uid> { self.chat_type.uid() }
291
292 pub fn content(&self) -> &Content { &self.content }
293
294 pub fn into_content(self) -> Content { self.content }
295
296 pub fn set_content(&mut self, content: Content) { self.content = content; }
297}
298
299#[derive(Clone, Debug)]
304pub struct Faction(pub String);
305impl Component for Faction {
306 type Storage = DenseVecStorage<Self>;
307}
308impl From<String> for Faction {
309 fn from(s: String) -> Self { Faction(s) }
310}
311
312pub enum SpeechBubbleType {
316 Tell,
318 Say,
319 Region,
320 Group,
321 Faction,
322 World,
323 Quest, Trade, None, }
328
329pub struct SpeechBubble {
331 pub content: Content,
332 pub icon: SpeechBubbleType,
333 pub timeout: Instant,
334}
335
336impl SpeechBubble {
337 pub const DEFAULT_DURATION: f64 = 5.0;
339
340 pub fn new(content: Content, icon: SpeechBubbleType) -> Self {
341 let timeout = Instant::now() + Duration::from_secs_f64(SpeechBubble::DEFAULT_DURATION);
342 Self {
343 content,
344 icon,
345 timeout,
346 }
347 }
348
349 pub fn content(&self) -> &Content { &self.content }
350}