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