New and improved alcohol (edit: finished!)

How would I go about this?

By default they are potions with one of three alcohol effects. Can I create new spells and have them show up on that list and simply select mine instead? Or do I need to do something else?

I already have plenty types and brands of alcohol created as custom items, but I need the right effect to put on them.

I’m looking to create quite a few spells - around 10 I’d say - as certain types will be more potent than others, some will be enchanted to have no negative side effects, some will have a chance to have collectible items under the lid when opened, etc.

I don’t think I will have any problems scripting the actual spells, but I can’t seem to find much information on creating new spells. Any directions would be appreciated, thanks.

Sincerely,

Charles.

Edit: Script is now finished. It seemed I needed quite a bit of help “scripting the actual spells” after all which I got for which I am very grateful. Thank you.

As it is currently, the script makes drinkers roll a fortitude save against poison. If the save fails, the drinker is dazed and takes some dex, con, int and wis damage; if it succeeds the dex damage is negated, the rest is halved and charisma is buffed by the same amount. If the drinkers con is set to 3 from drinking, they are knocked down and warned against consuming any more alcohol and it if is 3 when drinking and they fail the first save, they must roll again to avoid dying from alcohol poisoning. The script prevents queing alcohol to ensure players can react to the warning. The potency of the alcohol determines the DC, damage and duration.

This means dwarves are really good drinkers due to not only having a CON bonus, but also a +2 save against poison. It means Ironguts help resist the effects of alcohol and it means some classes will eventually be immune.

It requires alcohol items to have a local int on them corresponding a variable called in the script (default “Proof”/“nProof”) with a number representing the alcohol volume. It also requires that alcohol items sold in shops NOT be in infinite quantities or the local int will disappear and default to 0. It is a self only unique effect and is called from another script placed in module > OnActivateItem which requires that the name of the script and the tag of the item be identical.

You are welcome to use it if you like. It’s at the bottom of the thread.

Charles.

Use Cast Spell: Unique (Self) with tag-based scripting. No need to edit 2da files if you do it that way. The name of the script matches the tag of the alcohol item. BioWare even provides a template script (x2_it_example - I think that’s the name) for doing tag-based item events.

2 Likes

If you do want to use new spell IDs, look at spells.2da lines 406-408. These are the entries for beer, wine, and spirits, respectively. They all use the same script, nw_s3_alcohol, to apply their effects and just call GetSpellID() to determine which property cast them.

1 Like

If you do decide to use new spells, you might also consider spell-hooking, or making an entirely new spell script, rather than edit nw_s3_alcohol itself.

Now that Beamdog are enhancing NWN again, there’s a case for not touching official scripts, just in case they’re improved by the devs at a later date.

Probably academic in this instance.

3 Likes

Thanks for help!

While it seems very neat to use edited alcohol spells, the fact that I’ll be adding quite a few seems to make it a bit of a lost cause. I’ll have to figure out how to make custom spells eventually, but for now I’ve gone with unique items and tags, which seem to work fine.

I’ve run into a bit of a problem though; I’ve scripted different effects to take place depending on whether one succeeds a poison save, fail it, or are immune to poison. When testing, the saving throw shows up when drinking, but the effect is always as if it succeeded, even if I can see it just failed. Furthermore, the EffectAbilityDecrease seems to be capped at 3? This is a problem as I want enough alcohol in a short enough time to be fatal.

Script

void main()

{

object oA = GetItemActivator();

int nA = FortitudeSave(oA, 10, SAVING_THROW_TYPE_POISON);
int nB = 2;
int nC = (nB / 2);

effect eA = EffectAbilityDecrease(ABILITY_DEXTERITY, 1);
effect eB = EffectAbilityDecrease(ABILITY_CONSTITUTION, 1);
effect eC = EffectAbilityDecrease(ABILITY_INTELLIGENCE, 1);
effect eD = EffectAbilityDecrease(ABILITY_WISDOM, 1);
effect eE = EffectDazed();
effect eF = EffectVisualEffect(VFX_IMP_FORTITUDE_SAVING_THROW_USE);

if (nA = 0)

{

 ApplyEffectToObject(DURATION_TYPE_INSTANT, eE, oA);
 ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eA, oA, 300.0);

}

