diff --git a/public/audio/se/low_hp.wav b/public/audio/se/low_hp.wav
new file mode 100644
index 00000000000..cefc70c5ea4
Binary files /dev/null and b/public/audio/se/low_hp.wav differ
diff --git a/public/audio/se/upgrade.wav b/public/audio/se/upgrade.wav
new file mode 100644
index 00000000000..b04f5e6f754
Binary files /dev/null and b/public/audio/se/upgrade.wav differ
diff --git a/src/auto-play.ts b/src/auto-play.ts
index beeee150484..66059816616 100644
--- a/src/auto-play.ts
+++ b/src/auto-play.ts
@@ -1,6 +1,6 @@
 import { SelectModifierPhase } from "./battle-phases";
 import BattleScene, { Button } from "./battle-scene";
-import { ModifierTier, ModifierType, PokemonBaseStatBoosterModifierType, PokemonHpRestoreModifierType, PokemonReviveModifierType } from "./modifier-type";
+import { ModifierTier, ModifierType, ModifierTypeOption, PokemonBaseStatBoosterModifierType, PokemonHpRestoreModifierType, PokemonReviveModifierType } from "./modifier-type";
 import Pokemon, { AiType, EnemyPokemon, PlayerPokemon, PokemonMove } from "./pokemon";
 import { Species } from "./species";
 import { getTypeDamageMultiplier } from "./type";
@@ -169,10 +169,10 @@ export function initAutoPlay() {
         }
     }
 
