diff --git a/src/engine/fox_beam.c b/src/engine/fox_beam.c
index d6751bcc..4f1a9af0 100644
--- a/src/engine/fox_beam.c
+++ b/src/engine/fox_beam.c
@@ -1,9 +1,11 @@
+#include <stdlib.h>
 #include "global.h"
 #include "assets/ast_landmaster.h"
 #include "assets/ast_aquas.h"
 #include "assets/ast_great_fox.h"
 #include "assets/ast_versus.h"
 #include "port/interpolation/FrameInterpolation.h"
+#include "port/hooks/Events.h"
 
 Vec3f sShotViewPos;
 
@@ -661,6 +663,10 @@ void PlayerShot_ApplyDamageToActor(PlayerShot* shot, Actor* actor, s32 hitIndex)
     Vec3f sp44 = { 0.0f, 0.0f, -100.0f };
     Vec3f sp38;
 
+    PlayerShotEvent* event = malloc(sizeof(PlayerShotEvent));
+    EventSystem_CallEvent(EVENT_PLAYER_SHOT, event);
+    free(event);
+
     actor->dmgType = DMG_BEAM;
     actor->dmgPart = hitIndex - 1;
     actor->timer_0C2 = 2;
diff --git a/src/port/Engine.cpp b/src/port/Engine.cpp
index 200eb46c..3feea094 100644
--- a/src/port/Engine.cpp
+++ b/src/port/Engine.cpp
@@ -36,20 +36,16 @@
 #include <VertexFactory.h>
 #include "audio/GameAudio.h"
 #include "port/patches/DisplayListPatch.h"
-// #include "sf64audio_provisional.h"
+
+#include "port/hooks/impl/EventSystem.h"
+#include "port/hooks/Events.h"
 
 #include <Fast3D/gfx_pc.h>
-#include <Fast3D/gfx_rendering_api.h>
 #include <SDL2/SDL.h>
-#include <fstream>
 #include <filesystem>
 
 namespace fs = std::filesystem;
 
-// extern "C" AudioBufferParameters gAudioBufferParams;
-
-#include <utility>
-
 extern "C" {
 float gInterpolationStep = 0.0f;
 #include <sf64thread.h>
@@ -188,6 +184,12 @@ void GameEngine::Create() {
 #if defined(__SWITCH__) || defined(__WIIU__)
     CVarRegisterInteger("gControlNav", 1); // always enable controller nav on switch/wii u
 #endif
+
+    EventSystem::Instance->RegisterListener(EVENT_PLAYER_SHOT, EVENT_PRIORITY_NORMAL, [](IEvent* event) {
+        auto pse = (PlayerShotEvent*) event;
+        SPDLOG_INFO("Player shot event fired for actor {}", pse->actorId);
+    });
+
 }
 
 void GameEngine::Destroy() {
diff --git a/src/port/hooks/Events.h b/src/port/hooks/Events.h
new file mode 100644
index 00000000..9c56592c
--- /dev/null
+++ b/src/port/hooks/Events.h
@@ -0,0 +1,3 @@
+#pragma once
+
+#include "list/PlayerShotEvent.h"
diff --git a/src/port/hooks/impl/EventSystem.cpp b/src/port/hooks/impl/EventSystem.cpp
new file mode 100644
index 00000000..5aa74bc0
--- /dev/null
+++ b/src/port/hooks/impl/EventSystem.cpp
@@ -0,0 +1,29 @@
+#include "EventSystem.h"
+
+EventSystem* EventSystem::Instance = new EventSystem();
+
+size_t EventSystem::RegisterListener(EventID id, EventPriority priority, EventCallback callback) {
+    if (std::find(this->mEventListeners[id].begin(), this->mEventListeners[id].end(), callback) != this->mEventListeners[id].end()) {
+        throw std::runtime_error("Listener already registered");
+    }
+
+    this->mEventListeners[id].push_back(callback);
+}
+
+void EventSystem::CallEvent(EventID id, IEvent* event) {
+    if (this->mEventListeners[id].empty()) {
+        return;
+    }
+
+    for (auto& callback : this->mEventListeners[id]) {
+        callback(event);
+    }
+}
+
+extern "C" size_t EventSystem_RegisterListener(EventID id, EventPriority priority, EventCallback callback) {
+    return EventSystem::Instance->RegisterListener(id, priority, callback);
+}
+
+extern "C" void EventSystem_CallEvent(EventID id, void* event) {
+    EventSystem::Instance->CallEvent(id, static_cast<IEvent*>(event));
+}
\ No newline at end of file
diff --git a/src/port/hooks/impl/EventSystem.h b/src/port/hooks/impl/EventSystem.h
new file mode 100644
index 00000000..3e6cf29b
--- /dev/null
+++ b/src/port/hooks/impl/EventSystem.h
@@ -0,0 +1,44 @@
+#pragma once
+
+#include <stdint.h>
+
+typedef uint16_t EventID;
+
+typedef enum EventType {
+    EVENT_TYPE_PRE,
+    EVENT_TYPE_NORMAL,
+    EVENT_TYPE_POST,
+} EventType;
+
+typedef enum EventPriority {
+    EVENT_PRIORITY_LOW,
+    EVENT_PRIORITY_NORMAL,
+    EVENT_PRIORITY_HIGH,
+} EventPriority;
+
+typedef struct IEvent {
+    bool cancelled;
+} IEvent;
+
+typedef void (*EventCallback)(IEvent*);
+
+// ID             Type
+// 00000000000000 00
+#define EVENT_ID(id, type) ((id << 2) | type)
+
+#ifdef __cplusplus
+#include <array>
+#include <vector>
+
+class EventSystem {
+public:
+    static EventSystem* Instance;
+    size_t RegisterListener(EventID id, EventPriority priority, EventCallback callback);
+    void CallEvent(EventID id, IEvent* event);
+private:
+    std::array<std::vector<EventCallback>, 0xFFFF> mEventListeners;
+};
+#else
+extern size_t EventSystem_RegisterListener(EventID id, EventPriority priority, EventCallback callback);
+extern void EventSystem_CallEvent(EventID id, void* event);
+#endif
\ No newline at end of file
diff --git a/src/port/hooks/list/PlayerShotEvent.h b/src/port/hooks/list/PlayerShotEvent.h
new file mode 100644
index 00000000..8a495861
--- /dev/null
+++ b/src/port/hooks/list/PlayerShotEvent.h
@@ -0,0 +1,10 @@
+#pragma once
+
+#include "port/hooks/impl/EventSystem.h"
+
+typedef struct {
+    IEvent event;
+    int actorId;
+} PlayerShotEvent;
+
+#define EVENT_PLAYER_SHOT EVENT_ID(1, EVENT_TYPE_NORMAL)
\ No newline at end of file