else if (nA = 1)

{

 ApplyEffectToObject(DURATION_TYPE_INSTANT, eF, oA);
 ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eB, oA, 300.0);

}

else if (nA = 2)

{

SendMessageToPC(oA, "The alcohol has little effect on you.");

}

}

Some of the stuff isn’t used yet. I’m making a basic script first and then editing the values for specific types of drinks (scripts) later. I kept them here because that’s the way the script currently looks (and it compiles fine.)

== is not the same as =. You are using assignment in your comparisons.

if (nA = 0) // sets nA to 0 and because 0 is false the expression is not true,
if (nA = 1) // sets nA to 1 and since 1 is true the expression is true and this branch is taken.

Try if (nA == 0) …

2 Likes

Thanks! That did it!

So basically = is for answering and == is for asking?

I seem to have run into another problem…

Summary

void main()

{

object oA = GetItemActivator();

int nA = 20;
int nB; if (nA < 10) {int nB = 10;} else {int nB = nA;}
int nC = FortitudeSave(oA, nB, SAVING_THROW_TYPE_POISON);
int nD; if (nC == 0) {int nD = nA / 5;} else {int nD = nA / 10;}

float fA = IntToFloat(nD * 60);

effect eA = EffectAbilityDecrease(ABILITY_DEXTERITY, nD);
effect eB = EffectAbilityDecrease(ABILITY_CONSTITUTION, nD);
effect eC = EffectAbilityDecrease(ABILITY_INTELLIGENCE, nD);
effect eD = EffectAbilityDecrease(ABILITY_WISDOM, nD);
effect eE = EffectAbilityIncrease(ABILITY_CHARISMA, nD);

if (nC == 0)

{

ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eA, oA, fA);
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eB, oA, fA);
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eC, oA, fA);
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eD, oA, fA);
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectDazed(), oA, fA/20);

}

else if (nC == 1)

{

ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_FORTITUDE_SAVING_THROW_USE), oA);
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eB, oA, fA);
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eC, oA, fA);
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eD, oA, fA);
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eE, oA, fA);

}

else if (nC == 2)

{

SendMessageToPC(oA, "The alcohol has little effect on you.");

}

}

Currently when testing the DC is 0 and there are no stat changes (or dazes on natural 1s), but there is a visual effect when succeeding the save.

I think it is the variables being defined within if conditionals.
Is this not possible? Or am I doing it wrong?

I tried removing the initial int=X; before the ifs which were only there to make the script compile and it did the exact same thing (I was surprised it worked since I had read ActivateItem scripts required scripts to actually compile, but this would suggest it only needs them to work ie. be logically consistent.)

I can make it work without them by adding more abilitydecrease effects, but it would be nice to to if this could work and how.

Thanks for reading.

Charles.

I don’t see what the problem is. Try this code:

