Condition and Action Scripts in Conversation Editor: Need sTarget parameter instead of PC/PC Party

Hello everyone,

I have been tinkering with the NWN2 toolset for the past five years without any scripting experience in hopes of creating a JRPG-style turn-based combat mod. It has been a labor of love and pain. The entire battle system is being developed as a conversation in the conversation editor. I rely entirely on the built in conversation action and condition scripts, GC_, GA_ to make everything work. I have seen other attempts to do turn-based battling, but what I am building is not based on Dungeons and Dragons rulesets so the conversation editor really allows for a lot of flexibility.

I hit a roadblock a few years that made me want to give up, though a few hours of messing with the script editor and looking at the forums last night has inspired me to ask for help.

  1. Is there a community expansion pack of GC_ and GA_ scripts? (Examples of ones I have implemented so far from the community include Sunjammer’s check_items and item count scripts, apply visual effect scripts, as well as ga_do_damage by DannJ)

  2. My main issue is with the limited parameters of the scripts. In particular, many of the scripts check the PC or the PC party instead of a tag-based sTarget. I can’t perform skill checks, saving throw checks (reflex, will, fortitude), inventory checks, or gift items to Non-PCs, but that is how I am building the system. I need to change the PC parameter to sTarget and I can’t for the life of me figure out how to successfully compile a script with those modifications.

  3. My immediate need is for:

A. ga_give_item script that can give an item to an sTarget instead of a PC and

B. for a script that can check if an item is in the inventory of an sTarget instead of a PC/Party.

C. Is there a way to modify the ga_floating_text script to display floaty text over an sTarget instead of the PC?

  1. Beyond that, I am very interested in the ways that the community has expanded this noob-friendly conversation based scripting library and any of you would be willing to share your work.

Here is a screenshot of my work-in-progress:


Any help you can offer would be immensely appreciated!

Best,

SilentFaction

A. ga_give_item_npc:

// ga_give_item_npc
/*
    Give an item to an NPC
*/

#include "ginc_param_const"

void CreateItemsOnObject(string sItemResRef, object oTarget = OBJECT_SELF, int nQuantity = 1);

void main(string sTarget, string sItemResRef, int nQuantity)
{
	// sanity check: have any parameters been left empty?
	if (sTarget == "" || sItemResRef == "" || nQuantity == 0)
	{
		PrintString("ERROR: empty parameter(s) passed to ga_give_item_npc");
		return;
	}

	object oTarget = GetTarget(sTarget);
	if (GetIsObjectValid(oTarget))
	{
		CreateItemsOnObject(sItemResRef, oTarget, nQuantity);
	}
}

void CreateItemsOnObject(string sItemResRef, object oTarget = OBJECT_SELF, int nQuantity = 1)
{
	int i = 1;
	while (i <= nQuantity)
	{
		CreateItemOnObject(sItemResRef, oTarget, 1);
		i++;
	}
}

B. gc_check_item_npc:

// gc_check_item_npc
/*
	This script checks to see if the NPC has an item in their inventory
*/

#include "ginc_param_const"

int CountItems(object oTarget, string sItemTag);

int StartingConditional(string sTarget, string sItemTag, int nQuantity)
{
	// sanity check: have any parameters been left empty?
	if (sTarget == "" || sItemTag == "" || nQuantity == 0)
	{
		PrintString("ERROR: empty parameter(s) passed to gc_check_item_npc");
		return FALSE;
	}

	int nCount = 0;
	object oTarget = GetTarget(sTarget);
	if (GetIsObjectValid(oTarget))
	{
		// sanity check: ensure target has one of this item before checking all items
		if (GetIsObjectValid(GetItemPossessedBy(oTarget, sItemTag)))
		{
			nCount += CountItems(oTarget, sItemTag);
		}
	}

	return nCount >= nQuantity;
}


int CountItems(object oTarget, string sItemTag)
{
	int nCount;
	object oItem;

	oItem = GetFirstItemInInventory(oTarget);
	while (GetIsObjectValid(oItem))
	{
		if (GetTag(oItem) == sItemTag)
		{
			nCount += GetItemStackSize(oItem);
		}

		oItem = GetNextItemInInventory(oTarget);
	}

	int nSlot;
	for (nSlot = 0; nSlot < NUM_INVENTORY_SLOTS; nSlot++)
	{
		oItem = GetItemInSlot(nSlot, oTarget);
		if (GetTag(oItem) == sItemTag)
		{
			nCount += GetItemStackSize(oItem);
		}
	}

	return nCount;
}

