Trying to disable "remains" from dropping in OC

I have commented out the “CraftDropItems” function in the nw_c2_default7 script and placed it in my documents nwn2 override folder. I have some other custom functions in that script for custom drops and it is working throughout the original campaign.

There is a section in the Eyegouger Approach area where 4 orcs are standing behind some barricades. When I kill those orcs three “remains” bags are created.

I have server shout code in place that tell me if there is a special death script and there is none detected for those orcs.

Been struggling with this for a while now and I don’t know where to look next.

This is a slightly time consuming option but if you don’t want any random drops then open each module with the toolset, press the view tab then module properties and finally properties. Scroll down until you find variables. Open it up by pressing the three dot box select add and put in…

X2_L_NO_TREASURE for the name and set the Value int to 1 then save it and go on to the next module.

1 Like

Just make an NSS script in your /override folder that sets the variable and run it from the console command

void main()
{
     SetLocalInt(GetModule, X2_L_NO_TREASURE, 1);
}
1 Like

These are good solutions but not something I want to do.

I would like to know what script is causing the remains to appear even though CraftDropItems is commented out in the default7.

void main()
{
	object oCreature = OBJECT_SELF;
	string sDeathScript = GetLocalString(oCreature, "DeathScript");


		AssignCommand(GetModule(), SpeakString("onDeath Creature: " + GetName(oCreature), TALKVOLUME_SHOUT));
		AssignCommand(GetModule(), SpeakString("onDeath Creature Tag: " + GetTag(oCreature), TALKVOLUME_SHOUT));
		AssignCommand(GetModule(), SpeakString("Special Script: " + sDeathScript, TALKVOLUME_SHOUT));
		
		if (sDeathScript != "")
			{
			ExecuteScript(sDeathScript, oCreature);
			}

	AssignCommand(GetModule(), SpeakString("craft drop disabled", TALKVOLUME_SHOUT));
   	//craft_drop_items(oCreature);

	

	//Additional code for custom auto-loot system.
	object oPC = GetLastKiller();
	object oItem, oCopy;
	int iBaseType;
	
	oItem = GetFirstItemInInventory(oCreature);
	while(GetIsObjectValid(oItem))
		{
		iBaseType = (GetBaseItemType(oItem));
		if (iBaseType == BASE_ITEM_CBLUDGWEAPON
			|| iBaseType == BASE_ITEM_CGIANT_AXE
			|| iBaseType == BASE_ITEM_CGIANT_SWORD
			|| iBaseType == BASE_ITEM_CPIERCWEAPON
			|| iBaseType == BASE_ITEM_CREATUREITEM
			|| iBaseType == BASE_ITEM_CSLASHWEAPON
			|| iBaseType == BASE_ITEM_CSLSHPRCWEAP)
			{
			AssignCommand(GetModule(), SpeakString("Destroying Creature Item: " + GetName(oItem), TALKVOLUME_SHOUT));
			DestroyObject(oItem); 
			}
		else
			{
			AssignCommand(GetModule(), SpeakString("Copying Creature Item to PC: " + GetName(oItem), TALKVOLUME_SHOUT));
			oCopy = CopyObject(oItem, GetLocation(oPC), oPC);
			DestroyObject(oItem);  
			}
        oItem = GetNextItemInInventory(oCreature);
		}   
}

Check the creature’s OnSpawn script (nw_c2_default9). Random treasure is generated from there. That’s also where the X2_L_NOTREASURE switch is checked.

Looking at the default_9 I am not seeing anything other than the GenerateTreasure function, in my test code all inventory items are either deleted or transferred to the PC on death. That code is working as intended.

Looking at the x2_inc_compon script now and I see items are created in that script. Running some tests…

        // * if does not have an inventory then treat as a door
        if (GetHasInventory(OBJECT_SELF) == FALSE) bDoor = TRUE;
        
        // * appearance type is index into the 2da
        string sResRef = Get2DAString("des_crft_drop", sCol, nAppearance );
        string sNum = Get2DAString("des_crft_drop", sNumCol, nAppearance);
        if (sNum != "****" && sNum != "") nNum = StringToInt(sNum);
        if (sResRef != "****" && sResRef != "")
        	{
            int i = 1;
            location lLoc = GetLocation(OBJECT_SELF);
            // * By default only spawn 1 of each unless otherwise indicated
            for (i=1; i<=nNum; i++)
				{
				if (bDoor == TRUE)
					{
					AssignCommand(GetModule(), SpeakString("is door = TRUE", TALKVOLUME_SHOUT));
					AssignCommand(GetModule(), SpeakString("resref of item: " + sResRef, TALKVOLUME_SHOUT));
					AssignCommand(GetModule(), SpeakString("OBJECT_SELF = " + GetName(oSelf), TALKVOLUME_SHOUT));
					CreateObject(OBJECT_TYPE_ITEM, sResRef, lLoc);
					}
				else
					{
					AssignCommand(GetModule(), SpeakString("is door = FALSE", TALKVOLUME_SHOUT));
					AssignCommand(GetModule(), SpeakString("resref of item: " + sResRef, TALKVOLUME_SHOUT));
					AssignCommand(GetModule(), SpeakString("OBJECT_SELF = " + GetName(oSelf), TALKVOLUME_SHOUT));
					CreateItemOnObject(sResRef, oSelf); // * create item on object's inventory
					}
				}
	        }