void main()
{
	object oActivator = GetItemActivator();

	int nA = 20; // Alcohol Resistance?
	int nDC;

	if (nA < 10) {
		nDC = 10;
	}
	else {
		nDC = nA;
	}

	int nSave = FortitudeSave(oActivator, nDC, SAVING_THROW_TYPE_POISON);
	int nDamage = nA / 5;
	if (nSave != 0) { nDamage = nDamage / 2; }
	    
	float fDur = IntToFloat(nDamage * 60);

	effect eDex = EffectAbilityDecrease(ABILITY_DEXTERITY, nDamage);
	effect eCon = EffectAbilityDecrease(ABILITY_CONSTITUTION, nDamage);
	effect eInt = EffectAbilityDecrease(ABILITY_INTELLIGENCE, nDamage);
	effect eWis = EffectAbilityDecrease(ABILITY_WISDOM, nDamage);
	effect eCha = EffectAbilityIncrease(ABILITY_CHARISMA, nDamage);
	effect eDaze = EffectDazed();
	effect eVis = EffectVisualEffect(VFX_IMP_FORTITUDE_SAVING_THROW_USE);

	if (nSave == 0)
	{
		ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eDex, oActivator, fDur);
		ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eCon, oActivator, fDur);
		ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eInt, oActivator, fDur);
		ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eWis, oActivator, fDur);
		ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eDaze, oActivator, fDur/20);
	}
	else if (nSave == 1)
	{
		ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oActivator);
		ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eCon, oActivator, fDur);
		ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eInt, oActivator, fDur);
		ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eWis, oActivator, fDur);
		ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eCha, oActivator, fDur);
	}
	else if (nSave == 2)
	{
		SendMessageToPC(oActivator, "The alcohol has little effect on you.");
	}
}
1 Like

Yours seems to work fine.

Maybe this has something to do with layers of referencing? I think I read somewhere that there is a cap.

int nA is alcohol proof% - the higher it is the higher the DC and damage

The module has the OnActivateItem script where it plays a script named after the activated items tag.

This script would be named i_alcohol_20 and so would any kind of alcohol (regardless of unique name and resref) with that exact alcohol proof. This way, all I have to do to make a new kind of alcohol is copy the script and change nA and name it accordingly.

I will probably make some bottles of some 80% slivovitz-like stuff that will therefore have a DC of 80 and stat penalties of 16 on failures and implement some kind of knockdown or sleep if one reaches 3 in any stat from drinking.

I also considered adding charges by checking the item for a local int and if it was lower than the amount of charges set (like setting alcohol proof) respawn the bottle in the players inventory and set the int to +1. This however seems like a lost cause due to the fact that potions are stackable which will probably make a huge mess of things. The maximum stack setting on items only seem to apply for stores.

Thanks for the help.

If you define a variable inside ‘if’, you cannot use this variable outside ‘if’. That’s why you get an error when compiling (that’s pretty obvious, since a condition for ‘if’ can return false sometimes).

And that’s also the reason why you get DC = 0 (0 is a default value for a new numeric variable). You defined (int nB) a variable for DC two times – before ‘if’ and inside ‘if’, but the compiler ignores variable definitions inside ‘if’ for the code outside ‘if’. See my code, I’m only assigning a new value inside ‘if’, the definition is placed before ‘if’.

And the same error with nD variable…

So if you replace

int nA = 20;
int nB; if (nA < 10) {int nB = 10;} else {int nB = nA;}
int nC = FortitudeSave(oA, nB, SAVING_THROW_TYPE_POISON);
int nD; if (nC == 0) {int nD = nA / 5;} else {int nD = nA / 10;}

with

int nA = 20;
int nB; if (nA < 10) {nB = 10;} else {nB = nA;}
int nC = FortitudeSave(oA, nB, SAVING_THROW_TYPE_POISON);
int nD; if (nC == 0) {nD = nA / 5;} else {nD = nA / 10;}

you’ll get the correct result.

1 Like

Thanks for the explanation.

Does this mean that any variable I’ve defined can be reassigned a new definition later in the script by simply calling it = NewFunction?

Charles.

AFAIK, it will lead to the error.

VARIABLE ALREADY USED WITHIN SCOPE

Error Message Meaning:
This error message is saying that you are trying to define a variable whose name is already in use by another variable, function, or constant defined earlier in the same scope.

Scope is a term used when discussing identifier names to refer to those areas (or sections or lines) of a script where the name is “known” or “defined”. Code scoping blocks are identified by the curly braces “{” and “}”. All statements appearing between a matching set of curly braces are in the same block or scope. There is also a “script global” scope which is all the areas of the script that are outside of all scoping blocks and function bodies.

