PC drops inventory while unequipping weapons (OC, Act III, West Harbor)

Howdy,

Long story short, the PC drops items in the inventory onto the ground when unequipping held weapons, even if there are still inventory slots open.

The Problem:

A player may not know their items are on the ground.” (Zhjaeve, DR 1374)

Not game braking. Just have to remember to pick the item(s) back up after the cutscene. Afterall, the PC walks to The Scar under duress and sits down, the Silver Sword is reformed, then, battle. That’s a lot of distraction.

When:
Act III, in West Harbor, while walking toward “The Scar”, when entering the trigger that starts the cutscene to reform the Silver Sword.

What happens:
*The cut scene starts, the PC animation for digging in inventory can be seen, then, you can hear and see the inventory bag hitting the ground.
*After the cutscene has ended, the items in an inventory bag (body bag) can be seen and accessed on the ground where the PC was standing during the beginning of the cutscene.
*The PC inventory shows the first one or two items missing, depending on how many weapons the PC is carrying and how many spaces there are in the first pouch of Inventory.

Info:
3000_Neverwinter_A3.mod
3052_westharbor (area)
Possible script: 3052a_cut_scar
Possible conversation: 3052_cut_scar

Pertinent Info:
* “Override” folder = Empty. Also occurs if using LCountach’s “Big Bag Inventory” v1.00 (One of the top five mods)
*Running the game from “nwn2main.exe” or “NWLauncher.exe” (NWN2 Client Extension)

The Save:
000366 - A3 WH B4 Scar.zip

A few test results:

DUAL WIELDING
Item bag produced:

 *Play State-Inventory sorted, first 104 inventory slots have items, last 24 inventory slots are empty (slots 105-28).
 Results:  Equipped weapons are in inventory slots 105 and 106.  Item in first inventory slot is placed on ground.  Silver Shard slots are empty.

 *First 3 slots emptied after inventory sorted (no Silver Shards in first pouch (not in inventory slots 1-24)).
 Results:  Equipped weapons are in inventory slots 1 and 2. One inventory item is placed on the ground).

 *First 4 slots emptied after inventory sorted (no Silver Shards in first pouch (first 32 inventory slots)).  Two Silver Shards in inventory slots 97 and 98.  Inventory slots 105-128 are empty.
 Results:  Equipped weapons are now in inventory slots 1 and 2.  Slots 3-5 are empty. One item is placed on ground.  Slots 97 and 98 are empty.  Slots 105-128 are empty.

Item bag not placed on the ground:

 *First 3 slots emptied, all seven Silver Shards are placed within inventory slots 4-32 (Silver shards are in slots 4-10), last 24 inventory slots are empty (105-128).
 Results:  Equipped weapons are in inventory slots 1 and 2.  Silver Shard inventory slots are empty.

 *First 4 slots emptied after inventory sorted, Silver Shards in slots 86, 88, 91-95 (none in first pouch (first 32 slots)), no items in last 32 slots (97-105).
 Results:  Equipped weapons are now in slots 1 and 2 of of inventory. Slots 3 and 4 are empty.  Silver Shard slots are empty.

ONE WEAPON WIELDING

Item Bag Produced:

 *Play State-Inventory sorted, first 105 inventory slots have items, Silver Shards are in slots 86, 88, 91-95, last 23 inventory slots are empty (slots 106-28).
 Results:  Equipped weapon is in inventory slots 106 and 106.  Slot 1 is empty.  Item is on the ground (taken from slot 1).  Silver Shard slots are empty.

 *First 2 slots emptied after inventory sorted (no Silver Shards in first pouch (not in inventory slots 1-24)).
 Results:  Equipped weapon is in inventory slot 1. Slots 2 and 3 are empty. One inventory item is placed on the ground.

~ Maybe it’s just me :wink:

1 Like

It’s an issue in the script 3052a_cut_scar where it checks if you have 127 or more items, and if so, drops the first one on the ground - the problem is that it also counts all items inside bags, so it’s not hard to have an amount of items exceeding the amount of inventory spaces in your primary inventory.