-    const tryGetBestModifier = (modifierTypes: Array<ModifierType>, predicate: Function) => {
-        for (let mt = 0; mt < modifierTypes.length; mt++) {
-            const modifierType = modifierTypes[mt];
-            if (predicate(modifierType)) {
+    const tryGetBestModifier = (modifierTypeOptions: Array<ModifierTypeOption>, predicate: Function) => {
+        for (let mt = 0; mt < modifierTypeOptions.length; mt++) {
+            const modifierTypeOption = modifierTypeOptions[mt];
+            if (predicate(modifierTypeOption.type)) {
                 return mt;
             }
         }
@@ -194,12 +194,12 @@ export function initAutoPlay() {
             originalModifierSelectUiHandlerShow.apply(this, [ args ]);
 
             const party = thisArg.getParty();
-            const modifierTypes = modifierSelectUiHandler.options.map(o => o.modifierType);
+            const modifierTypeOptions = modifierSelectUiHandler.options.map(o => o.modifierTypeOption);
             const faintedPartyMemberIndex = party.findIndex(p => !p.hp);
             const lowHpPartyMemberIndex = party.findIndex(p => p.getHpRatio() <= 0.5);
             const criticalHpPartyMemberIndex = party.findIndex(p => p.getHpRatio() <= 0.25);
 
-            let optionIndex = tryGetBestModifier(modifierTypes, (modifierType: ModifierType) => {
+            let optionIndex = tryGetBestModifier(modifierTypeOptions, (modifierType: ModifierType) => {
                 if (modifierType instanceof PokemonHpRestoreModifierType) {
                     if (modifierType instanceof PokemonReviveModifierType) {
                         if (faintedPartyMemberIndex > -1) {
@@ -214,7 +214,7 @@ export function initAutoPlay() {
             });
 
             if (optionIndex === -1) {
-                optionIndex = tryGetBestModifier(modifierTypes, (modifierType: ModifierType) => {
+                optionIndex = tryGetBestModifier(modifierTypeOptions, (modifierType: ModifierType) => {
                     if (modifierType.tier >= ModifierTier.ULTRA) {
                         nextPartyMemberIndex = 0;
                         return true;
@@ -223,7 +223,7 @@ export function initAutoPlay() {
             }
 
             if (optionIndex === -1) {
-                optionIndex = tryGetBestModifier(modifierTypes, (modifierType: ModifierType) => {
+                optionIndex = tryGetBestModifier(modifierTypeOptions, (modifierType: ModifierType) => {
                     if (modifierType instanceof PokemonBaseStatBoosterModifierType) {
                         nextPartyMemberIndex = 0;
                         return true;
@@ -232,7 +232,7 @@ export function initAutoPlay() {
             }
 
             if (optionIndex === -1) {
-                optionIndex = tryGetBestModifier(modifierTypes, (modifierType: ModifierType) => {
+                optionIndex = tryGetBestModifier(modifierTypeOptions, (modifierType: ModifierType) => {
                     if (lowHpPartyMemberIndex && modifierType instanceof PokemonHpRestoreModifierType && !(ModifierType instanceof PokemonReviveModifierType)) {
                         nextPartyMemberIndex = lowHpPartyMemberIndex;
                         return true;
@@ -248,7 +248,7 @@ export function initAutoPlay() {
                 thisArg.time.delayedCall(20, () => {
                     modifierSelectUiHandler.processInput(Button.ACTION);
                     thisArg.time.delayedCall(250, () => {
-                        console.log(modifierTypes[optionIndex]?.name);
+                        console.log(modifierTypeOptions[optionIndex]?.type.name);
                         if (thisArg.getCurrentPhase() instanceof SelectModifierPhase) {
                             if (optionIndex < modifierSelectUiHandler.options.length - 1) {
                                 optionIndex++;
diff --git a/src/battle-phases.ts b/src/battle-phases.ts
index 1d3a79be948..3c1f800e2df 100644
--- a/src/battle-phases.ts
+++ b/src/battle-phases.ts
@@ -16,7 +16,7 @@ import { EvolutionPhase } from "./evolution-phase";
 import { BattlePhase } from "./battle-phase";
 import { BattleStat, getBattleStatLevelChangeDescription, getBattleStatName } from "./battle-stat";
 import { Biome, biomeLinks } from "./biome";
-import { ModifierType, PokemonModifierType, PokemonMoveModifierType, getModifierTypesForWave, regenerateModifierPoolThresholds } from "./modifier-type";
+import { ModifierType, ModifierTypeOption, PokemonModifierType, PokemonMoveModifierType, getModifierTypeOptionsForWave, regenerateModifierPoolThresholds } from "./modifier-type";
 
 export class SelectStarterPhase extends BattlePhase {
   constructor(scene: BattleScene) {
@@ -1294,7 +1294,7 @@ export class SelectModifierPhase extends BattlePhase {
     regenerateModifierPoolThresholds(party);
     const modifierCount = new Utils.IntegerHolder(3);
     this.scene.applyModifiers(ExtraModifierModifier, modifierCount);
-    const types: Array<ModifierType> = getModifierTypesForWave(this.scene.currentBattle.waveIndex - 1, modifierCount.value, party);
+    const types: Array<ModifierTypeOption> = getModifierTypeOptionsForWave(this.scene.currentBattle.waveIndex - 1, modifierCount.value, party);
 
     const modifierSelectCallback = (cursor: integer) => {
       if (cursor < 0) {
@@ -1312,8 +1312,8 @@ export class SelectModifierPhase extends BattlePhase {
             this.scene.ui.setMode(Mode.MODIFIER_SELECT);
             const modifierType = types[cursor];
             const modifier = !isMoveModifier
-              ? modifierType.newModifier(party[slotIndex])
-              : modifierType.newModifier(party[slotIndex], option - PartyOption.MOVE_1);
+              ? modifierType.type.newModifier(party[slotIndex])
+              : modifierType.type.newModifier(party[slotIndex], option - PartyOption.MOVE_1);
             this.scene.addModifier(modifier).then(() => super.end());
             this.scene.ui.clearText();
             this.scene.ui.setMode(Mode.MESSAGE);
@@ -1321,7 +1321,7 @@ export class SelectModifierPhase extends BattlePhase {
             this.scene.ui.setMode(Mode.MODIFIER_SELECT, types, modifierSelectCallback);
         }, pokemonModifierType.selectFilter, modifierType instanceof PokemonMoveModifierType ? (modifierType as PokemonMoveModifierType).moveSelectFilter : undefined);
       } else {
-        this.scene.addModifier(types[cursor].newModifier()).then(() => super.end());
+        this.scene.addModifier(types[cursor].type.newModifier()).then(() => super.end());
         this.scene.ui.clearText();
         this.scene.ui.setMode(Mode.MESSAGE);
       }
diff --git a/src/battle-scene.ts b/src/battle-scene.ts
index d71a8fd7411..a410a96f77b 100644
--- a/src/battle-scene.ts
+++ b/src/battle-scene.ts
@@ -195,6 +195,7 @@ export default class BattleScene extends Phaser.Scene {
 		this.loadSe('stat_down');
 		this.loadSe('faint');
 		this.loadSe('flee');
+		this.loadSe('low_hp');
 		this.loadSe('exp');
 		this.loadSe('level_up');
 		this.loadSe('sparkle');
@@ -202,6 +203,7 @@ export default class BattleScene extends Phaser.Scene {
 		this.loadSe('shine');
 		this.loadSe('charge');
 		this.loadSe('beam');
+		this.loadSe('upgrade');
 		this.loadSe('error');
 
 		this.loadSe('pb');
diff --git a/src/modifier-type.ts b/src/modifier-type.ts
index 0661f9231c0..ded653d36dc 100644
--- a/src/modifier-type.ts
+++ b/src/modifier-type.ts
@@ -328,15 +328,16 @@ export function regenerateModifierPoolThresholds(party: PlayerPokemon[]) {
   console.log(modifierPoolThresholds)
 }
 
-export function getModifierTypesForWave(waveIndex: integer, count: integer, party: PlayerPokemon[]): ModifierType[] {
+export function getModifierTypeOptionsForWave(waveIndex: integer, count: integer, party: PlayerPokemon[]): ModifierTypeOption[] {
   if (waveIndex % 10 === 0)
-    return modifierPool[ModifierTier.LUXURY];
-  return new Array(count).fill(0).map(() => getNewModifierType(party));
+    return modifierPool[ModifierTier.LUXURY].map(m => new ModifierTypeOption(m, false));
+  return new Array(count).fill(0).map(() => getNewModifierTypeOption(party));
 }
 
-function getNewModifierType(party: PlayerPokemon[]): ModifierType {
+function getNewModifierTypeOption(party: PlayerPokemon[]): ModifierTypeOption {
   const tierValue = Utils.randInt(256);
-  const tier = tierValue >= 52 ? ModifierTier.COMMON : tierValue >= 8 ? ModifierTier.GREAT : tierValue >= 1 ? ModifierTier.ULTRA : ModifierTier.MASTER;
+  const upgrade = Utils.randInt(32) === 0;
+  const tier: ModifierTier = (tierValue >= 52 ? ModifierTier.COMMON : tierValue >= 8 ? ModifierTier.GREAT : tierValue >= 1 ? ModifierTier.ULTRA : ModifierTier.MASTER) + (upgrade ? 1 : 0);
   const thresholds = Object.keys(modifierPoolThresholds[tier]);
   const totalWeight = parseInt(thresholds[thresholds.length - 1]);
   const value = Utils.randInt(totalWeight);
@@ -354,5 +355,15 @@ function getNewModifierType(party: PlayerPokemon[]): ModifierType {
     modifierType = (modifierType as WeightedModifierType).modifierType;
   if (modifierType instanceof ModifierTypeGenerator)
     modifierType = (modifierType as ModifierTypeGenerator).generateType(party);
-  return modifierType as ModifierType;
+  return new ModifierTypeOption(modifierType as ModifierType, upgrade);
+}
+
+export class ModifierTypeOption {
+  public type: ModifierType;
+  public upgraded: boolean;
+
+  constructor(type: ModifierType, upgraded: boolean) {
+    this.type = type;
+    this.upgraded = upgraded;
+  }
 }
\ No newline at end of file
diff --git a/src/ui/modifier-select-ui-handler.ts b/src/ui/modifier-select-ui-handler.ts
index 67c3d17665e..a45f2eb450a 100644
--- a/src/ui/modifier-select-ui-handler.ts
+++ b/src/ui/modifier-select-ui-handler.ts
@@ -1,5 +1,5 @@
 import BattleScene, { Button } from "../battle-scene";
-import { ModifierTier, ModifierType } from "../modifier-type";
+import { ModifierTier, ModifierType, ModifierTypeOption } from "../modifier-type";
 import { getPokeballAtlasKey, PokeballType } from "../pokeball";
 import { addTextObject, TextStyle } from "../text";
 import AwaitableUiHandler from "./awaitable-ui-handler";
@@ -48,16 +48,18 @@ export default class ModifierSelectUiHandler extends AwaitableUiHandler {
 
     this.getUi().clearText();
 
-    const types = args[0] as ModifierType[];
-    for (let m = 0; m < types.length; m++) {
-      const sliceWidth = (this.scene.game.canvas.width / 6) / (types.length + 2);
-      const option = new ModifierOption(this.scene, sliceWidth * (m + 1) + (sliceWidth * 0.5), -this.scene.game.canvas.height / 12 - 24, types[m]);
+    const typeOptions = args[0] as ModifierTypeOption[];
+    for (let m = 0; m < typeOptions.length; m++) {
+      const sliceWidth = (this.scene.game.canvas.width / 6) / (typeOptions.length + 2);
+      const option = new ModifierOption(this.scene, sliceWidth * (m + 1) + (sliceWidth * 0.5), -this.scene.game.canvas.height / 12 - 24, typeOptions[m]);
       option.setScale(0.5);
       this.scene.add.existing(option);
       this.modifierContainer.add(option);
       this.options.push(option);
     }
 
+    const hasUpgrade = typeOptions.filter(to => to.upgraded).length;
+
     this.scene.tweens.add({
       targets: this.overlayBg,
       alpha: 0.5,
@@ -72,15 +74,15 @@ export default class ModifierSelectUiHandler extends AwaitableUiHandler {
       duration: 1250,
       onUpdate: t => {
         const value = t.getValue();
-        const index = Math.floor(value * types.length);
-        if (index > i && index <= types.length) {
+        const index = Math.floor(value * typeOptions.length);
+        if (index > i && index <= typeOptions.length) {
           const option = this.options[i++];
-          option?.show(Math.floor((1 - value) * 1250) * 0.325);
+          option?.show(Math.floor((1 - value) * 1250) * 0.325 + (hasUpgrade ? 2000 : 0));
         }
       }
     });
 
-    this.scene.time.delayedCall(4000, () => {
+    this.scene.time.delayedCall(4000 + (hasUpgrade ? 2000 : 0), () => {
       this.setCursor(0);
       this.awaitingActionInput = true;
       this.onActionInput = args[1];
@@ -140,7 +142,7 @@ export default class ModifierSelectUiHandler extends AwaitableUiHandler {
 
     const sliceWidth = (this.scene.game.canvas.width / 6) / (this.options.length + 2);
     this.cursorObj.setPosition(sliceWidth * (cursor + 1) + (sliceWidth * 0.5) - 20, -this.scene.game.canvas.height / 12 - 20);
-    ui.showText(this.options[this.cursor].modifierType.description);
+    ui.showText(this.options[this.cursor].modifierTypeOption.type.description);
 
     return ret;
   }
@@ -179,33 +181,43 @@ export default class ModifierSelectUiHandler extends AwaitableUiHandler {
 }
 
 class ModifierOption extends Phaser.GameObjects.Container {
-  public modifierType: ModifierType;
+  public modifierTypeOption: ModifierTypeOption;
   private pb: Phaser.GameObjects.Sprite;
+  private pbTint: Phaser.GameObjects.Sprite;
   private itemContainer: Phaser.GameObjects.Container;
   private item: Phaser.GameObjects.Sprite;
   private itemTint: Phaser.GameObjects.Sprite;
   private itemText: Phaser.GameObjects.Text;
 
-  constructor(scene: BattleScene, x: number, y: number, modifierType: ModifierType) {
+  constructor(scene: BattleScene, x: number, y: number, modifierTypeOption: ModifierTypeOption) {
     super(scene, x, y);
 
-    this.modifierType = modifierType;
+    this.modifierTypeOption = modifierTypeOption;
 
     this.setup();
   }
 
   setup() {
-    this.pb = this.scene.add.sprite(0, -150, 'pb', this.getPbAtlasKey());
-    this.pb.setScale(2);
+    const getPb = (): Phaser.GameObjects.Sprite => {
+      const pb = this.scene.add.sprite(0, -150, 'pb', this.getPbAtlasKey(true));
+      pb.setScale(2);
+      return pb;
+    };
+
+    this.pb = getPb();
     this.add(this.pb);
 
+    this.pbTint = getPb();
+    this.pbTint.setVisible(false);
+    this.add(this.pbTint);
+
     this.itemContainer = this.scene.add.container(0, 0);
     this.itemContainer.setScale(0.5);
     this.itemContainer.setAlpha(0);
     this.add(this.itemContainer);
 
     const getItem = () => {
-      const item = this.scene.add.sprite(0, 0, 'items', this.modifierType.iconImage);
+      const item = this.scene.add.sprite(0, 0, 'items', this.modifierTypeOption.type.iconImage);
       return item;
     };
 
@@ -216,7 +228,7 @@ class ModifierOption extends Phaser.GameObjects.Container {
     this.itemTint.setTintFill(Phaser.Display.Color.GetColor(255, 192, 255));
     this.itemContainer.add(this.itemTint);
 
-    this.itemText = addTextObject(this.scene, 0, 35, this.modifierType.name, TextStyle.PARTY, { align: 'center' });
+    this.itemText = addTextObject(this.scene, 0, 35, this.modifierTypeOption.type.name, TextStyle.PARTY, { align: 'center' });
     this.itemText.setOrigin(0.5, 0);
     this.itemText.setAlpha(0);
     this.itemText.setTint(this.getTextTint());
@@ -253,11 +265,39 @@ class ModifierOption extends Phaser.GameObjects.Container {
       }
     });
 
+    if (this.modifierTypeOption.upgraded) {
+      this.scene.time.delayedCall(remainingDuration, () => {
+        this.scene.sound.play('upgrade');
+        this.pbTint.setPosition(this.pb.x, this.pb.y);
+        this.pbTint.setTintFill(0xFFFFFF);
+        this.pbTint.setAlpha(0);
+        this.pbTint.setVisible(true);
+        this.scene.tweens.add({
+          targets: this.pbTint,
+          alpha: 1,
+          duration: 1000,
+          ease: 'Sine.easeIn',
+          onComplete: () => {
+            this.pb.setTexture('pb', this.getPbAtlasKey(false));
+            this.scene.tweens.add({
+              targets: this.pbTint,
+              alpha: 0,
+              duration: 1000,
+              ease: 'Sine.easeOut',
+              onComplete: () => {
+                this.pbTint.setVisible(false);
+              }
+            });
+          }
+        });
+      });
+    }
+
     this.scene.time.delayedCall(remainingDuration + 2000, () => {
       if (!this.scene)
         return;
 
-      this.pb.setTexture('pb', `${this.getPbAtlasKey()}_open`);
+      this.pb.setTexture('pb', `${this.getPbAtlasKey(false)}_open`);
       this.scene.sound.play('pb_rel');
       
       this.scene.tweens.add({
@@ -292,12 +332,12 @@ class ModifierOption extends Phaser.GameObjects.Container {
     })
   }
 
-  getPbAtlasKey() {
-    return getPokeballAtlasKey(this.modifierType.tier as integer as PokeballType);
+  getPbAtlasKey(beforeUpgrade: boolean) {
+    return getPokeballAtlasKey((this.modifierTypeOption.type.tier - (beforeUpgrade && this.modifierTypeOption.upgraded ? 1 : 0)) as integer as PokeballType);
   }
 
   getTextTint(): integer {
-    switch (this.modifierType.tier) {
+    switch (this.modifierTypeOption.type.tier) {
       case ModifierTier.COMMON:
         return 0xffffff
       case ModifierTier.GREAT: