Useful Odds and Ends

Information and Discussion Relating to NWN2 Scripting and Coding

Moderators: Moderator, Developer, DM

User avatar
gedweyignasia
Custom Content
Posts: 1353
Joined: Sat Nov 23, 2013 11:27 pm
Location: EST/UTC-4

Useful Odds and Ends

Unread post by gedweyignasia »

I've been running into a bunch of things that seem like they need to get done a lot, so I'm going to keep a running list of script examples for everyone's use in case they run into a situation where they need this. Out of consideration for fairness, I'm going to omit any code (e.g. damage calculation) that might otherwise give players an edge if you used my code verbatim.

Soft Boundary
Prevent a PC from entering a trigger

Flexible Active Loot Cleaner
For use in areas that don't empty often so they might not be suitable for other loot cleanup scripts. Clean up items based on flexible gold value formula, allows special items to be excluded from cleanup, such as those that have had the DMFI tool used on them.
Last edited by gedweyignasia on Fri Mar 06, 2015 4:39 pm, edited 5 times in total.
User avatar
gedweyignasia
Custom Content
Posts: 1353
Joined: Sat Nov 23, 2013 11:27 pm
Location: EST/UTC-4

Re: Useful Odds and Ends

Unread post by gedweyignasia »

"Soft" Boundary: This pushes the PC back a bit if they enter a trigger denoting a place the PC should not be allowed to go.

The calling object is the trigger painted in the area the player should not go. This function is best used in the OnEnter event of a trigger. The trigger must be convex in shape, and the first point painted must be as close to the center as possible.

NWScript doesn't let us get the direction a player is moving (just his character's orientation), or the normal of the trigger he's intersecting, so this script figures out which way he's moving with the mouse or WASD keys and pushes him back in the appropriate direction.

Code: Select all

void blockPlayer(object oPC,int two_directions=FALSE) {
	
	// Stop what they're doing
	AssignCommand(oPC,ClearAllActions());
	
	location lPC = GetLocation(oPC);
	location lBar = GetLocation(OBJECT_SELF);
	
	// Make an arrow that points a short distance backwards along the direction the PC is moving
	vector qPC = GetPositionFromLocation(lPC);		// Arrow from 0,0,0 pointing to PC's position
	vector qBar = GetPositionFromLocation(lBar);	// Arrow from 0,0,0 pointing to barrier center
	vector vToBar = qBar - qPC;						// Arrow from PC to barrier center
	float fSpeed = 0.50f;							// Distance to push the player back
	float fDir = GetFacing(oPC);					// Direction PC is facing
	float fTrigDir = VectorToAngle(vToBar);			// Direction of arrow from PC to barrier center
	float dotProd = cos(fDir-fTrigDir);				// Normalized dot-product
	float rt2over2 = (1.0f / sqrt(2.0f));
	if ((dotProd >= rt2over2) || (two_directions && (dotProd >= 0.0f))){
		//FloatingTextStringOnCreature("Forward",oPC);
	}
	else if ((dotProd <= (-1.0f * rt2over2)) || (two_directions)) {
		//FloatingTextStringOnCreature("Backward",oPC);
		fSpeed *= -1;
	}
	else if (sin(fDir-fTrigDir) <= 0.0f) {
		//FloatingTextStringOnCreature("Leftward",oPC);
		fDir += 90.0f;
	}
	else {
		//FloatingTextStringOnCreature("Rightward",oPC);
		fDir -= 90.0f;
	}
	
	vector vDiff = Vector(cos(fDir),sin(fDir),0.0f);	// You could use VectorToAngle(fDir) instead
	vDiff = fSpeed * VectorNormalize(vDiff);		// Entirely unnecessary to normalize :)
	qPC = qPC - vDiff;								// Push the PC by the arrow vDiff
	
	// Preserve the PC's orientation (and area!!) when pushing him back
	float thetaPC = GetFacingFromLocation(lPC);
	object areaPC = GetArea(oPC);
	location destPC = Location(areaPC,qPC,thetaPC);
	
	// Move the PC by your arrow
	location locSafe = CalcSafeLocation(oPC,destPC,fSpeed/2.0,TRUE,FALSE);	// Try a good spot
	if (GetIsLocationValid(locSafe)) {
		AssignCommand(oPC,ActionJumpToLocation(locSafe));
	}
	else {
		qPC = qPC - vDiff;	// Try a further back position, but allow the player to be placed in his current spot
		locSafe = CalcSafeLocation(oPC,destPC,fSpeed*2.0,TRUE,FALSE);
		AssignCommand(oPC,ActionJumpToLocation(locSafe));
	}
	
	// Keep him from persisting
	// Totally unsure if these three lines are doing anything, but it's good feng shui
	float fMRF = GetMovementRateFactor(oPC);
	SetMovementRateFactor(oPC,0.0001f * fMRF);	// Time-out
	AssignCommand(oPC,DelayCommand(1.0f,SetMovementRateFactor(OBJECT_SELF,GetMovementRateFactor(OBJECT_SELF)/0.0001f)));
	
}
How to Paint this Trigger:
Hidden: show
Image
Notes:

- It is important that the trigger is not painted such that it overlaps itself

- It's best to provide a visual effect to let the player know he's being blocked (unless you have a physical object like a door there), or he may think there's a glitch. I used effect VFX_AURA_BLADE_BARRIER on the center of the trigger to denote a magical ward obstructing his path.

- It may be useful to scale the speed to some other value based on the player's landspeed. This should get rid of the "jumpiness" that you otherwise see.

- This works best when used as directed, on a doorway, with the two_directions flag set to FALSE.

- It would be best to find whatever they used to make you sit for a moment in the FAI drawbridge script and apply the same effect (less the animation) to hold the player in place for a moment.
Last edited by gedweyignasia on Tue Mar 03, 2015 2:44 am, edited 3 times in total.
User avatar
gedweyignasia
Custom Content
Posts: 1353
Joined: Sat Nov 23, 2013 11:27 pm
Location: EST/UTC-4

Re: Useful Odds and Ends

Unread post by gedweyignasia »

Rewrote the soft-blocker script; it now works as intended, and properly stops WASD movement in addition to mouse movement.
User avatar
gedweyignasia
Custom Content
Posts: 1353
Joined: Sat Nov 23, 2013 11:27 pm
Location: EST/UTC-4

Re: Useful Odds and Ends

Unread post by gedweyignasia »

"Flexible Loot Cleaner" - Remove waste items from the area

This is a script to clean up loot bags in the object's area. It first checks to make sure the object is not a plot item and has not been flagged as special. A non-plot item might be special, for instance, if a player has used the DMFI tool on it. The DMFI tool can be adjusted to flag the item as special using SetIsSpecial(oItem). This allows the system to be used in areas where players might use items to act as markers, signs, etc, as in areas like the Minotaur Maze.


Removes all items in the area worth less than max_value gold after coef1*x^pow1 + coef2*x^pow2 ticks, where x is the item's value in gold. Ticks are counted in floats then rounded, not truncated.

Examples use a 6-second heartbeat, but 1 minute is recommended. Adjust the numbers as necessary.

Ex1:

Code: Select all

coef1 = 50/1000 = 0.05f
pow1 = 1.0f
coef2 = 0.0f
pow2 = 0.0f
The item's lifetime will be 5 minutes per 1000 gold

Ex2:

Code: Select all

coef1 = 50/1000 = 0.05f
pow1 = 1.0f
coef2 = 30.0f
pow2 = 0.0f
The item's lifetime will be 3 min + 5 min per 1000 gold

Code: Select all

void loot_cleanup (int max_value, float coef1=0.05f, float pow1=1.0f, float coef2=30.0f, float pow2=0.0f) {
	
	// Iterate over items, ticking down to their deaths
	int nNth = 1;
    object oItem = GetNearestObject(OBJECT_TYPE_ITEM, OBJECT_SELF, nNth);
    while (GetIsObjectValid(oItem) && (GetPlotFlag(oItem)==FALSE) && (GetIsSpecial(oItem) != 1) && (GetGoldPieceValue(oItem) < max_value)) {
	
		// Make sure it knows when to die
		int iMaxTicks = GetLocalInt(oItem,"ged_lc_maxTicks");
		if (iMaxTicks <= 0) {
			float value = GetGoldPieceValue(oItem) + 0.0f;
			float fMaxTicks = coef1*pow(value,pow1) + coef2*pow(value,pow2);
			iMaxTicks = FloatToInt(fMaxTicks);
		}
		
		// Countdown
		int myTicks = GetLocalInt(oItem,"ged_lc_myTicks");
		if (myTicks <= 0) {
			myTicks = 0;
		}
		else {
			myTicks++;
		}
		
		// Remove the item if necessary, otherwise update its values
		if (myTicks >= iMaxTicks) {
			DestroyObject(oItem);
		}
		else {
			SetLocalInt(oItem,"ged_lc_myTicks",myTicks);
			SetLocalInt(oItem,"ged_lc_maxTicks",iMaxTicks);
		}
		
		// Move to the next item
        oItem = GetNearestObject(OBJECT_TYPE_ITEM, OBJECT_SELF, ++nNth);
	}
}

int GetIsSpecial(object oItem) {
	return GetLocalInt(oItem,"ged_lc_iSpecial");
}

void SetIsSpecial(object oItem, int bSpecial=TRUE) {
	SetLocalInt(oItem,"ged_lc_iSpecial",1);
}
This code is sensitive and should be tested thoroughly before use.

Edit: There's a really stupid bug in the while loop. Just move the rest of the checks down a line and if any are true, get the next item and put a continue statement.

Return to “NWN2 Scripting”