https://nwnlexicon.com/index.php/Compiler_Error_Messages#VARIABLE_ALREADY_USED_WITHIN_SCOPE

In your example those variables were in different scopes (the one in main(), the other in if()).

1 Like

Got it, thanks. :smiley:

This code could be condensed using EffectLinkEffects to link all the alcohol effects and only applying once.

#include "x2_inc_switches"

void main()
{
int nEvent =GetUserDefinedItemEventNumber();
object oPC;
object oItem;

//  This code runs when the Unique Power property of the item is used
//  Note that this event fires PCs only
if (nEvent ==  X2_ITEM_EVENT_ACTIVATE)
{
    oPC = GetItemActivator(); // Changed name of variable from oActivator to oPC - just my preference as event fires PC only
    oItem = GetItemActivated();

/*
    // Is this block of code needed?
    // Since nA is set at 20, if (nA <10) can never be true so nDC will always equal 20.

    	int nA = 20; // Alcohol Resistance?
    	int nDC;

    	if (nA < 10)
    {
        		nDC = 10;
    	}
    	else
    {
        		nDC = nA;
    	}

    // If the DC is variable based on the type of alcohol then use an integer variable on the item and grab that value from the
    // item itself.
*/

    // New code
    int nDC = GetLocalInt(oItem, "SaveDC");
    if (nDC < 10) nDC = 10;

    int nSave; // Moved save check to below variable declaration - just my scritping style
	   // int nDamage = nA/5; // Just use nDC/5
    int nDamage = nDC/5;
    if (nSave != 0)
    {
        nDamage = nDamage/2;
    }

	   float fDur = IntToFloat(nDamage *60);

	   effect eDex  = EffectAbilityDecrease(ABILITY_DEXTERITY, nDamage);
	   effect eCon  = EffectAbilityDecrease(ABILITY_CONSTITUTION, nDamage);
    effect eInt  = EffectAbilityDecrease(ABILITY_INTELLIGENCE, nDamage);
	   effect eWis  = EffectAbilityDecrease(ABILITY_WISDOM, nDamage);
	   effect eCha  = EffectAbilityIncrease(ABILITY_CHARISMA, nDamage);
	   effect eDaze = EffectDazed();
    //effect eVis  = EffectVisualEffect(VFX_IMP_FORTITUDE_SAVING_THROW_USE); // Why this VFX? - players associate this with a good thing
                                                                           // I would use VFX_IMP_REDUCE_ABILITY_SCORE
    effect eVis  = EffectVisualEffect(VFX_IMP_REDUCE_ABILITY_SCORE);
    effect eLink;

    nSave = FortitudeSave(oPC, nDC, SAVING_THROW_TYPE_POISON);
    switch (nSave)
    {
        case 0:
            eLink = EffectLinkEffects(eDex, eCon);
            eLink = EffectLinkEffects(eLink, eInt);
            eLink = EffectLinkEffects(eLink, eWis);
            ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eDaze, oPC, fDur/20);
            break;

        case 1:
            eLink = EffectLinkEffects(eCon, eInt);
            eLink = EffectLinkEffects(eLink, eWis);
            break;

        case 2:
         		 SendMessageToPC(oPC, "The alcohol has little effect on you.");
            break;
    	}
	   if (nSave < 2)
    	{
		      ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oPC);
		      ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eLink, oPC, fDur);
    }
}

}

3 Likes

The block of code you’re asking about is needed, not for the script itself, but for the purpose of making it easily moddable. I change the value of nA; I change the DC, dmg, etc. along with it. With that being said I made the script look neater; this is what it looks like atm.

Summary

void main()