There is some other script creating the remains bag.

I set SetLocalInt(GetModule, X2_L_NO_TREASURE, 1);
I placed a return statement right after the voidmain() in the default 7, default 9 and in the inc_compon scripts.

Remains are still being created.

I cant edit the modules from the OC cause it crashes my toolset when trying to load (Windows 7 64 - GF GTX970). I had a hard time getting it to run at first, DX and DotNet stuff. It will load my module with the edited scripts and I am able to compile scripts just fine. I did an install on another Windows XP box and its worse. Game wont run and toolset crashes when trying to load an OC module. I ordered an AGP video card for that machine so in the mean time I will keep at it until I figure it out. I am thinking it has something to do with the creature tags and the area but I wont know until my toolset is able to open the OC Modules.

Those 4 orcs are preplaced in the area and equipped with StuddedLeather, Light xbow, and Bolts. On 3 of the 4 orcs, those items are flagged Droppable …

1 Like

Thanks for the info, I tried this code but the remains still appeared. Then again I will try again in the morning after I get some sleep.

void AutoLoot(object oCreature, object oPC)
{
	object oItem, oCopy;
	int iBaseType;
	
	oItem = GetFirstItemInInventory(oCreature);
	while(GetIsObjectValid(oItem))
		{
		iBaseType = (GetBaseItemType(oItem));
		if (iBaseType == BASE_ITEM_CBLUDGWEAPON
			|| iBaseType == BASE_ITEM_CGIANT_AXE
			|| iBaseType == BASE_ITEM_CGIANT_SWORD
			|| iBaseType == BASE_ITEM_CPIERCWEAPON
			|| iBaseType == BASE_ITEM_CREATUREITEM
			|| iBaseType == BASE_ITEM_CSLASHWEAPON
			|| iBaseType == BASE_ITEM_CSLSHPRCWEAP)
			{
			DestroyObject(oItem); 
			}
		else
			{
			if (GetDroppableFlag(oItem)) SetDroppableFlag(oItem, FALSE);
			oCopy = CopyObject(oItem, GetLocation(oPC), oPC);
			DestroyObject(oItem);  
			}
		oItem = GetNextItemInInventory(oCreature);
		}
}

@4BOLTMAIN

EDIT: I just looked at your script again, and it looks like you are trying to place any items carried by a creature directly onto a PC that kills it. Is that what you actually want?

If it is this, then that is a bit more complicated and reminds me of the issues I had when I designed my own creature OnDeath routines. I ended up having to jump through some loops to get what I was after, but eventually achieved it. I think some of the design I used may help you here too. It involved firing another script that handled the copying of the items involved, and may well require more work than first realised, especially when trying to do it to the OC scripts.

I would look at placing SetLootable FALSE on creatures prior any death. I.e. on their OnSpawn script.

It’s been a while since I have had to work much with the OnDeath, but I recall there being some other things you have to consider regarding the way creatures drop their “treasure”.

Also, your script implies you are being more specific about what is dropped than stopping it completely, perhaps?

Basically, however, (as others have alluded to also) I believe you need to address this at time of spawn, unless these creatures acquire other gear after spawning. If so, then you should be able to do something like you do for their OnDeath … but do not delay this function in any way or it will have “dropped” before being destroyed.

I quickly copied and pasted an edited version of my own function from my own monster on death … Sorry if it looks a bit messy. (I tidied it a bit.) This function QuickFix is called without any delay. NB: It addresses creature items that somehow turned droppable and items I had already assigned as non-drop.

If you wanted to destroy everything, then just remove the condition check.

