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(Content, 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 is_player_msg(&self) -> bool {
135 matches!(
136 self,
137 Self::Tell(_, _)
138 | Self::Say(_)
139 | Self::Group(_, _)
140 | Self::Faction(_, _)
141 | Self::Region(_)
142 | Self::World(_)
143 )
144 }
145
146 pub fn into_plain_msg(self, text: impl ToString) -> GenericChatMsg<G> {
147 GenericChatMsg {
148 chat_type: self,
149 content: Content::Plain(text.to_string()),
150 }
151 }
152
153 pub fn into_msg(self, content: Content) -> GenericChatMsg<G> {
154 GenericChatMsg {
155 chat_type: self,
156 content,
157 }
158 }
159
160 pub fn uid(&self) -> Option<Uid> {
162 match self {
163 ChatType::Online(_) => None,
164 ChatType::Offline(_) => None,
165 ChatType::CommandInfo => None,
166 ChatType::CommandError => None,
167 ChatType::FactionMeta(_) => None,
168 ChatType::GroupMeta(_) => None,
169 ChatType::Kill(_, _) => None,
170 ChatType::Tell(u, _t) => Some(*u),
171 ChatType::Say(u) => Some(*u),
172 ChatType::Group(u, _s) => Some(*u),
173 ChatType::Faction(u, _s) => Some(*u),
174 ChatType::Region(u) => Some(*u),
175 ChatType::World(u) => Some(*u),
176 ChatType::Npc(u) => Some(*u),
177 ChatType::NpcSay(u) => Some(*u),
178 ChatType::NpcTell(u, _t) => Some(*u),
179 ChatType::Meta => None,
180 }
181 }
182
183 pub fn is_private(&self) -> Option<bool> {
185 match self {
186 ChatType::Online(_)
187 | ChatType::Offline(_)
188 | ChatType::CommandInfo
189 | ChatType::CommandError
190 | ChatType::FactionMeta(_)
191 | ChatType::GroupMeta(_)
192 | ChatType::Npc(_)
193 | ChatType::NpcSay(_)
194 | ChatType::NpcTell(_, _)
195 | ChatType::Meta
196 | ChatType::Kill(_, _) => None,
197 ChatType::Tell(_, _) | ChatType::Group(_, _) | ChatType::Faction(_, _) => Some(true),
198 ChatType::Say(_) | ChatType::Region(_) | ChatType::World(_) => Some(false),
199 }
200 }
201}
202
203#[derive(Debug, Clone, Serialize, Deserialize)]
205pub struct GenericChatMsg<G> {
206 pub chat_type: ChatType<G>,
207 content: Content,
208}
209
210pub type ChatMsg = GenericChatMsg<String>;
211pub type UnresolvedChatMsg = GenericChatMsg<Group>;
212
213impl<G> GenericChatMsg<G> {
214 pub const MAX_BYTES_PLAYER_CHAT_MSG: usize = 256;
215 pub const NPC_DISTANCE: f32 = 100.0;
216 pub const NPC_SAY_DISTANCE: f32 = 30.0;
217 pub const REGION_DISTANCE: f32 = 1000.0;
218 pub const SAY_DISTANCE: f32 = 100.0;
219
220 pub fn npc(uid: Uid, content: Content) -> Self {
221 let chat_type = ChatType::Npc(uid);
222 Self { chat_type, content }
223 }
224
225 pub fn npc_say(uid: Uid, content: Content) -> Self {
226 let chat_type = ChatType::NpcSay(uid);
227 Self { chat_type, content }
228 }
229
230 pub fn npc_tell(from: Uid, to: Uid, content: Content) -> Self {
231 let chat_type = ChatType::NpcTell(from, to);
232 Self { chat_type, content }
233 }
234
235 pub fn death(kill_source: KillSource, victim: Uid) -> Self {
236 Self {
237 chat_type: ChatType::Kill(kill_source, victim),
238 content: Content::Plain(String::new()),
239 }
240 }
241
242 pub fn map_group<T>(self, mut f: impl FnMut(G) -> T) -> GenericChatMsg<T> {
243 let chat_type = match self.chat_type {
244 ChatType::Online(a) => ChatType::Online(a),
245 ChatType::Offline(a) => ChatType::Offline(a),
246 ChatType::CommandInfo => ChatType::CommandInfo,
247 ChatType::CommandError => ChatType::CommandError,
248 ChatType::FactionMeta(a) => ChatType::FactionMeta(a),
249 ChatType::GroupMeta(g) => ChatType::GroupMeta(f(g)),
250 ChatType::Kill(a, b) => ChatType::Kill(a, b),
251 ChatType::Tell(a, b) => ChatType::Tell(a, b),
252 ChatType::Say(a) => ChatType::Say(a),
253 ChatType::Group(a, g) => ChatType::Group(a, f(g)),
254 ChatType::Faction(a, b) => ChatType::Faction(a, b),
255 ChatType::Region(a) => ChatType::Region(a),
256 ChatType::World(a) => ChatType::World(a),
257 ChatType::Npc(a) => ChatType::Npc(a),
258 ChatType::NpcSay(a) => ChatType::NpcSay(a),
259 ChatType::NpcTell(a, b) => ChatType::NpcTell(a, b),
260 ChatType::Meta => ChatType::Meta,
261 };
262
263 GenericChatMsg {
264 chat_type,
265 content: self.content,
266 }
267 }
268
269 pub fn get_group(&self) -> Option<&G> {
270 match &self.chat_type {
271 ChatType::GroupMeta(g) => Some(g),
272 ChatType::Group(_, g) => Some(g),
273 _ => None,
274 }
275 }
276
277 pub fn to_bubble(&self) -> Option<(SpeechBubble, Uid)> {
278 self.uid()
279 .map(|from| (SpeechBubble::new(self.content.clone(), self.icon()), from))
280 }
281
282 pub fn icon(&self) -> SpeechBubbleType {
283 match &self.chat_type {
284 ChatType::Online(_) => SpeechBubbleType::None,
285 ChatType::Offline(_) => SpeechBubbleType::None,
286 ChatType::CommandInfo => SpeechBubbleType::None,
287 ChatType::CommandError => SpeechBubbleType::None,
288 ChatType::FactionMeta(_) => SpeechBubbleType::None,
289 ChatType::GroupMeta(_) => SpeechBubbleType::None,
290 ChatType::Kill(_, _) => SpeechBubbleType::None,
291 ChatType::Tell(_u, _) => SpeechBubbleType::Tell,
292 ChatType::Say(_u) => SpeechBubbleType::Say,
293 ChatType::Group(_u, _s) => SpeechBubbleType::Group,
294 ChatType::Faction(_u, _s) => SpeechBubbleType::Faction,
295 ChatType::Region(_u) => SpeechBubbleType::Region,
296 ChatType::World(_u) => SpeechBubbleType::World,
297 ChatType::Npc(_u) => SpeechBubbleType::None,
298 ChatType::NpcSay(_u) => SpeechBubbleType::Say,
299 ChatType::NpcTell(_f, _t) => SpeechBubbleType::Say,
300 ChatType::Meta => SpeechBubbleType::None,
301 }
302 }
303
304 pub fn uid(&self) -> Option<Uid> { self.chat_type.uid() }
306
307 pub fn content(&self) -> &Content { &self.content }
308
309 pub fn into_content(self) -> Content { self.content }
310
311 pub fn set_content(&mut self, content: Content) { self.content = content; }
312}
313
314#[derive(Clone, Debug)]
319pub struct Faction(pub String);
320impl Component for Faction {
321 type Storage = DenseVecStorage<Self>;
322}
323impl From<String> for Faction {
324 fn from(s: String) -> Self { Faction(s) }
325}
326
327pub enum SpeechBubbleType {
331 Tell,
333 Say,
334 Region,
335 Group,
336 Faction,
337 World,
338 Quest, Trade, None, }
343
344pub struct SpeechBubble {
346 pub content: Content,
347 pub icon: SpeechBubbleType,
348 pub timeout: Instant,
349}
350
351impl SpeechBubble {
352 pub const DEFAULT_DURATION: f64 = 5.0;
354
355 pub fn new(content: Content, icon: SpeechBubbleType) -> Self {
356 let timeout = Instant::now() + Duration::from_secs_f64(SpeechBubble::DEFAULT_DURATION);
357 Self {
358 content,
359 icon,
360 timeout,
361 }
362 }
363
364 pub fn content(&self) -> &Content { &self.content }
365}