1use crate::{
2 Explosion,
3 character::CharacterId,
4 combat::{AttackSource, DeathEffects, RiderEffects},
5 comp::{
6 self, DisconnectReason, LootOwner, Ori, Pos, UnresolvedChatMsg, Vel,
7 agent::Sound,
8 invite::{InviteKind, InviteResponse},
9 slot::EquipSlot,
10 },
11 generation::{EntityInfo, SpecialEntity},
12 interaction::Interaction,
13 lottery::LootSpec,
14 mounting::VolumePos,
15 outcome::Outcome,
16 resources::{BattleMode, Secs},
17 rtsim::{self, RtSimEntity},
18 terrain::SpriteKind,
19 trade::{TradeAction, TradeId},
20 uid::Uid,
21 util::Dir,
22};
23use serde::{Deserialize, Serialize};
24use specs::Entity as EcsEntity;
25use std::{collections::VecDeque, sync::Mutex};
26use uuid::Uuid;
27use vek::*;
28
29pub type SiteId = u64;
30pub type PluginHash = [u8; 32];
32
33pub enum LocalEvent {
34 Jump(EcsEntity, f32),
36 ApplyImpulse {
38 entity: EcsEntity,
39 impulse: Vec3<f32>,
40 },
41 Boost { entity: EcsEntity, vel: Vec3<f32> },
43 CreateOutcome(Outcome),
45}
46
47#[derive(Clone, Debug, Default, Deserialize, Serialize)]
48pub struct UpdateCharacterMetadata {
49 pub skill_set_persistence_load_error: Option<comp::skillset::SkillsPersistenceError>,
50}
51
52pub struct NpcBuilder {
53 pub stats: comp::Stats,
54 pub skill_set: comp::SkillSet,
55 pub health: Option<comp::Health>,
56 pub poise: comp::Poise,
57 pub inventory: comp::inventory::Inventory,
58 pub body: comp::Body,
59 pub agent: Option<comp::Agent>,
60 pub alignment: comp::Alignment,
61 pub scale: comp::Scale,
62 pub anchor: Option<comp::Anchor>,
63 pub loot: LootSpec<String>,
64 pub pets: Vec<(NpcBuilder, Vec3<f32>)>,
65 pub rtsim_entity: Option<RtSimEntity>,
66 pub projectile: Option<comp::Projectile>,
67 pub heads: Option<comp::body::parts::Heads>,
68 pub death_effects: Option<DeathEffects>,
69 pub rider_effects: Option<RiderEffects>,
70 pub rider: Option<Box<Self>>,
71}
72
73impl NpcBuilder {
74 pub fn new(stats: comp::Stats, body: comp::Body, alignment: comp::Alignment) -> Self {
75 Self {
76 stats,
77 skill_set: comp::SkillSet::default(),
78 health: None,
79 poise: comp::Poise::new(body),
80 inventory: comp::Inventory::with_empty(),
81 body,
82 agent: None,
83 alignment,
84 scale: comp::Scale(1.0),
85 anchor: None,
86 loot: LootSpec::Nothing,
87 rtsim_entity: None,
88 projectile: None,
89 pets: Vec::new(),
90 heads: None,
91 death_effects: None,
92 rider_effects: None,
93 rider: None,
94 }
95 }
96
97 pub fn with_rider(mut self, rider: impl Into<Option<NpcBuilder>>) -> Self {
98 let rider: Option<NpcBuilder> = rider.into();
99 self.rider = rider.map(Box::new);
100 self
101 }
102
103 pub fn with_heads(mut self, heads: impl Into<Option<comp::body::parts::Heads>>) -> Self {
104 self.heads = heads.into();
105 self
106 }
107
108 pub fn with_health(mut self, health: impl Into<Option<comp::Health>>) -> Self {
109 self.health = health.into();
110 self
111 }
112
113 pub fn with_poise(mut self, poise: comp::Poise) -> Self {
114 self.poise = poise;
115 self
116 }
117
118 pub fn with_agent(mut self, agent: impl Into<Option<comp::Agent>>) -> Self {
119 self.agent = agent.into();
120 self
121 }
122
123 pub fn with_anchor(mut self, anchor: comp::Anchor) -> Self {
124 self.anchor = Some(anchor);
125 self
126 }
127
128 pub fn with_rtsim(mut self, rtsim: RtSimEntity) -> Self {
129 self.rtsim_entity = Some(rtsim);
130 self
131 }
132
133 pub fn with_projectile(mut self, projectile: impl Into<Option<comp::Projectile>>) -> Self {
134 self.projectile = projectile.into();
135 self
136 }
137
138 pub fn with_scale(mut self, scale: comp::Scale) -> Self {
139 self.scale = scale;
140 self
141 }
142
143 pub fn with_inventory(mut self, inventory: comp::Inventory) -> Self {
144 self.inventory = inventory;
145 self
146 }
147
148 pub fn with_skill_set(mut self, skill_set: comp::SkillSet) -> Self {
149 self.skill_set = skill_set;
150 self
151 }
152
153 pub fn with_loot(mut self, loot: LootSpec<String>) -> Self {
154 self.loot = loot;
155 self
156 }
157
158 pub fn with_pets(mut self, pets: Vec<(NpcBuilder, Vec3<f32>)>) -> Self {
159 self.pets = pets;
160 self
161 }
162
163 pub fn with_death_effects(mut self, death_effects: Option<DeathEffects>) -> Self {
164 self.death_effects = death_effects;
165 self
166 }
167
168 pub fn with_rider_effects(mut self, rider_effects: Option<RiderEffects>) -> Self {
169 self.rider_effects = rider_effects;
170 self
171 }
172}
173
174pub struct ClientDisconnectEvent(pub EcsEntity, pub DisconnectReason);
182
183pub struct ClientDisconnectWithoutPersistenceEvent(pub EcsEntity);
184
185pub struct CommandEvent(pub EcsEntity, pub String, pub Vec<String>);
186
187pub struct CreateSpecialEntityEvent {
188 pub pos: Vec3<f32>,
189 pub entity: SpecialEntity,
190}
191
192pub struct CreateShipEvent {
193 pub pos: Pos,
194 pub ori: Ori,
195 pub ship: comp::ship::Body,
196 pub rtsim_entity: Option<RtSimEntity>,
197 pub driver: Option<NpcBuilder>,
198}
199
200pub struct CreateItemDropEvent {
201 pub pos: Pos,
202 pub vel: Vel,
203 pub ori: Ori,
204 pub item: comp::PickupItem,
205 pub loot_owner: Option<LootOwner>,
206}
207pub struct CreateObjectEvent {
208 pub pos: Pos,
209 pub vel: Vel,
210 pub body: comp::object::Body,
211 pub object: Option<comp::Object>,
212 pub item: Option<comp::PickupItem>,
213 pub light_emitter: Option<comp::LightEmitter>,
214 pub stats: Option<comp::Stats>,
215}
216
217pub struct InitializeCharacterEvent {
219 pub entity: EcsEntity,
220 pub character_id: CharacterId,
221 pub requested_view_distances: crate::ViewDistances,
222}
223
224pub struct InitializeSpectatorEvent(pub EcsEntity, pub crate::ViewDistances);
225
226pub struct UpdateCharacterDataEvent {
227 pub entity: EcsEntity,
228 pub components: (
229 comp::Body,
230 Option<comp::Hardcore>,
231 comp::Stats,
232 comp::SkillSet,
233 comp::Inventory,
234 Option<comp::Waypoint>,
235 Vec<(comp::Pet, comp::Body, comp::Stats)>,
236 comp::ActiveAbilities,
237 Option<comp::MapMarker>,
238 ),
239 pub metadata: UpdateCharacterMetadata,
240}
241
242pub struct ExitIngameEvent {
243 pub entity: EcsEntity,
244}
245
246pub struct RequestSiteInfoEvent {
247 pub entity: EcsEntity,
248 pub id: SiteId,
249}
250
251pub struct TamePetEvent {
252 pub pet_entity: EcsEntity,
253 pub owner_entity: EcsEntity,
254}
255
256pub struct UpdateMapMarkerEvent {
257 pub entity: EcsEntity,
258 pub update: comp::MapMarkerChange,
259}
260
261pub struct MakeAdminEvent {
262 pub entity: EcsEntity,
263 pub admin: comp::Admin,
264 pub uuid: Uuid,
265}
266
267pub struct DeleteCharacterEvent {
268 pub entity: EcsEntity,
269 pub requesting_player_uuid: String,
270 pub character_id: CharacterId,
271}
272
273pub struct TeleportToPositionEvent {
274 pub entity: EcsEntity,
275 pub position: Vec3<f32>,
276}
277
278pub struct RequestPluginsEvent {
279 pub entity: EcsEntity,
280 pub plugins: Vec<PluginHash>,
281}
282
283pub struct SetBattleModeEvent {
284 pub entity: EcsEntity,
285 pub battle_mode: BattleMode,
286}
287
288pub struct ChatEvent {
293 pub msg: UnresolvedChatMsg,
294 pub from_client: bool,
299}
300
301pub struct CreateNpcEvent {
302 pub pos: Pos,
303 pub ori: Ori,
304 pub npc: NpcBuilder,
305}
306
307pub struct CreateAuraEntityEvent {
308 pub auras: comp::Auras,
309 pub pos: Pos,
310 pub creator_uid: Uid,
311 pub duration: Option<Secs>,
312}
313
314pub struct ExplosionEvent {
315 pub pos: Vec3<f32>,
316 pub explosion: Explosion,
317 pub owner: Option<Uid>,
318}
319
320pub struct BonkEvent {
321 pub pos: Vec3<f32>,
322 pub owner: Option<Uid>,
323 pub target: Option<Uid>,
324}
325
326pub struct HealthChangeEvent {
327 pub entity: EcsEntity,
328 pub change: comp::HealthChange,
329}
330
331pub struct KillEvent {
332 pub entity: EcsEntity,
333}
334
335pub struct HelpDownedEvent {
336 pub helper: Option<Uid>,
337 pub target: Uid,
338}
339
340pub struct DownedEvent {
341 pub entity: EcsEntity,
342}
343
344pub struct PoiseChangeEvent {
345 pub entity: EcsEntity,
346 pub change: comp::PoiseChange,
347}
348
349pub struct DeleteEvent(pub EcsEntity);
350
351pub struct DestroyEvent {
352 pub entity: EcsEntity,
353 pub cause: comp::HealthChange,
354}
355
356pub struct InventoryManipEvent(pub EcsEntity, pub comp::InventoryManip);
357
358pub struct GroupManipEvent(pub EcsEntity, pub comp::GroupManip);
359
360pub struct RespawnEvent(pub EcsEntity);
361
362pub struct ShootEvent {
363 pub entity: Option<EcsEntity>,
364 pub pos: Pos,
365 pub dir: Dir,
366 pub body: comp::Body,
367 pub light: Option<comp::LightEmitter>,
368 pub projectile: comp::Projectile,
369 pub speed: f32,
370 pub object: Option<comp::Object>,
371}
372
373pub struct ThrowEvent {
374 pub entity: EcsEntity,
375 pub pos: Pos,
376 pub dir: Dir,
377 pub light: Option<comp::LightEmitter>,
378 pub projectile: comp::Projectile,
379 pub speed: f32,
380 pub object: Option<comp::Object>,
381 pub equip_slot: EquipSlot,
382}
383
384pub struct ShockwaveEvent {
385 pub properties: comp::shockwave::Properties,
386 pub pos: Pos,
387 pub ori: Ori,
388}
389
390pub struct KnockbackEvent {
391 pub entity: EcsEntity,
392 pub impulse: Vec3<f32>,
393}
394
395pub struct LandOnGroundEvent {
396 pub entity: EcsEntity,
397 pub vel: Vec3<f32>,
398 pub surface_normal: Vec3<f32>,
399}
400
401pub struct SetLanternEvent(pub EcsEntity, pub bool);
402
403pub struct NpcInteractEvent(pub EcsEntity, pub EcsEntity);
404
405pub struct DialogueEvent(pub EcsEntity, pub EcsEntity, pub rtsim::Dialogue);
406
407pub struct InviteResponseEvent(pub EcsEntity, pub InviteResponse);
408
409pub struct InitiateInviteEvent(pub EcsEntity, pub Uid, pub InviteKind);
410
411pub struct ProcessTradeActionEvent(pub EcsEntity, pub TradeId, pub TradeAction);
412
413pub enum MountEvent {
414 MountEntity(EcsEntity, EcsEntity),
415 MountVolume(EcsEntity, VolumePos),
416 Unmount(EcsEntity),
417}
418
419pub struct SetPetStayEvent(pub EcsEntity, pub EcsEntity, pub bool);
420
421pub struct PossessEvent(pub Uid, pub Uid);
422
423pub struct TransformEvent {
424 pub target_entity: Uid,
425 pub entity_info: EntityInfo,
426 pub allow_players: bool,
429 pub delete_on_failure: bool,
432}
433
434pub struct StartInteractionEvent(pub Interaction);
435
436pub struct AuraEvent {
437 pub entity: EcsEntity,
438 pub aura_change: comp::AuraChange,
439}
440
441pub struct BuffEvent {
442 pub entity: EcsEntity,
443 pub buff_change: comp::BuffChange,
444}
445
446pub struct EnergyChangeEvent {
447 pub entity: EcsEntity,
448 pub change: f32,
449 pub reset_rate: bool,
450}
451
452pub struct ComboChangeEvent {
453 pub entity: EcsEntity,
454 pub change: i32,
455}
456
457pub struct ParryHookEvent {
458 pub defender: EcsEntity,
459 pub attacker: Option<EcsEntity>,
460 pub source: AttackSource,
461 pub poise_multiplier: f32,
462}
463
464pub struct MineBlockEvent {
466 pub entity: EcsEntity,
467 pub pos: Vec3<i32>,
468 pub tool: Option<comp::tool::ToolKind>,
469}
470
471pub struct TeleportToEvent {
472 pub entity: EcsEntity,
473 pub target: Uid,
474 pub max_range: Option<f32>,
475}
476
477pub struct SoundEvent {
478 pub sound: Sound,
479}
480
481pub struct CreateSpriteEvent {
482 pub pos: Vec3<i32>,
483 pub sprite: SpriteKind,
484 pub del_timeout: Option<(f32, f32)>,
485}
486
487pub struct EntityAttackedHookEvent {
488 pub entity: EcsEntity,
489 pub attacker: Option<EcsEntity>,
490}
491
492pub struct ChangeAbilityEvent {
493 pub entity: EcsEntity,
494 pub slot: usize,
495 pub auxiliary_key: comp::ability::AuxiliaryKey,
496 pub new_ability: comp::ability::AuxiliaryAbility,
497}
498
499pub struct ChangeStanceEvent {
500 pub entity: EcsEntity,
501 pub stance: comp::Stance,
502}
503
504pub struct ChangeBodyEvent {
505 pub entity: EcsEntity,
506 pub new_body: comp::Body,
507}
508
509pub struct RemoveLightEmitterEvent {
510 pub entity: EcsEntity,
511}
512
513pub struct StartTeleportingEvent {
514 pub entity: EcsEntity,
515 pub portal: EcsEntity,
516}
517
518pub struct ToggleSpriteLightEvent {
519 pub entity: EcsEntity,
520 pub pos: Vec3<i32>,
521 pub enable: bool,
522}
523
524pub struct RegrowHeadEvent {
525 pub entity: EcsEntity,
526}
527
528struct EventBusInner<E> {
529 queue: VecDeque<E>,
530 #[cfg(debug_assertions)]
535 recv_count: u8,
536}
537
538pub struct EventBus<E> {
539 inner: Mutex<EventBusInner<E>>,
540}
541
542impl<E> Default for EventBus<E> {
543 fn default() -> Self {
544 Self {
545 inner: Mutex::new(EventBusInner {
546 queue: VecDeque::new(),
547 #[cfg(debug_assertions)]
548 recv_count: 0,
549 }),
550 }
551 }
552}
553
554impl<E> EventBus<E> {
555 pub fn emitter(&self) -> Emitter<E> {
556 Emitter {
557 bus: self,
558 events: VecDeque::new(),
559 }
560 }
561
562 pub fn emit_now(&self, event: E) {
563 self.inner.lock().expect("Poisoned").queue.push_back(event);
564 }
565
566 pub fn recv_all(&self) -> impl ExactSizeIterator<Item = E> + use<E> {
567 {
568 let mut guard = self.inner.lock().expect("Poisoned");
569 #[cfg(debug_assertions)]
570 {
571 guard.recv_count = guard.recv_count.saturating_add(1);
572 }
573 core::mem::take(&mut guard.queue)
574 }
575 .into_iter()
576 }
577
578 pub fn recv_all_mut(&mut self) -> impl ExactSizeIterator<Item = E> + use<E> {
579 let inner = self.inner.get_mut().expect("Poisoned");
580 #[cfg(debug_assertions)]
581 {
582 inner.recv_count = inner.recv_count.saturating_add(1);
583 }
584 core::mem::take(&mut inner.queue).into_iter()
585 }
586
587 #[cfg(debug_assertions)]
588 pub fn recv_count(&mut self) -> u8 { self.inner.get_mut().expect("Poisoned").recv_count }
589}
590
591pub struct Emitter<'a, E> {
592 bus: &'a EventBus<E>,
593 pub events: VecDeque<E>,
594}
595
596impl<E> Emitter<'_, E> {
597 pub fn emit(&mut self, event: E) { self.events.push_back(event); }
598
599 pub fn emit_many(&mut self, events: impl IntoIterator<Item = E>) { self.events.extend(events); }
600
601 pub fn append(&mut self, other: &mut VecDeque<E>) { self.events.append(other) }
602
603 pub fn append_vec(&mut self, vec: Vec<E>) {
604 if self.events.is_empty() {
605 self.events = vec.into();
606 } else {
607 self.events.extend(vec);
608 }
609 }
610}
611
612impl<E> Drop for Emitter<'_, E> {
613 fn drop(&mut self) {
614 if !self.events.is_empty() {
615 let mut guard = self.bus.inner.lock().expect("Poision");
616 guard.queue.append(&mut self.events);
617 }
618 }
619}
620
621pub trait EmitExt<E> {
622 fn emit(&mut self, event: E);
623 fn emit_many(&mut self, events: impl IntoIterator<Item = E>);
624}
625
626#[macro_export]
644macro_rules! event_emitters {
645 ($($vis:vis struct $read_data:ident[$emitters:ident] { $($ev_ident:ident: $ty:ty),+ $(,)? })+) => {
646 mod event_emitters {
647 use super::*;
648 use specs::shred;
649 $(
650 #[derive(specs::SystemData)]
651 pub struct $read_data<'a> {
652 $($ev_ident: Option<specs::Read<'a, $crate::event::EventBus<$ty>>>),+
653 }
654
655 impl<'a> $read_data<'a> {
656 pub fn get_emitters(&self) -> $emitters {
657 $emitters {
658 $($ev_ident: self.$ev_ident.as_ref().map(|e| e.emitter())),+
659 }
660 }
661 }
662
663 pub struct $emitters<'a> {
664 $($ev_ident: Option<$crate::event::Emitter<'a, $ty>>),+
665 }
666
667 impl<'a> $emitters<'a> {
668 #[expect(unused)]
669 pub fn append(&mut self, mut other: Self) {
670 $(
671 self.$ev_ident.as_mut().zip(other.$ev_ident).map(|(a, mut b)| a.append(&mut b.events));
672 )+
673 }
674 }
675
676 $(
677 impl<'a> $crate::event::EmitExt<$ty> for $emitters<'a> {
678 fn emit(&mut self, event: $ty) { self.$ev_ident.as_mut().map(|e| e.emit(event)); }
679 fn emit_many(&mut self, events: impl IntoIterator<Item = $ty>) { self.$ev_ident.as_mut().map(|e| e.emit_many(events)); }
680 }
681 )+
682 )+
683 }
684 $(
685 $vis use event_emitters::{$read_data, $emitters};
686 )+
687 }
688}