////////////////////////////////////////////////////////////////////////////////////////////
// FIX CREATURE ITEMS TURNING DROPPABLE WHEN PLACED IN AREA - CANNOT BELIEVE IT!
// NEEDED HERE TO IN CASE CREATURE POLYMORPHED BEFORE IT DIED!
////////////////////////////////////////////////////////////////////////////////////////////
void QuickFix();
void QuickFix()
{		
	object oItem = GetFirstItemInInventory(OBJECT_SELF);	
	
	while(oItem != OBJECT_INVALID)
	{		
		int iDESTROY = GetBaseItemType(oItem);
		
		// THIS MAY BE SUPERFLUOUS NOW AS I SORTED THE ONSPAWN BETTER NOW
		if((iDESTROY > 68 && iDESTROY < 74) || GetDroppableFlag(oItem) == FALSE)
		{
			SetPlotFlag(oItem, FALSE);
			DestroyObject(oItem, 0.01, FALSE);	
		}			
		
		oItem = GetNextItemInInventory(OBJECT_SELF);
	}	
}
1 Like

Good morning and thanks for the help.

The auto loot code that I posted above works on “most” creatures. so today I will start with disabling that function and then adding some server tells that will show me a list of all the items in the creatures inventory when killed.

I wrote some code last night that I can run after the area is cleared and it tells me what objects remain in the area have inventory, what object type they are and also what items are in their inventory.

I discovered 3 things when doing this…

  1. Remains is OBJECT_TYPE_PLACEABLE and not OBJECT_TYPE_ITEM
  2. When I put a loop to cycle through the objects inventory inside the loop cycling through the objects in the area it crashes my game. As you mentioned above I had to create a separate function for that.
  3. When “script looting” the objects left in the area the remains bag can only be destroyed after setting the plot flag to false.

I am not quite as frustrated today so I should be able to make more progress and get more information to accomplish my goal.

1 Like

Yes, these are the sort of things I am on about when it comes to creature drops. Items are being moved to placeable loot bags. But, the “lootable corpse” property also has a bearing, I believe. (It’s been a long time since I looked into this.)

I had to have a separate placeable object created to handle an extra script firing to manage other OnDeath stuff (at the time of creature death). (*)

(*) I needed extra script activity after a creature died, and this was a way around it. That is, once a creature has died, and moved its treasure, you may need more time to run scripts associated with this creature and/or its drop. Otherwise, the OnDeath script is often over before you need it to do something.

However, it sounds like you are on the right track now.

The crossbows and arrows on the orcs are not showing in their inventory like the Blade Spider Leg’s do on the spiders. I am going to move the loop to the onspawn and use the WriteTimestampedLogEntry function.

I think I have a better understanding of what you are doing.
Try this:

void StartLooting(object oItem, object oPC)
{
	if (GetDroppableFlag(oItem))
	{
		int iBaseType =	GetBaseItemType(oItem);
		
		if (iBaseType == BASE_ITEM_CBLUDGWEAPON
			|| iBaseType == BASE_ITEM_CGIANT_AXE
			|| iBaseType == BASE_ITEM_CGIANT_SWORD
			|| iBaseType == BASE_ITEM_CPIERCWEAPON
			|| iBaseType == BASE_ITEM_CREATUREITEM
			|| iBaseType == BASE_ITEM_CSLASHWEAPON
			|| iBaseType == BASE_ITEM_CSLSHPRCWEAP)
		{
			DestroyObject(oItem);
		}
	
		else
		{
			CopyObject(oItem, GetLocation(oPC), oPC);
			DestroyObject(oItem);  
		}
	}
}

void AutoLoot(object oCreature, object oPC)
{
	int n;	
	object oItem = GetFirstItemInInventory(oCreature);
	
	while(GetIsObjectValid(oItem))
	{
		StartLooting(oItem, oPC);
		oItem = GetNextItemInInventory(oCreature);
	}

	for (n=INVENTORY_SLOT_HEAD; n<=NUM_INVENTORY_SLOTS; n++)
	{
		StartLooting(GetItemInSlot(n, oCreature), oPC);		
	}
}
1 Like

Thanks for the response. The auto loot function I wrote works as intended on most creatures. The first 4 orcs in Eyegouger Approach drop 3 remains bags, 2 with crossbows one one with arrows. Ive been working on this issue for the last couple days and I am pretty sure those items are not in the creatures inventory. I have placed return; statements immediately after the void main() in the default 7, the default 9 and also the x2_inc_compon (to ensure the craft_drop_items() does not fire) and the remains bags are still being created.