Here’s an alternative version that you can drop in your override directory that does things a bit differently; instead of checking if inventory is full, it’ll create the silver sword regardless of inventory space, and if inventory is full, it will drop to the ground. It then checks if PC has the sword, and if not, drops the first item in inventory, picks up the silver sword and equips it, then picks up the item that was dropped.

Testing it, it seems to work fine, with the minor issue that, if inventory is full, the sword does not display properly in CS when holding it up, but that’s still preferable to potentially not noticing lost items, especially bags (since they take up the first slots if using auto sort).

3052a_cut_scar.7z (37.4 KB)

Relevant changes are in the UnequipPC and CreateAndEquipSilverSwordOnPC functions.

5 Likes

@Akhacha @Axe_Edge

i hope to have a look at this and do somethin for Nwn2Fixes … sry no eta

2 Likes

@Akhacha

Very cool. Thanks for looking into it. I’ve downloaded your fix and will give it a try. There’s no telling how much cool loot has been left on that hillside over the past 15 years.

@kevL_s

This is definitely one for NWN2Fixes :wink:

There is a bit more to it, though. Without Akhacha’s fix, I ran some more tests.

 *Duel Wielding, first few inventory slots filled with silver shards, only the last 23 slots are empty.
 Results:  PC digs through inventory, an item can be heard being discarded as the PC bends down, but a loot bag would not be produced.

It’s interesting how the script produces a loot bag if the silver shards are not in the first six inventory slots (but they are still within the first 24 slots), yet, a loot bag is not produced if the silver shards are in the first six inventory slots.

“Know this game never ceases to surprise the great Zerthimon. Even the original forger of this sundered Silver Sword, now reborn, cannot foretell challenges this game has remaining. Know, this game still breathes, contemplates, and lies in wait until able to release a great puzzle to be overcome. Know…this game…still…LIVES.”
(Zhjaeve, DR 1389)

2 Likes

That seems expected - the script destroys the silver shards present in the module, even if not in inventory. If a shard is in the first inventory slot, it will be put on the ground, and then destroyed, leaving no loot bag.

Yeah, as to be expected for the inventory slots.

What I think is funny is the PC is seen bending down and doesn’t drop anything.

As long as the script is cycling through the inventory to find silver shards, and the position of the shards can be known, why not place the objects into the vacated shard slots. Or cycle a second time to find the vacated slots when placing things into the inventory (I guess this would be the normal script for placing objects into the inventory).

:slight_smile:

The problem with that is that there’s no guarantee that the PC has the shards in their inventory, since you can give them to party members, or they could be located in a bag.

1 Like

Nwn2fixes part2 - #58 by kevL_s

2 Likes

Does anyone know (for sure) whether or not player is allowed to change the Party after this point in the OC plot?

The thing is … this cutscene destroys all shards (or it’s supposed to). But if a shard was given to a companion that is no longer in the party, it won’t be destroyed along with the others (not with the current script). However, it’s expensive to spawn/despawn roster members to check them for shards (noticeable freeze for several seconds).

So id rather not, but will if player can stash shards on companions, despawn them from the Party and do this cutscene, then respawn them as members of the Party …

2 Likes

Howdy!

Your post reminded me that I did notice a shard in someone’s inventory well after The Scar. I remembered having it while battling the KoS. Today, I went looking and found the missing shard.

In my save above (party is at The Scar), Ammon Jerro is not in the party and he has a silver shard (fire and ice resistance) in his inventory. Ammon Jerro keeps it. The PC has seven shards.

And yes, the player is allowed to change the party after The Scar. There is plenty left for the party to do and the party travels a few more times, changing members if needed.

Today, for the heck of it, I loaded the save (at The Scar), turned the party around without triggering the forging cut scene, and used a world transition to return to Crossroad Keep. I was able to change the party out prior to returning to The Scar to forge the sword.

