1) Create a new potion. We'll be sticking a scripted magic effect into this later.
2) Create an alternate poison for this potion. Feel free to fill in its effects now. Remove any sound it makes.
3) Create a new spell. This will have all of the effects the potion is supposedly applying.
4) Create a Message. Check it to be a message box and set your desired text. Create two buttons. The first (index 0) should be labeled Potion, Drink, or something like that. The second should be Poison, Coat, or whatever.
5) Create a fire-and-forget self-targetted script magic effect. The description you give on it is what will show on the potion. You need to tell about both effects, but be very brief so the text doesn't auto-size to tiny letters. My example potion is a new Synthetic Blood Serum. It can either be consumed as a vampire to feed and get sun immunity or used as a poison against vampires (ignoring their normal poison immunity). Its description is:
Drink: One day of immunty to sunlight and blood thirst.
Coat: Weaken vampire for <60> seconds.
Something similarly short will be needed. Be very concise. Attach a new script to this magic effect. This is a cut-down version of my script meant to be used as an example.
Spoiler
Scriptname PotionPoisonToggleScript extends activemagiceffect;The message box you created previously is set hereMessage Property UseChoice Auto;The potion this magic effect is on is set here.Potion Property ItemPotionSelf Auto;The poison alternative is set herePotion Property ItemPoisonAlternate Auto;This is the spell that casts the potion's actual effects.Spell Property SpellPotion Auto;Variables used in the scriptInt iSelectionChoice = 0Actor TargetActorEvent OnEffectStart(Actor akTarget, Actor akCaster)TargetActor = akTarget;If we have the option to use this as a potion or poison, present the choice.;Actually performing this requires that all three of these Properties have been set.if UseChoice && ItemPotionSelf && ItemPoisonAlternate ;This sets the variable to be equal to the index of the button the player presses ;Remember that 0 is Drink Potion and 1 is Apply Poison iSelectionChoice = UseChoice.Show() ;If the player chose Poison if iSelectionChoice == 1 TargetActor.AddItem(ItemPoisonAlternate, 1, true) TargetActor.EquipItem(ItemPoisonAlternate) ;This is used because it only occurs during game mode ;Doing the switch in menu mode causes issues RegisterForSingleUpdate(0.5) ;If the player chose Potion or something went wrong, we'll default to potion else PotionActivate() endif;If we don't have an option, just use this as a potionelse PotionActivate()endifEndEventEvent OnUpdate() ;If you chose not to use the poison or had one already applied, remove it and give back potion while TargetActor.GetItemCount(ItemPoisonAlternate) TargetActor.RemoveItem(ItemPoisonAlternate, 1, true) TargetActor.AddItem(ItemPotionSelf, 1, true) endwhileEndEventFunction PotionActivate() SpellPotion.Cast(TargetActor)EndFunction
Now apply that magic effect to the base potion. Importantly, make sure the duration is 1 second (or longer, I don't care). If the potion is instant, the OnUpdate event can never fire.
The modified version of that script used for my example Synthetic Blood Serum is this:
Spoiler
Scriptname DLC1SyntheticBloodEffectScript extends activemagiceffectplayervampirequestscript Property PlayerVampireQuest AutoMessage Property UseChoice AutoPotion Property ItemPotionSelf AutoPotion Property ItemPoisonAlternate AutoSpell Property SpellPotion AutoBool Property SunProtection AutoInt iSelectionChoice = 0Actor TargetActorEvent OnEffectStart(Actor akTarget, Actor akCaster)TargetActor = akTarget;If we have the option to use this as a potion or poison, present the choice.if UseChoice && ItemPotionSelf && ItemPoisonAlternate iSelectionChoice = UseChoice.Show() ;Chose Poison if iSelectionChoice == 1 TargetActor.AddItem(ItemPoisonAlternate, 1, true) TargetActor.EquipItem(ItemPoisonAlternate) ;This is used because it only occurs during game mode RegisterForSingleUpdate(0.5) ;Chose Potion (or something went wrong) else PotionFeed() endif;If we don't have an option, just use this as a potionelse PotionFeed()endifEndEventEvent OnUpdate() ;If you chose not to use the poison or had one already applied, remove it and give back potion while TargetActor.GetItemCount(ItemPoisonAlternate) TargetActor.RemoveItem(ItemPoisonAlternate, 1, true) TargetActor.AddItem(ItemPotionSelf, 1, true) endwhileEndEventFunction PotionFeed() ;I've decompiled the Dawnguard version of this script and modified this function. ;That's why you're seeing me send parameters it isn't normally capable of receiving PlayerVampireQuest.VampireFeed(false, true) if SunProtection SpellPotion.Cast(TargetActor) endifEndFunction
But this method can do more than just potion/poison! Want to make a potion that can be either consumed or lobbed as a grenade?
1) Create a new potion like last time.
2) Make a blank magic effect that does nothing. Write your description for the potion into its effects. Potions inexplicably cannot have their own descriptions and our other magic effect will have Hide in UI checked, so this magic effect is just needed to apply a description. That's all it does.
3) Create a scroll alternate for the potion instead of a poison. Feel free to fill in its effects now. Using a lobber projectile would be a cool way to represent throwing the potion (maybe if consumed it gives frost resistance, if thrown cause fire damage?)
4) Create a new spell. Again, this is where the actual potion effects are.
5) Create a Message with two options as before. Index 0 should be potion and index 1 should be scroll.
6) Make a fire-and-forget self-targeted script magic effect as before, but this time make sure to check Hide in UI on it. For its script, use something like this:
Spoiler
Scriptname PotionScrollToggleScript extends activemagiceffect;The message box you created previously is set hereMessage Property UseChoice Auto;The potion this magic effect is on is set here.Potion Property ItemPotionSelf Auto;The scroll alternative is set hereScroll Property ItemScrollAlternate Auto;This is the spell that casts the potion's actual effects.Spell Property SpellPotion Auto;Variables used in the scriptInt iSelectionChoice = 0Actor TargetActorEvent OnEffectStart(Actor akTarget, Actor akCaster)TargetActor = akTarget;If we have the option to use this as a potion or scroll, present the choice.if UseChoice && ItemPotionSelf && ItemScrollAlternate iSelectionChoice = UseChoice.Show() ;Chose Scroll if iSelectionChoice == 1 TargetActor.AddItem(ItemScrollAlternate, 1, true) TargetActor.EquipItem(ItemScrollAlternate) ;This is used because it only occurs during game mode ;Although RegisterForSingleUpdate is fairly safe with low values, it's ;not a good idea to use RegisterForUpdate with anything lower than 1 RegisterForUpdate(1) ;Chose Potion (or something went wrong) else PotionActivate() endif;If we don't have an option, just use this as a potionelse PotionActivate()endifEndEventEvent OnUpdate() ;If you have multiple copies of the potion, we'll queue the next up ;This is because scrolls can keep getting cast as long as you have copies ;It would be annoying for the player to have to manually select each one ;Now as long as you have potions, you can cast at least each update (1 second) if TargetActor.GetItemCount(ItemScrollAlternate) < 2 && TargetActor.GetItemCount(ItemPotionSelf) TargetActor.RemoveItem(ItemScrollAlternate, 1, true) TargetActor.AddItem(ItemPotionSelf, 1, true) endif ;If you unequip the scroll, we'll convert all copies back to potions if TargetActor.IsEquipped(ItemScrollAlternate) == 0 while TargetActor.GetItemCount(ItemScrollAlternate) TargetActor.RemoveItem(ItemScrollAlternate, 1, true) TargetActor.AddItem(ItemPotionSelf, 1, true) endwhile ;Make sure to end this whole thing. Dispel alone should do it, but let's be safe UnregisterForUpdate() Dispel() Return EndIfEndEventFunction PotionActivate() SpellPotion.Cast(TargetActor)EndFunction
Now apply the magic effect to the potion. Important, set its duration to be something stupid. Years of real-time duration are totally safe to use within the game engine. Just set it so long that it would never run out. We'll end it ourselves when needed with that Dispel() function, but it needs to keep running as long as the player could potentially still be equipped with the alternate scroll. Scrolls can't have their own scripts, so we need some method to keep running things. This is why you set Hide in UI on the effect. You don't want the thing and its silly duration showing to the player.
Note: I have not tested the potion/scroll system. I know the potion/poison system works great, but admittedly the potion/scroll is just a theory I've not messed with yet. The script compiles and I see no reason it shouldn't work, but let me know if you find an issue and I'll correct it.
Technically this could be expanded to make potions that can have any kind of alternate ability. You could make potions of bound armor, even. That would be cool. In fact, you could make a single potion with the ability to be used as many different things. It's fairly easy to modify this script, so use it for whatever you want.
Or what about a mod that changed the http://www.uesp.net/wiki/Skyrim:The_White_Phial_%28Full%29 so that it has every one of the options at once and you could just pick which one you want when you drink it? That should be easy to do too.