C. ga_floating_text_npc:

// ga_floating_text_npc
/*
	Do floaty text on NPC
*/

#include "ginc_param_const"

void main(string sTarget, string sText)
{
	// sanity check: have any parameters been left empty?
	if (sTarget == "" || sText == "")
	{
		PrintString("ERROR: empty parameter(s) passed to ga_floating_text_npc");
		return;
	}

	object oTarget = GetTarget(sTarget);
	if (GetIsObjectValid(oTarget))
	{
		AssignCommand(oTarget, SpeakString(sText));
	}
}
2 Likes

Amazing! So indebted to you Aqvilinus ! I just tested these and they are working great.

You’re welcome, @silentfaction00! It took me about 10 minutes to write these scripts, ha.
Feel free to ask if you need any other help.

2 Likes

You are surely a Wizard sent to Middle Earth to help noobs like me.

Perhaps there is a bit more you can help with…

  1. At one point I stumbled upon a GC_ script that performed a DC roll on a Creature (sTarget) that checked it’s reflex, fortitude and/or will scores. Since these are the only ”stats” that I am aware of that are not hard-coded to cap per D&D rules, I wanted to be able to use them for my battle system to calculate things like evasion chance etc. The script used to be on the vault somewhere but it is gone now :frowning:

  2. Additionally, it would be cool if the GC_ skill roll checks could apply to an NPC target instead of the PC/Party.

Thanks again! I am so excited to get working on this again.

I use custom GUIs and TLK to makeover the game to look like a JRPG in the mold of Final Fantasy or Chrono trigger

@Aqvilinus

note that the 3rd parameter is/ought be nStackSize

That is, if trying to create 2+ of an item that has a maxstacksize of 1 … that method you got there only creates one.

the stock ‘ga_give_item’ does a loop through nQuantity to account for this; that’s what got me wondering … (tested)

 
ps. Does CountItems() look inside bags? i don’t recall offhand if a loop over inventory checks inside bags (not sure if it should but …)

Didn’t know this, fixed, thanks :wink:

I think so. From NWN Lexicon:

When an item with an inventory (such as a bag of holding) is returned using the GetFirstItemInInventory and GetNextItemInInventory functions, the next call to GetNextItemInInventory will start to look inside the nested inventory (e.g. the bag of holding’s inventory).

1 Like

In my testing the item appears to be stacking past the 10 stack limit (possibly by adding items to an additional inventory slot). I made an alternate version of this script provided by @ Aqvilinus that removes items from NPCs. For some reason, the script does not remove as specified the quantity of items indicated, instead it overrides the quantity to be less than whatever is specified for the stack. So I added 11 items (max stack of 10). When I asked the script to remove 1 it removed all 10. If I ask it to remove by 1 it makes whatever the quantity of the stack is less than 1 (0). Any ideas on how to fix!

// ga_take_item_npc
/*
Take an item from an NPC
*/

#include “ginc_param_const”

void main(string sTarget, string sItemTag, int nQuantity)
{
// sanity check: have any parameters been left empty?
if (sTarget == “” || sItemTag == “” || nQuantity == 0)
{
PrintString("");
return;
}

object oTarget = GetTarget(sTarget);
if (GetIsObjectValid(oTarget))
	TakeNumItems (oTarget,sItemTag,nQuantity);

}

the stock version of TakeNumItems() is bugged, try this

// ga_take_item_npc
/*
    Take an item from an NPC
*/

#include "ginc_param_const"

void TakeItems(object oTarget, string sItem, int nQuantity);

//
void main(string sTarget, string sItemTag, int nQuantity)
{
    // sanity check: have any parameters been left empty?
    if (sTarget == "" || sItemTag == "" || nQuantity == 0)
    {
        PrintString("ERROR: empty parameter(s) passed to ga_take_item_npc");
        return;
    }

    object oTarget = GetTarget(sTarget);
    if (GetIsObjectValid(oTarget))
        TakeItems(oTarget, sItemTag, nQuantity);
}


