diff --git a/Common/Rewards/CardTransformReward.cs b/Common/Rewards/CardTransformReward.cs
new file mode 100644
index 00000000..15bdf1f5
--- /dev/null
+++ b/Common/Rewards/CardTransformReward.cs
@@ -0,0 +1,106 @@
+using BaseLib.Abstracts;
+using BaseLib.Extensions;
+using BaseLib.Patches.Content;
+using MegaCrit.Sts2.Core.Entities.Players;
+using MegaCrit.Sts2.Core.Localization;
+using MegaCrit.Sts2.Core.Models.Cards;
+using MegaCrit.Sts2.Core.Rewards;
+using MegaCrit.Sts2.Core.Runs;
+using MegaCrit.Sts2.Core.Saves.Runs;
+
+namespace BaseLib.Common.Rewards;
+
+///
+/// A reward class similar to the card removal one created by ,
+/// only for transforming instead of removing cards
+///
+///
+/// In a relic or power's AfterCombatEnd override
+///
+/// room.AddExtraReward(Owner.Player, new CardTransformReward(Owner.Player) {Amount = Amount, Upgrade = true});
+///
+public sealed class CardTransformReward(Player player) : CustomReward(player)
+{
+ ///
+ /// A new defined with the attribute
+ ///
+ [CustomEnum] public static RewardType CardTransform;
+ ///
+ /// Reference to the defined earlier
+ ///
+ protected override RewardType RewardType => CardTransform;
+
+ ///
+ /// Whether the card rewards should be upgraded or not
+ ///
+ public required bool Upgrade;
+ ///
+ /// How many cards can be selected in this reward screen
+ ///
+ public required int Amount;
+
+ ///
+ /// The description to show in the reward screen,
+ /// switches based on whether the reward will upgrade the transformed cards
+ ///
+ public override LocString Description
+ {
+ get
+ {
+ LocString locString = new LocString("gameplay_ui", "COMBAT_REWARD_CARD_TRANSFORM");
+ locString.Add("cards", Amount);
+ locString.Add("Upgrade", Upgrade);
+ return locString;
+ }
+ }
+ ///
+ public override bool IsPopulated => true;
+ public static string RewardIcon => ImageHelperExtensions.GetModImagePath("ui/reward_screen/reward_icon_card_transform.png");
+ ///
+ protected override string IconPath => RewardIcon;
+
+
+ ///
+ /// Serializing the reward, saving whether to upgrade and how many cards to transform in the vanilla fields
+ ///
+ public override SerializableReward ToSerializable()
+ {
+ return new SerializableReward()
+ {
+ RewardType = CardTransform,
+ GoldAmount = Amount,
+ WasGoldStolenBack = Upgrade
+ };
+ }
+
+ ///
+ /// Recreates the reward from the saved
+ ///
+ /// The that was created and saved from
+ ///
+ /// The the reward belongs to
+ public CardTransformReward CreateFromSerializable(SerializableReward save, Player player)
+ {
+ return new CardTransformReward(player) {
+ // hijacking the gold amounts as a temp hack before worrying about extending the serialized values
+ Amount = save.GoldAmount,
+ Upgrade = save.WasGoldStolenBack
+ };
+ }
+
+ ///
+ public override SerializableCustomReward SerializeMethod => CreateFromSerializable;
+
+ ///
+ public override void MarkContentAsSeen() { }
+
+ ///
+ public override Task Populate() { return Task.CompletedTask; }
+
+ ///
+ protected override async Task OnSelect()
+ {
+ BaseLibMain.Logger.Info("Obtained card transformation from reward");
+ return await RunManager.Instance.RewardSynchronizer.DoLocalCardTransform(Amount, true);
+ }
+}
diff --git a/Common/Rewards/CardTransformRewardMessage.cs b/Common/Rewards/CardTransformRewardMessage.cs
new file mode 100644
index 00000000..955e0487
--- /dev/null
+++ b/Common/Rewards/CardTransformRewardMessage.cs
@@ -0,0 +1,78 @@
+using BaseLib.Abstracts;
+using BaseLib.Patches.Content;
+using MegaCrit.Sts2.Core.Combat;
+using MegaCrit.Sts2.Core.Entities.Players;
+using MegaCrit.Sts2.Core.Helpers;
+using MegaCrit.Sts2.Core.Logging;
+using MegaCrit.Sts2.Core.Multiplayer.Game;
+using MegaCrit.Sts2.Core.Multiplayer.Serialization;
+using MegaCrit.Sts2.Core.Runs;
+
+namespace BaseLib.Common.Rewards;
+
+///
+/// Message for transforming a card from a new reward type
+///
+public sealed class ZZ_CardTransformRewardMessage : CustomRewardMessage
+{
+ internal void HandleCardTransformedMessage(ZZ_CardTransformRewardMessage message, ulong senderId)
+ {
+ BaseLibMain.Logger.Debug($"Handling message {message}");
+ var rs = RunManager.Instance.RewardSynchronizer;
+ if (CombatManager.Instance.IsInProgress)
+ {
+ rs.BufferCustomRewardMessage(message, senderId);
+ BaseLibMain.Logger.Debug($"Buffered card transform message for {rs.PlayerCollection()?.GetPlayer(senderId)}");
+ return;
+ }
+
+ Player? player = rs.PlayerCollection()?.GetPlayer(senderId);
+ if (player == rs.LocalPlayerRef())
+ {
+ throw new InvalidOperationException("CardTransformRewardMessage should not be sent to the player transforming the card");
+ }
+ TaskHelper.RunSafely(rs.DoCardTransform(player, message.Amount, message.Upgrade));
+ }
+
+ ///
+ public override void Dispose(RunLocationTargetedMessageBuffer messageBuffer)
+ {
+ BaseLibMain.Logger.Debug($"Unregistering handler for {GetType()}");
+ messageBuffer.UnregisterMessageHandler(HandleCardTransformedMessage);
+ }
+
+ ///
+ public override void Initialize(RunLocationTargetedMessageBuffer messageBuffer)
+ {
+ BaseLibMain.Logger.Debug($"Registering handler for {GetType()}");
+ messageBuffer.RegisterMessageHandler(HandleCardTransformedMessage);
+ }
+
+ ///
+ /// Whether to upgrade the card as well as transforming
+ ///
+ public required bool Upgrade;
+ ///
+ /// The amount of cards to select from
+ ///
+ public required int Amount;
+
+ ///
+ public override LogLevel LogLevel => LogLevel.Debug;
+
+ ///
+ public override void Deserialize(PacketReader reader)
+ {
+ Location = reader.Read();
+ Amount = reader.ReadInt();
+ Upgrade = reader.ReadBool();
+ }
+
+ ///
+ public override void Serialize(PacketWriter writer)
+ {
+ writer.Write(Location);
+ writer.WriteInt(Amount);
+ writer.WriteBool(Upgrade);
+ }
+}