During the playthrough, when I did notice the shard after The Scar, I just remembered Z commenting earlier that the party had enough shards to forge the sword. Plotwise, the party knew they wouldn’t recover all of the shards to the silver sword. Mechanically, I knew the game intended for the PC to have all of the shards given during the game. Story wise, I just accepted that I had enough shards to reform the sword.

I’ll think on a way to deal with a wayward shard.

Today was all done with the original scripts.

:slight_smile:

2 Likes

excellent, Axe. that’s what i wanted to know …

yes that’s my testcase. In the scripted routine I have now, a count is kept for how many shards it destroys (8 total) and if all shards are ingame, no need to start spawning/despawning the roster … so that’s fast. Currently the routine finds 7 shards and pops Ammon (he’s the first char on the roster), finds the 8th shard and that’s fast too.

If player did not find all 8 shards then the routine has to pop all ~10 nonparty roster members … but i got it hidden behind the fade-to-black at the start of the cutscene, so it’s not that bad.

 
 
Shards can, however, also be stashed in chests etc. My routine will find and destroy only stashed-shards in the 3000 module (and on the roster). A global var should perhaps be implemented like “scar_done_destroy_shards” and checked when player/pc/party load into other modules …

 

yeh but that’s not the spirit of it,

2 Likes

Smart!

1 Like

@kevL_s

Is this not an ideal place to use GetObjectByTag?

I mean if the tags are known, won’t it eliminate any need for greater search routines … Or have I missed something obvious? … Which is quite likely. :wink:

hi Lance, interesting question … buckle up :)

Think about a rostermember who is despawned (aka not in the party, not at a hangout waypoint). It’s not in the Module-object.

All its items should not be in the Module-object. It and its inventory have despawned … all that stuff is saved to a .ROS file and should no longer be extant in the Module-object.

But my (rather intensive) testing and debugging indicates that it is, if the companion/rostermember was despawned in the currently loaded module. Yeah … if I give a shard to a companion and despawn the companion then GetObjectByTag() still gets an instance of the shard GetIsObjectValid(oShard) ( but there is no possessor GetItemPossessor(oShard), neither creature nor placeable container, and it’s not in an area GetArea(oShard) – this strikes me as a hardcoded bug ).

If I destroy that (fake) instance of a shard, then respawn the rostermember with (valid) shard, he still has it.

So i have to do some tomfoolery to discount fake shards as they are tallied up to 8 total.

 
Note that Ammon Jerro has a shard (in @Axe_Edge’s test save) – but his shard does not get counted (or appear as fake) since he is not spawned (and assumedly since he was not despawned in the currently loaded module – he is not in the Party when the save loads).

eg. In the following debugchat, shards #5 and #6 were given to Casavir and he was removed from the party (replaced by Bishop) before the cutscene. Those 2 shards are valid but have no possessor nor are they in an Area … but when Casavir is then respawned and checked for shards he has them also

- note that Ammon is not in the party and he has a shard but it is not found with GetObjectByTag() – it does get found properly, however, when he is spawned and checked for shards

3 Likes

aw why not, have some code ;)

const string SHARD_TAG_PRE = "nwn2_it_shard";
const int    SHARDS_TOTAL  = 8;

void tell(string sTell)
{ SendMessageToPC(GetFirstPC(FALSE), sTell); }

// kL helper for DestroyShardsRoster_start()
// Finish has to be delayed to give time for DestroyObject(oShard) to happen
// before saving out the character.
void DestroyShardsRoster_finish()
{
	tell("DestroyShardsRoster_finish()");
	object oRoster;

	string sRoster = GetFirstRosterMember();
	while (sRoster != "")
	{
		oRoster = GetObjectFromRosterName(sRoster);
		if (!GetIsObjectValid(GetFactionLeader(oRoster))) // 'oRoster' is not in the Party ->
		{
			SetScriptHidden(oRoster, FALSE);
			DespawnRosterMember(sRoster);
		}
		sRoster = GetNextRosterMember();
	}
}