// w/ thanks to Shadooow
void TakeItems(object oTarget, string sItem, int nQuantity)
{
    int i = 0;

    object oItem = GetFirstItemInInventory(oTarget);
    while (GetIsObjectValid(oItem) && nQuantity > 0)
    {
        if (GetTag(oItem) == sItem)
        {
            i = GetItemStackSize(oItem);
            if (i > 1 && i > nQuantity) // 1.71: proper stacked items handling
            {
                SetItemStackSize(oItem, i - nQuantity);
                return;
            }

            ActionTakeItem(oItem, oTarget);
            nQuantity -= i;
        }
        oItem = GetNextItemInInventory(oTarget);
    }
}
2 Likes

That did it! Thank you so much

1 Like

Try this:

// gc_save_dc(string sTarget, int nSavingThrow, int nDC, int nSaveType, string sTargetVersus)
/*
	Parameters:
		string sTarget       = target NPC being checked (see Target's note)
		int nSavingThrow     = saving throw int to check
		int nDC              = difficulty class to beat
		int nSaveType        = int describing saving throw type
		string sTargetVersus = target creature or object to save against (default: OBJECT_SELF)

	Remarks:
		saving throw ints
		1   Fortitude saving throw.
		2   Reflex saving throw.
		3   Will saving throw.

		saving throw type ints
		0   No saving throw type.
		1   Against mind affecting spells.
		2   Against poison.
		3   Against disease.
		4   Against fear.
		5   Against sound based effects.
		6   Against acid.
		7   Against fire.
		8   Against electical based effects.
		9   Against positive effects.
		10  Against negative effects.
		11  Against death effects.
		12  Against cold based effects.
		13  Against divine effects.
		14  Against trap type effects.
		15  Against spells.
		16  Against good effects.
		17  Against evil effects.
		18  Against law effects.
		19  Against chaos effects.
*/

#include "ginc_param_const"

int StartingConditional(string sTarget, int nSavingThrow, int nDC, int nSaveType, string sTargetVersus)
{
	// sanity checks
	if (sTarget == "" || nSavingThrow < 1 || nSavingThrow > 3 || nSaveType < 0 || nSaveType > 19)
	{
		PrintString("ERROR: invalid parameter(s) passed to gc_save_dc");
		return FALSE;
	}

	// sanity checks to prevent wrapping around
	if (nDC < 1)
	{
		nDC = 1;
	}
	else if (nDC > 255)
	{
		nDC = 255;
	}

	if (sTargetVersus == "" || !GetIsObjectValid(GetTarget(sTargetVersus)))
		sTargetVersus = TARGET_OBJECT_SELF;

	object oTarget     = GetTarget(sTarget);
	object oSaveVersus = GetTarget(sTargetVersus);

	if (GetIsObjectValid(oTarget))
	{
		if (nSavingThrow == SAVING_THROW_FORT)
		{
			if (FortitudeSave(oTarget, nDC, nSaveType, oSaveVersus) > 0)
			{
				return TRUE;
			}
		}
		else if (nSavingThrow == SAVING_THROW_REFLEX)
		{
			if (ReflexSave(oTarget, nDC, nSaveType, oSaveVersus) > 0)
			{
				return TRUE;
			}
		}
		else if (nSavingThrow == SAVING_THROW_WILL)
		{
			if (WillSave(oTarget, nDC, nSaveType, oSaveVersus) > 0)
			{
				return TRUE;
			}
		}
	}

	return FALSE;
}

// gc_skill_dc_npc(string sTarget, int nSkill, int nDC)
/*
	Determine if NPC's skill roll is successful.

	Parameters:
		string sTarget = target NPC being checked (see Target's note).
		int nSkill     = skill int to check
		int nDC        = difficulty class to beat

	Remarks:
		skill ints
		0	APPRAISE
		1	BLUFF
		2	CONCENTRATION
		3	CRAFT ALCHEMY
		4	CRAFT ARMOR
		5	CRAFT WEAPON
		6	DIPLOMACY
		7	DISABLE DEVICE
		8	DISCIPLINE
		9	HEAL
		10	HIDE
		11	INTIMIDATE
		12	LISTEN
		13	LORE
		14	MOVE SILENTLY
		15	OPEN LOCK
		16	PARRY
		17	PERFORM
		18	RIDE
		19	SEARCH
		20	CRAFT TRAP
		21	SLEIGHT OF HAND
		22	SPELL CRAFT
		23	SPOT
		24	SURVIVAL
		25	TAUNT
		26	TUMBLE
		27	USE MAGIC DEVICE
*/

#include "ginc_param_const"