I am struggling with finding out how and why these bags are being created so I can prevent it from happening. Keep in mind that I am unable to open OC modules, my toolset crashes. I am able to open my own module with the edited scripts so I am utilizing code to try and get this information.

Did a bit of testing…Found some flaws in your script:

  1. It only checks items in the inventory, not equipped items.
  2. It takes inventory items that are not flagged as droppable.
  3. It does not take gold.

That being said, those left-over loot bags were likely equipped items that were flagged as droppable.

I adjusted your script, and it worked quite well:

void StartLooting(object oItem, object oPC)
{
	if (GetDroppableFlag(oItem))
	{
		int iBaseType =	GetBaseItemType(oItem);
		
		if (iBaseType == BASE_ITEM_CBLUDGWEAPON
			|| iBaseType == BASE_ITEM_CGIANT_AXE
			|| iBaseType == BASE_ITEM_CGIANT_SWORD
			|| iBaseType == BASE_ITEM_CPIERCWEAPON
			|| iBaseType == BASE_ITEM_CREATUREITEM
			|| iBaseType == BASE_ITEM_CSLASHWEAPON
			|| iBaseType == BASE_ITEM_CSLSHPRCWEAP)
		{
			DestroyObject(oItem);
		}
	
		else
		{
			CopyObject(oItem, GetLocation(oPC), oPC);
			DestroyObject(oItem);  
		}
	}
}

void main()
{
	object oCreature = OBJECT_SELF;
	string sDeathScript = GetLocalString(oCreature, "DeathScript");

	AssignCommand(GetModule(), SpeakString("onDeath Creature: " + GetName(oCreature), TALKVOLUME_SHOUT));
	AssignCommand(GetModule(), SpeakString("onDeath Creature Tag: " + GetTag(oCreature), TALKVOLUME_SHOUT));
	AssignCommand(GetModule(), SpeakString("Special Script: " + sDeathScript, TALKVOLUME_SHOUT));
		
	if (sDeathScript != "")
	{
		ExecuteScript(sDeathScript, oCreature);
	}

	AssignCommand(GetModule(), SpeakString("craft drop disabled", TALKVOLUME_SHOUT));
   	//craft_drop_items(oCreature);

	object oPC = GetFirstPC(FALSE);
	object oItem = GetFirstItemInInventory(oCreature);
	int n;
		
	while(GetIsObjectValid(oItem))
	{
		StartLooting(oItem, oPC);
		oItem = GetNextItemInInventory(oCreature);
	}

	for (n=INVENTORY_SLOT_HEAD; n<=NUM_INVENTORY_SLOTS; n++)
	{
		StartLooting(GetItemInSlot(n, oCreature), oPC);		
	}
	
	int nGold = GetGold(oCreature);
	
	if (nGold > 0) AssignCommand(oPC, TakeGoldFromCreature(nGold, oCreature));
}
2 Likes

Thank you for your help on this!

My mistake was assuming that GetFirstItemInInventory loop included the equipped items.

No more loot bags!

After testing for a bit I decided against copying the equipped items to the player because it seems every orc has leather armor and either a sword or a crossbow…lol!

If I ever decide to share these scripts for my version of a faster paced OC your name is mentioned for credit in the script. Thanks again.

4 Likes

just for reference →

// Duplicates the object specified by oSource.
// ONLY creatures and items can be specified.
// If an owner is specified and the object is an item, it will be put into their inventory
// If the object is a creature, they will be created at the location.
// If a new tag is specified, it will be assigned to the new object.
object CopyObject(object oSource, location locLocation, object oOwner = OBJECT_INVALID, string sNewTag = "");

// duplicates the item and returns a new object
// oItem - item to copy
// oTargetInventory - create item in this object's inventory. If this parameter
//                    is not valid, the item will be created in oItem's location
// bCopyVars - copy the local variables from the old item to the new one
// * returns the new item
// * returns OBJECT_INVALID for non-items.
// * can only copy empty item containers. will return OBJECT_INVALID if oItem contains
//   other items.
// * if it is possible to merge this item with any others in the target location,
//   then it will do so and return the merged object.
object CopyItem(object oItem, object oTargetInventory=OBJECT_INVALID, int bCopyVars=FALSE);
2 Likes

@kevL_s

Yes, a good point, especially when variables need to be preserved on the items concerned in any copies.

I am familiar with the difference. For example in NWN you cant use CopyItem for storing or retrieving gold in a container by scripting, you have to use CopyObject. I will assume it is the same for NWN2.

1 Like