// kL helper for DestroyShardsRoster_start()
int DestroyShardsInventory(object oTarget)
{
	tell("DestroyShardsInventory() " + GetName(oTarget));
	int tally = 0;

	object oShard;

	int i = 0;
	while (++i <= SHARDS_TOTAL)
	{
		oShard = GetItemPossessedBy(oTarget, SHARD_TAG_PRE + IntToString(i));
		if (GetIsObjectValid(oShard))
		{
			++tally;
			tell(". " + GetTag(oShard) + " " + GetName(GetItemPossessor(oShard)));
			DestroyObject(oShard, 0.f, FALSE);
		}
	}
	tell(". . shards= " + IntToString(tally));
	return tally;
}

// kL helper for DestroyShards()
// Those geniuses forgot that a despawned roster character could have shard(s)
// (and that shards can be stashed in containers in other Modules).
void DestroyShardsRoster_start(int tally)
{
	tell("DestroyShardsRoster_start()");
	object oRoster;

	location loc = GetLocation(GetPCSpeaker());

	string sRoster = GetFirstRosterMember();
	while (sRoster != "")
	{
		if (!GetIsObjectValid(GetObjectFromRosterName(sRoster)))
		{
			oRoster = SpawnRosterMember(sRoster, loc);
			SetScriptHidden(oRoster, TRUE);

			if ((tally += DestroyShardsInventory(oRoster)) >= SHARDS_TOTAL)
				break;
		}
		sRoster = GetNextRosterMember();
	}
	tell(". shards found total= " + IntToString(tally));
	DelayCommand(0.f, DestroyShardsRoster_finish());
}

// kL rewrite
// note: silver shards cannot be dropped on the ground. See 'k_mod_unacquire' in
// the OC Campaign folder.
void DestroyShards()
{
	tell("DestroyShards()");
	int tally = 0;

	object oShard;

	int i = 0;
	while (++i <= SHARDS_TOTAL)
	{
		oShard = GetObjectByTag(SHARD_TAG_PRE + IntToString(i));
		if (GetIsObjectValid(oShard))
		{
			object oPossessor = GetItemPossessor(oShard);
			if (GetIsObjectValid(oPossessor))
			{
				tell(". " + GetTag(oShard) + " " + GetName(oPossessor));
				// its on party that are despawned (in the current Module)
				// can remain as 'loose' its in the Module in addition to
				// being saved out on the roster character
				//
				// so don't count those loose/rogue its; count only its
				// in containers
				++tally;
			}
			else
			{
				object oArea = GetArea(oShard);
				if (GetIsObjectValid(oArea))
				{
					tell(". " + GetTag(oShard) + " " + GetName(oArea) + " ( " + GetTag(oArea) + " )");
				}
				else
					tell(". " + GetTag(oShard) + " AREA INVALID");
			}

			DestroyObject(oShard, 0.f, FALSE);
		}
	}
	tell(". . shards found= " + IntToString(tally));

	if (tally < SHARDS_TOTAL)
	{
		SendMessageToPC(GetPCSpeaker(), "<c=crimson>Checking roster for Shards ...</c>");
		DelayCommand(0.f, DestroyShardsRoster_start(tally));
	}
}
3 Likes

Puts on Devil Horns

During a multi player session, a shard is given to a PC (other than the main PC), then, the player of the shard bearing PC doesn’t make it to The Scar that day.

Devil Horns look nice
:slight_smile:

Other than Main PCs are listed in a save.

1 Like

:disguised_face:

yeh… the original designers should have tracked their shards better… agreed

( there should have been checks done in the OnEnter handlers of module etc to see if the Scar is done; if so, destroy any shards on PCs entering – this could handle both PCs and/or respawning companions* )

/mehbulehgh

 
* as well as shards that were left behind in chests (in other Modules) …

i could look into it, depends on what Modules are still accessible after The Scar

Where is IRAPS when you need him?

Here is a list of places accessible after forging the silver sword at The Scar.