{

object oA = GetItemActivator();

int nA = 20;
int nB = nA; if (nA < 10) {nB = 10;}
int nC = FortitudeSave(oA, nB, SAVING_THROW_TYPE_POISON);
int nD = nA / 5; if (nC == 1) {nD = nA / 10;}

float fA = IntToFloat(nD * 60);

effect eA = EffectAbilityDecrease(ABILITY_DEXTERITY, nD);
effect eB = EffectAbilityDecrease(ABILITY_CONSTITUTION, nD);
effect eC = EffectAbilityDecrease(ABILITY_INTELLIGENCE, nD);
effect eD = EffectAbilityDecrease(ABILITY_WISDOM, nD);
effect eE = EffectAbilityIncrease(ABILITY_CHARISMA, nD);

if (nC == 0)

{

ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eA, oA, fA);
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eB, oA, fA);
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eC, oA, fA);
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eD, oA, fA);
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectDazed(), oA, fA/20);

}

else if (nC == 1)

{

ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_FORTITUDE_SAVING_THROW_USE), oA);
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eB, oA, fA);
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eC, oA, fA);
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eD, oA, fA);
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eE, oA, fA);

}

}

Will look into linking effects later. I haven’t even fully looked through the script you posted yet as I keep having to look things up, but I’m working on it :smiley:

I am considering this:

string sNA = GetDescription(GetItemActivated);
int nA = StringToInt(sNA);

which would allow me to “sacrifice” immersion by replacing item descriptions of breweries and taste with a number (the alcohol proof %) in order to achieve the neatness of having all my different kinds of alcohol use only one script and still differ in potency.

I’m too artsy and not autistic enough to feel like it’s worth it. I want players able to read about halfling breweries and pepper flavorings. :stuck_out_tongue:

Here you want a local variable on the item.

int nA = GetLocalIn(GetItemActiaveted(), “ALCOHOL_DC”);
if (nA <= 0) nA = 20; // Or whatever you want the default to be.

Then you set the ALCOHOL_DC variable on your items to give different values. You can set them on the blueprints.

1 Like

That’s what I did in my overhaul; I just set the value to 10 if nDC was less than 10 (e.g. default 10) - as was in the original script.

1 Like

Yeah, I was talking to the OP. I may have used the wrong variable name in my example. Charles was looking for a different value for nA and was going to clobber the item description to set it when it should just be a variable.

Charles… look at the nDC part of the script pstemarie provided. If you want that based on nA you can use a variable for nA instead and recalculate the DC as you were before.

2 Likes

Thanks for the suggestions! I read up on Linked Effects and it seems that if a drinker is immune to one effect he will also be immune to the rest, so I think I’ll keep them as is for now.

Using a local variable on the item is awesome! Now I get to have nice descriptions and varying strengths with only a single script. :smiley: Thanks again.

Summary

void main()

{

object oA = GetItemActivator();

int nA = GetLocalInt(GetItemActivated(), “Proof”);
int nB = FortitudeSave(oA, nA, SAVING_THROW_TYPE_POISON);
int nC = nA / 5; if (nB == 1) {nC = nA / 10;} if (nC < 1) {nC = 1;}

float fA = IntToFloat(nC * 60);

effect eA = EffectAbilityDecrease(ABILITY_DEXTERITY, nC);
effect eB = EffectAbilityDecrease(ABILITY_CONSTITUTION, nC);
effect eC = EffectAbilityDecrease(ABILITY_INTELLIGENCE, nC);
effect eD = EffectAbilityDecrease(ABILITY_WISDOM, nC);
effect eE = EffectAbilityIncrease(ABILITY_CHARISMA, nC);

if (nB == 0)

{

ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eA, oA, fA);
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eB, oA, fA);
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eC, oA, fA);
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eD, oA, fA);
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectDazed(), oA, fA/20);

}

else if (nB == 1)

{

ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_FORTITUDE_SAVING_THROW_USE), oA);
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eB, oA, fA);
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eC, oA, fA);
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eD, oA, fA);
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eE, oA, fA);

}

else

{

SendMessageToPC(oA, "The alcohol has little effect on you.");

}

}