diff --git a/src/field/arena-events.ts b/src/field/arena-events.ts
new file mode 100644
index 00000000000..1cc632030a5
--- /dev/null
+++ b/src/field/arena-events.ts
@@ -0,0 +1,77 @@
+import { ArenaTagSide } from "#app/data/arena-tag.js";
+import { ArenaTagType } from "#app/data/enums/arena-tag-type.js";
+import { TerrainType } from "#app/data/terrain.js";
+import { WeatherType } from "#app/data/weather.js";
+
+/** Alias for all {@linkcode ArenaEvent} type strings */
+export enum ArenaEventType {
+  /** Triggers when a {@linkcode WeatherType} is added, overlapped, or removed */
+  WEATHER_CHANGED = "onWeatherChanged",
+  /** Triggers when a {@linkcode TerrainType} is added, overlapped, or removed */
+  TERRAIN_CHANGED = "onTerrainChanged",
+
+  /** Triggers when a {@linkcode ArenaTagType} is added or removed */
+  TAG_CHANGED = "onTagChanged",
+}
+
+/**
+ * Base container class for all {@linkcode ArenaEventType} events
+ * @extends Event
+ */
+export class ArenaEvent extends Event {
+  /** The total duration of the {@linkcode ArenaEventType} */
+  public duration: number;
+  constructor(eventType: ArenaEventType, duration: number) {
+    super(eventType);
+
+    this.duration = duration;
+  }
+}
+/**
+ * Container class for {@linkcode ArenaEventType.WEATHER_CHANGED} events
+ * @extends ArenaEvent
+*/
+export class WeatherChangedEvent extends ArenaEvent {
+  /** The {@linkcode WeatherType} being overridden */
+  public oldWeatherType: WeatherType;
+  /** The {@linkcode WeatherType} being set */
+  public newWeatherType: WeatherType;
+  constructor(oldWeatherType: WeatherType, newWeatherType: WeatherType, duration: number) {
+    super(ArenaEventType.WEATHER_CHANGED, duration);
+
+    this.oldWeatherType = oldWeatherType;
+    this.newWeatherType = newWeatherType;
+  }
+}
+/**
+ * Container class for {@linkcode ArenaEventType.TERRAIN_CHANGED} events
+ * @extends ArenaEvent
+*/
+export class TerrainChangedEvent extends ArenaEvent {
+  /** The {@linkcode TerrainType} being overridden */
+  public oldTerrainType: TerrainType;
+  /** The {@linkcode TerrainType} being set */
+  public newTerrainType: TerrainType;
+  constructor(oldTerrainType: TerrainType, newTerrainType: TerrainType, duration: number) {
+    super(ArenaEventType.TERRAIN_CHANGED, duration);
+
+    this.oldTerrainType = oldTerrainType;
+    this.newTerrainType = newTerrainType;
+  }
+}
+/**
+ * Container class for {@linkcode ArenaEventType.TAG_CHANGED} events
+ * @extends ArenaEvent
+*/
+export class TagChangedEvent extends ArenaEvent {
+  /** The {@linkcode ArenaTagType} being set */
+  public arenaTagType: ArenaTagType;
+  /** The {@linkcode ArenaTagSide} the tag is being placed on */
+  public arenaTagSide: ArenaTagSide;
+  constructor(arenaTagType: ArenaTagType, arenaTagSide: ArenaTagSide, duration: number) {
+    super(ArenaEventType.TAG_CHANGED, duration);
+
+    this.arenaTagType = arenaTagType;
+    this.arenaTagSide = arenaTagSide;
+  }
+}
diff --git a/src/field/arena.ts b/src/field/arena.ts
index 817e5d5eaad..eac2eafb265 100644
--- a/src/field/arena.ts
+++ b/src/field/arena.ts
@@ -19,6 +19,7 @@ import { Terrain, TerrainType } from "../data/terrain";
 import { PostTerrainChangeAbAttr, PostWeatherChangeAbAttr, applyPostTerrainChangeAbAttrs, applyPostWeatherChangeAbAttrs } from "../data/ability";
 import Pokemon from "./pokemon";
 import * as Overrides from "../overrides";
+import { WeatherChangedEvent, TerrainChangedEvent, TagChangedEvent } from "./arena-events";
 
 export class Arena {
   public scene: BattleScene;
@@ -34,6 +35,8 @@ export class Arena {
   private pokemonPool: PokemonPools;
   private trainerPool: BiomeTierTrainerPools;
 
+  public readonly eventTarget: EventTarget = new EventTarget();
+
   constructor(scene: BattleScene, biome: Biome, bgm: string) {
     this.scene = scene;
     this.biomeType = biome;
@@ -300,6 +303,7 @@ export class Arena {
     const oldWeatherType = this.weather?.weatherType || WeatherType.NONE;
 
     this.weather = weather ? new Weather(weather, hasPokemonSource ? 5 : 0) : null;
+    this.eventTarget.dispatchEvent(new WeatherChangedEvent(oldWeatherType, this.weather?.weatherType, this.weather?.turnsLeft));
 
     if (this.weather) {
       this.scene.unshiftPhase(new CommonAnimPhase(this.scene, undefined, undefined, CommonAnim.SUNNY + (weather - 1)));
@@ -324,6 +328,7 @@ export class Arena {
     const oldTerrainType = this.terrain?.terrainType || TerrainType.NONE;
 
     this.terrain = terrain ? new Terrain(terrain, hasPokemonSource ? 5 : 0) : null;
+    this.eventTarget.dispatchEvent(new TerrainChangedEvent(oldTerrainType,this.terrain?.terrainType, this.terrain?.turnsLeft));
 
     if (this.terrain) {
       if (!ignoreAnim) {
@@ -545,6 +550,8 @@ export class Arena {
     this.tags.push(newTag);
     newTag.onAdd(this);
 
+    this.eventTarget.dispatchEvent(new TagChangedEvent(newTag.tagType, newTag.side, newTag.turnCount));
+
     return true;
   }
 
diff --git a/src/system/game-data.ts b/src/system/game-data.ts
index 5bc0df19aa2..b1cbf77f4c6 100644
--- a/src/system/game-data.ts
+++ b/src/system/game-data.ts
@@ -30,6 +30,7 @@ import { allMoves } from "../data/move";
 import { TrainerVariant } from "../field/trainer";
 import { OutdatedPhase, ReloadSessionPhase } from "#app/phases";
 import { Variant, variantData } from "#app/data/variant";
+import { TerrainChangedEvent, WeatherChangedEvent } from "#app/field/arena-events.js";
 
 const saveKey = "x0i2O7WRiANTqPmZ"; // Temporary; secure encryption is not yet necessary
 
@@ -740,6 +741,10 @@ export class GameData {
           });
 
           scene.arena.weather = sessionData.arena.weather;
+          scene.arena.eventTarget.dispatchEvent(new WeatherChangedEvent(null, scene.arena.weather?.weatherType, scene.arena.weather?.turnsLeft));
+
+          scene.arena.terrain = sessionData.arena.terrain;
+          scene.arena.eventTarget.dispatchEvent(new TerrainChangedEvent(null, scene.arena.terrain?.terrainType, scene.arena.terrain?.turnsLeft));
           // TODO
           //scene.arena.tags = sessionData.arena.tags;