Areas marked with an asterisk (*) are reachable by using the World Map immediately upon leaving The Scar.
Areas within areas are not necessarily listed.
This information is taken from my last playthrough, as well as opening up areas to look for transitions.
The encounters made while traveling seem to be within the modules listed, but some encounter areas may have been missed. I hope a PC wouldn’t leave a shard in an area where a “random encounter” occurred.
I’ll keep looking.

2100_Crossroad_Keep_A2
      Shandra's Farm     {3070}sh_farm     WM
     *Nolaloths Valley     3090_avalley     World Map(WM)
     *Strange Clearing     3190_forest     WM/(tr_3190_to_2100)

2300_Crossroad_Keep_Adv.mod
     *Crossroad Keep     2300_ck_farm     WM/p_2300_to_2310

2200_Port_Llast.mod
     *Port Llast     2200_portllast     WM/WM/WM   
     *Ember     2210_ember     WM/WM
      Duskwood Grove     2211_duskwood     WM/22_tr_duskwood_to_caverns
      Well     2220_cavernwell     dr_2220_to_2222
      Caverns     2222_cavernupper    dr_2222_to_2221/22_tr_caveexit_cl

2400_Illefarn_Ruins.mod
     *Ruins of Arvahn     2410_ogruins     WM/tr_30_zh_portal/2410_tr_protal_cl

3000_Neverwinter_A3.mod
     *Highcliff     {3010}highcliff     WM
     *Ironfist Stronghold     {3030}ir_strong     WM
     *Mount Galardrym    {3031}galardrym  WM/3032_from_3031/3033_from3031 
     *Circle of the Mere     {3041}cot_mere     WM
     *Guardian Ruins     {3051}guardruins
      WestHarbor(Scar)     {3052}westharbor     tr_to_3051/tr_to_3051    
     *Merchant Quarter     {3063}merchant     WM

3400_Merdelain.mod
      Vale of Merdelain     3400_gauntlet     3430_wp_entrance
      Inner Sanctum     3430_rsanctum

3500_Crossroad_Keep_Siege.mod
1 Like

@kevL_s

Yes, this is what we managed to conclude as a result of this post. :slight_smile:

And it sounds like the same issue I was experiencing when trying to track specific plot items. However, (if you recall), your suggestion of changing the TAG (using SetTag) at the time of each item discovery using the GetObjectByTag helped me to be able to determine when an object was specifically valid or not … and then destroy (or ignore) an “invalid” version when/if required.

This shard issue sounds similar to the one I was having? :thinking: Is not a similar change TAG option possible?

That would make recognising valid or invalid shard versions easier, wouldn’t it?

However, without looking more closely at the details, maybe there is a difference I missed. Perhaps SetTag cannot be used on an item not yet considered within a valid area or ownership in this case?

I have a lot of this (code below) for plot items … which destroys this instance of the object and makes the TAG for the item no longer valid, and thereby no longer findable … Also, bear in mind that the “item object” then completely disappears between reloads anyway. (i.e. It recognises it has been destroyed by the time we reload a game. The TAG change is just a temporary stop gap to prevent GetItemByTag from finding it incorrectly before a game reload. (*) )

SetPlotFlag(oItem, FALSE);
SetTag(oItem, "BYTAGFIX");
DestroyObject(oItem, 0.0, FALSE);

If you wanted to be more item specific (if shard tags or properties varied), then maybe use a new tag such as BYTAGFIX_ORIGNINALSHARDTAG and use FindSubString for special checks and or conditions?

(*) When a roster member is respawned, you can then destroy any BYTAGFIX items if required (having been set when asked to leave the party), or check if it is still required and us SetTag to correct it back again (if or when brought back into the party).

Although, another option I have with plot items, is to transfer all plot items to the Main PC from a despawned companion at time of despawning them. This is probably one of the easiest options, but it depends upon how the shard is setup for transfer / droppable etc.

1 Like

whoosh, I forgot about that

will have a think tomorrow

and look more at Axe’s stuff …

1 Like