int StartingConditional(string sTarget, int nSkill, int nDC)
{
	// sanity check: have any parameters been left empty?
	if (sTarget == "")
	{
		PrintString("ERROR: invalid target passed to gc_skill_dc_npc");
		return FALSE;
	}

	object oTarget = GetTarget(sTarget);
	if (GetIsObjectValid(oTarget))
	{
		int nSkillVal = GetSkillConstant(nSkill);
		if (GetIsSkillSuccessful(oTarget, nSkillVal, nDC))
		{
			return TRUE;
		}
	}

	return FALSE;
}
// gc_skill_rank_npc(string sTarget, int nSkill, int nRank)
/*
	Determine if NPC has sufficient rank in a particular skill.

	Parameters:
		string sTarget = target NPC being checked (see Target's note).
		int nSkill     = skill int to check
		int nRank      = minimum rank to return TRUE

	Remarks:
		skill ints
		0	APPRAISE
		1	BLUFF
		2	CONCENTRATION
		3	CRAFT ALCHEMY
		4	CRAFT ARMOR
		5	CRAFT WEAPON
		6	DIPLOMACY
		7	DISABLE DEVICE
		8	DISCIPLINE
		9	HEAL
		10	HIDE
		11	INTIMIDATE
		12	LISTEN
		13	LORE
		14	MOVE SILENTLY
		15	OPEN LOCK
		16	PARRY
		17	PERFORM
		18	RIDE
		19	SEARCH
		20	CRAFT TRAP
		21	SLEIGHT OF HAND
		22	SPELL CRAFT
		23	SPOT
		24	SURVIVAL
		25	TAUNT
		26	TUMBLE
		27	USE MAGIC DEVICE
*/
// BMA-OEI 9/02/05

#include "ginc_param_const"

int StartingConditional(string sTarget, int nSkill, int nRank)
{
	// sanity check
	if (sTarget == "")
	{
		PrintString("ERROR: invalid target passed to gc_skill_rank_npc");
		return FALSE;
	}

	object oTarget = GetTarget(sTarget);
	if (GetIsObjectValid(oTarget))
	{
		int nSkillVal = GetSkillConstant(nSkill);
		if (GetSkillRank(nSkillVal, oTarget) >= nRank)
		{
			return TRUE;
		}
	}

	return FALSE;
}

Bless you! This is everything I wanted and then some!

Dear Scripting Gods, Is there a ga_script that can modify a creature’s tag? Something that retrieves the tag of the creature (Get STarget by Tag) then changes the Tag to a new string. The idea is to use the script to toggle who the “target” is and reduce the number of nodes in my convo by changing the tag of the enemy instead of having a dozen paths in my conversation file. Support is greatly appreciated!!!

not sure what you mean,

a clearcut situation would be helpful, and a desired result

usually you don’t want to modify a creature’s tag … instead, perhaps, you might want a script to find a target-creature (whatever its tag is), and work with the creature-object directly

Maybe the best way to explain this is by looking at the ga_create_obj script. The script retrieves the ResRef of the Object, then gives the option to create a NewTag for the object (different than the one that corresponds to the Blueprint.) Similarly, I want to retrieve the tag of a creature, let’s say the tag is “Enemy_Leader”, then change that creature’s tag to a new tag, “Enemy_Target”. This will then allow me to use “Enemy_Target” in the Speaker or Listener nodes and for Camera placement purposes in the Conversation editor. I am designing a turn-based battling system that is a conversation file. If I can’t change the creature’s tag with a conversational action script, then I will have to have separate branching paths for all the Enemies in the formation by tag to get the cameras and effects to fire (Leader, LGuard, RGuard, LWing, RWing, SupportLead, RSupport, LSupport). I hope that makes sense!

So in review, I want a ga_ script that asks for the String of the Creature’s tag and then asks for a new string for the creature’s new tag. I want to change the creature’s tag.

1 Like

Try this:

// ga_change_tag
/*
	Finds the object with the specified tag and changes its tag to a new one.
	If there are several objects with the same tag, the first one closest
	to the conversation owner will be chosen in that case.
*/

#include "ginc_param_const"

void main(string sCurrentTag, string sNewTag)
{
	if (sCurrentTag == "" || sNewTag == "")
	{
		PrintString("ERROR: empty parameter(s) passed to ga_change_tag");
		return;
	}

	object oTarget = GetTarget(sCurrentTag);
	if (GetIsObjectValid(oTarget))
	{
		SetTag(oTarget, sNewTag);
	}
}
2 Likes

And the Gods never fail me. Thank you! Loving this community.