Author Sticky: Post Working Scripts Here
Iskander Posted: Tuesday, 25 June 12:46AM
Name: lib_weight (plus examples, tr_enter_weight and tr_exit_weight)

What it does: An include library to give you some weighing functions, with values taken from the PHB and MM (except where NWN differs, in which case NWN wins for consistency's sake). Functions include:
(list)
(*)int GetCreatureWeight(object oCritter);
(*)int GetInventoryWeight( object oToWeigh );
(*)float GetItemWeight( object oItem );
(*)int GetSphereWeight(location center, float radius, int threshold);
(*)int GetCubeWeight(location center, float halfSide, int threshold);
(/list-incomplete)

Notes: Documented in the script itself. Note also:
(list)
(*)I've left debug comments in the GetInventoryWeight() function.
(*)GetInventoryWeight() includes the weight of equipped items
(*)NWN takes care of Magic bags for me
(*)There's also a GetShapeWeight, but I don't recommend you use it... the GetCube and GetSphere functions just wrap it.
(*)tr_enter_weight automatically adds the weight of the entering object, but the GetSphere with a low radius doesn't seem to pick other critters as I would expect. I fear that's because they somehow fall outside the sphere. I wanted to use GetNearestCreatureToLocation(), and check its distance from the center, but I couldn't quite get it to work as I expected. Which is a bummer. I'll keep trying, though.
(/list-incomplete)

How to use: I wrote this because someone PMed me asking about pressure plates. Looking at the Pressure Plate placeables, I figured that they didn't, by default, have the smarts to do 'real' pressure plate logic, and would probably need to have their inventories filled or something naff like that. I wanted a pressure plate that a PC would have to leave something heavy on to hold a door open. Like 100lbs worth of heavy - another PC, a couple of suits of Full Plate, whatever - it should be hard. When populating dungeons, I want to be able to include triggers that a player or party has to reason about, rather than accept the prevailing game paradigm of "Look, a glowing thing called 'Pressure Plate', and it's got an inventory! Look! A rock! Hmmm... let's put the rock in the Pressure Plate's inventory... Bingo!".

Hence the library. Here's how I've used it in my experimental dungeon:
 


     

  1. (Optional) Paint a round Pressure Plate into your area. I made mine usable, but with no inventory, so it glows on mouse hover, but you can't open it and 'fill' it with junk.
     
  2. Paint a Generic Trigger around the pressure plate. If you zoom right in, and the plate's in the right spot, you can form a decent octagon that fits exactly right.
     
  3. There's a door nearby. I have two scripts called open_door and close_door that tell it to open and close. When it closes, it locks itself. (There's also open_door_time that's on a trigger on the other side of the door from the pressure plate. It's timed to shut the door, trapping the PC in the room with the plate).
     
  4. Put the tr_enter_weight script in the trigger's onEnter event
     
  5. Put the tr_exit_weight script in the trigger's onEnter event
     
  6. EDIT BOTH SCRIPTS - there are things you MUST change to get it to work
     
  7. That should be it, really.
     



lib_weight(b)

NWScript:
//:: ///////////// ///////////////// /////////////////

//:: Name: Weight library

//:: FileName: lib_weight

//:: ///////////// ///////////////// /////////////////

/*

     A library of weight-related functions.

     This library takes values from the PHB where

     appropriate, guesses at NWN's calculations where

     necessary, and makes weights up beyond that.

     Feel free to replace or modify any of the values,

     but if you've got a good 3ed. DnD reason for

     doing so, please drop me note on the NWN boards

     explaining why. Thanks.

     You might want to change iObjectType to include

     placeables. Or not.

*/

//:: ///////////// ///////////////// /////////////////

//:: Created By: Iskander

//:: Created On: 23 June, 2002

//:: ///////////// ///////////////// /////////////////

// Returns the average race/gender weight from the PHB for

// normal PC races, or averages by size from the MM for

// Creatures (except large and huge, which use lower bounds).

int GetCreatureWeight( object oCritter);

 

// Returns the weight of an item, based on its base type (0 if not an item)

// Where possible, weights were taken from the PHB, or NWN,

// with in-game differences taking priority for consistency

float GetItemWeight( object oToWeigh );

// Iterates over the items in something that GetHasInventory(), and

//  returns the sum of their GetItemWeight() (rounded to lbs)

int GetInventoryWeight( object oToWeigh );

// Iterates over every object in a sphere, and returns the total weight

// of objects added until the threshold was reached. If the threshold

// is zero, all objects are weighed.  This should improve performance

// improve performance when weighing heavily laden PCs against a

// relatively low-weight pressure plate, for example.

// - center    : location of the center of the sphere

// - radius    : the radius of the sphere

// - threshold : target weight boundary at which to stop weighing and

//               adding items. e.g. Set this to 100, and as soon as

//               enough items have been found to reach 100, the function

//               will return.  Weighs ALL ITEMS if threshold==0.

int GetSphereWeight( location center, float radius, int threshold);

// Iterates over every object in a cube, and returns the total weight

// of objects added until the threshold was reached. If the threshold

// is zero, all objects are weighed.  This should improve performance

// improve performance when weighing heavily laden PCs against a

// relatively low-weight pressure plate, for example.

// - center    : location of the center of the cube

// - halfSide  : half the length of the side of the cube, i.e  the

//               distance from the center to the midpoint of one side

// - threshold : target weight boundary at which to stop weighing and

//               adding items. e.g. Set this to 100, and as soon as

//               enough items have been found to reach 100, the function

//               will return.  Weighs ALL ITEMS if threshold==0.

int GetCubeWeight(location center, float halfSide, int threshold);

// Use GetSphereWeight or GetCubeWeight, not this function.

int GetShapeWeight( location center, float radius, int threshold, int iShape);

// A debug call

void debug(string sMsg);

// ----- The library ----- //

int GetSphereWeight( location center, float radius, int threshold) {

  return GetShapeWeight( center, radius, threshold, SHAPE_SPHERE);

}

int GetCubeWeight( location center, float halfSide, int threshold) {

  return GetShapeWeight( center, halfSide, threshold, SHAPE_CUBE);

}

int GetShapeWeight( location center, float radius, int threshold, int iShape) {

  float iWeightSoFar = 0.0;

  int iObjType = OBJECT_TYPE_CREATURE|OBJECT_TYPE_ITEM;

  object oOnPlate = GetFirstObjectInShape( iShape, radius, center, FALSE, iObjType);

  while (OBJECT_INVALID != oOnPlate) {

    switch (GetObjectType( oOnPlate ) ){

      case OBJECT_TYPE_CREATURE :

        iWeightSoFar += IntToFloat( GetCreatureWeight( oOnPlate ) );

        break;

      case OBJECT_TYPE_ITEM :

        iWeightSoFar += GetItemWeight( oOnPlate );

        break;

      default:

    }

    if ( threshold && FloatToInt( iWeightSoFar ) > threshold) break;

    if (GetHasInventory( oOnPlate ) ) {

      iWeightSoFar += GetInventoryWeight( oOnPlate );

    }

    oOnPlate = GetNextObjectInShape( iShape, radius, center, FALSE, iObjType);

  }

  return FloatToInt( iWeightSoFar );

}

int GetCreatureWeight( object oCritter) {

  if ( GetObjectType( oCritter ) != OBJECT_TYPE_CREATURE ) {

    debug("Object is not a creature");

    return 0;

  }

  switch ( GetRacialType( oCritter ) ) {

    case RACIAL_TYPE_DWARF:      // PHB average

      if (GetGender( oCritter ) == GENDER_MALE ) return 135;

      return 105;

    case RACIAL_TYPE_ELF:        // PHB average

      if (GetGender( oCritter ) == GENDER_MALE ) return 105;

      return 100;

    case RACIAL_TYPE_GNOME:      // PHB average

      if (GetGender( oCritter ) == GENDER_MALE ) return 45;

      return 40;

    case RACIAL_TYPE_HALFELF:    // PHB average

      if (GetGender( oCritter ) == GENDER_MALE ) return 145;

      return 125;

    case RACIAL_TYPE_HALFLING:   // PHB average

      if (GetGender( oCritter ) == GENDER_MALE ) return 35;

      return 30;

    case RACIAL_TYPE_HALFORC:    // PHB average

      if (GetGender( oCritter ) == GENDER_MALE ) return 205;

      return 185;

    case RACIAL_TYPE_HUMAN:      // PHB average

      if (GetGender( oCritter ) == GENDER_MALE ) return 195;

      return 180;

    default:

      switch ( GetCreatureSize( oCritter ) ) {

        case CREATURE_SIZE_HUGE:

          return 4000; // Lower bound from MM

        case CREATURE_SIZE_LARGE:

          return 500;  // Lower bound from MM

        case CREATURE_SIZE_MEDIUM:

          return 155; // About average for M races above

        case CREATURE_SIZE_SMALL:

          return 34;  // Average from MM

        case CREATURE_SIZE_TINY:

          return 5;   // Average 4.5 from MM (rounded)

        case CREATURE_SIZE_INVALID: // Not sure what's invalid... but it's weightless anyway.

        default:

          return 0;

      }

  }

  return 0;

}

// Returns the weight of an item, based on its base type (0 if not an item)

float GetItemWeight( object oToWeigh ) {

  if (GetObjectType( oToWeigh ) != OBJECT_TYPE_ITEM) return 0.0;

  float iWeight = 0.0f;

  switch ( GetBaseItemType( oToWeigh ) ) {

    case BASE_ITEM_DOUBLEAXE :

      iWeight = 25.0;

      break;

    case BASE_ITEM_HEAVYFLAIL :

    case BASE_ITEM_GREATAXE :

      iWeight = 20.0;

      break;

    case BASE_ITEM_TOWERSHIELD : // Should be 45

      iWeight = 19.4;

      break;

    case BASE_ITEM_GREATSWORD :

    case BASE_ITEM_HALBERD :

      iWeight = 15.0;

      break;

    case BASE_ITEM_DIREMACE :

    case BASE_ITEM_SCYTHE :

      iWeight = 12.0;

      break;

    case BASE_ITEM_BASTARDSWORD :

    case BASE_ITEM_LARGESHIELD : // Again, assuming wooden check NWN

    case BASE_ITEM_KATANA :

      iWeight = 10.0;

      break;

    case BASE_ITEM_HEAVYCROSSBOW :

      iWeight = 9.0;

      break;

    case BASE_ITEM_MORNINGSTAR :

    case BASE_ITEM_WARHAMMER :

      iWeight = 8.0;

      break;

    case BASE_ITEM_BATTLEAXE :

      iWeight = 7.0;

      break;

    case BASE_ITEM_LIGHTMACE :

    case BASE_ITEM_LIGHTCROSSBOW :

      iWeight = 6.0;

      break;

    case BASE_ITEM_SHORTSPEAR :

    case BASE_ITEM_HANDAXE :

    case BASE_ITEM_LIGHTFLAIL :

    case BASE_ITEM_SMALLSHIELD :  // Assuming wooden - check NWN

      iWeight = 5.0;

      break;

    case BASE_ITEM_TWOBLADEDSWORD : // Huhn? should be much more

      iWeight = 4.4;

      break;

    case BASE_ITEM_QUARTERSTAFF :

    case BASE_ITEM_THROWINGAXE :

    case BASE_ITEM_LONGSWORD :

    case BASE_ITEM_SCIMITAR :

    case BASE_ITEM_MAGICSTAFF :

      iWeight = 4.0;

      break;

    case BASE_ITEM_CLUB :

    case BASE_ITEM_SICKLE :

    case BASE_ITEM_RAPIER :

    case BASE_ITEM_SHORTSWORD :

    case BASE_ITEM_LONGBOW :

    case BASE_ITEM_KUKRI :

      iWeight = 3.0;

      break;

    case BASE_ITEM_LIGHTHAMMER :

    case BASE_ITEM_SHORTBOW :

    case BASE_ITEM_HELMET :

    case BASE_ITEM_MAGICROD :

      iWeight = 2.0;

      break;

    case BASE_ITEM_KAMA :

    case BASE_ITEM_GLOVES :

    case BASE_ITEM_DAGGER :

    case BASE_ITEM_MAGICWAND :

    case BASE_ITEM_BELT :

    case BASE_ITEM_BOOK : // Should be 3

    case BASE_ITEM_BRACER :

    case BASE_ITEM_LARGEBOX :

      iWeight = 1.0;

      break;

    case BASE_ITEM_AMULET :

    case BASE_ITEM_BOOTS :

    case BASE_ITEM_HEALERSKIT :

    case BASE_ITEM_MISCMEDIUM :

    case BASE_ITEM_MISCLARGE :

    case BASE_ITEM_THIEVESTOOLS :

    case BASE_ITEM_TRAPKIT :

      iWeight = 0.5;

      break;

    case BASE_ITEM_MISCSMALL :

      iWeight = 0.3;

      break;

    case BASE_ITEM_TORCH :

    case BASE_ITEM_POTIONS :

    case BASE_ITEM_RING :

    case BASE_ITEM_KEY :

    case BASE_ITEM_SCROLL :

    case BASE_ITEM_SPELLSCROLL :

    case BASE_ITEM_DART :

      iWeight = 0.1;

      break;

    case BASE_ITEM_ARMOR :

      switch ( GetItemACValue( oToWeigh ) ) {

        case 8 :

        case 7 :

          iWeight = 50.0;

          break;

        case 6 :

          iWeight = 45.0;

          break;

        case 5 :

          iWeight = 40.0;

          break;

        case 4 :

          iWeight = 30.0;

          break;

        case 3 :

          iWeight = 15.0;

          break;

        case 2 :

          iWeight = 10.0;

          break;

        case 1 :

          iWeight =  5.0;

          break;

        case 0 :              // Robes, etc.

        default: iWeight =  1.0;

      }

      break;

    case BASE_ITEM_BULLET :

    case BASE_ITEM_SHURIKEN :

    case BASE_ITEM_BOLT :

    case BASE_ITEM_ARROW :

    case BASE_ITEM_CREATUREITEM :

    case BASE_ITEM_CSLASHWEAPON :

    case BASE_ITEM_CPIERCWEAPON :

    case BASE_ITEM_CBLUDGWEAPON :

    case BASE_ITEM_CSLSHPRCWEAP :

    case BASE_ITEM_SLING :

    case BASE_ITEM_GEM :

    case BASE_ITEM_GOLD :

    case BASE_ITEM_MISCTALL : // No idea

    case BASE_ITEM_MISCWIDE : // No idea

    default:

      iWeight = 0.0;

  }

//  if ( GetNumStackedItems( oToWeigh ) > 0 )

    return iWeight * IntToFloat( GetNumStackedItems( oToWeigh ) );

}

// Iterates over the items in something that GetHasInventory(), and

//  returns the sum of their GetItemWeight()

int GetInventoryWeight( object oToWeigh ) {

  float iTotalWeight = 0.0;

  object oItem;

  int iSlot;

  for ( iSlot = 0; iSlot < NUM_INVENTORY_SLOTS; iSlot++ ) {

    oItem = GetItemInSlot( iSlot, oToWeigh);

    if ( OBJECT_INVALID != oItem ) {

      debug( GetName( oItem ) + " weighs " + FloatToString( GetItemWeight( oItem ) ) );

      iTotalWeight += GetItemWeight( oItem );

    }

  }

  oItem = GetFirstItemInInventory( oToWeigh );

  while ( OBJECT_INVALID != oItem ) {

    debug( GetName( oItem ) + " weighs " + FloatToString( GetItemWeight( oItem ) ) );

    iTotalWeight += GetItemWeight( oItem );

    oItem = GetNextItemInInventory( oToWeigh );

  }

  return FloatToInt( iTotalWeight );

}

// Speak some 'meaningful' text

void debug(string sMsg) {

  SendMessageToPC(GetFirstPC(),sMsg);

  SendMessageToAllDMs(sMsg);

}



(b)tr_enter_weight

NWScript:
//:: ///////////// ///////////////// /////////////////

//:: Name: Pressure Plate weight trigger

//:: FileName: tr_enter_weight

//:: ///////////// ///////////////// /////////////////

/*

  This is to turn a trigger into a pressure plate, and

  requires my lib_weight library.

  Things you might (will) want to change:

  iThreshold - the weight at which the trigger is activated.

  fRadius    - the radius of the cylinder in which objects

               are weighed. You may want to rescript this

               geometry completely.

  say()      - I used this mostly for debugging, but also

               to make a 'click' when the trigger works.

  eVis       - A puff of smoke (steam)

  others     - SEE THE CAPITALS BELOW FOR IMPORTANT BITS

*/

//:: ///////////// ///////////////// /////////////////

//:: Created By: Iskander

//:: Created On: 24 June, 2002

//:: ///////////// ///////////////// /////////////////

#include "lib_weight"

// Function declarations

// Makes the Pressure Plate talk, since the trigger can't.

// You should modify this function for the behaviour you want.

void say(string sMessage);

// Function to hold the weight calculation logic. N.B. for

// efficiency, this STOPS ADDING WEIGHT when iThreshold is reached.

int GetPlateWeight(); // Does most of the work

// A round pressure plate is 1.0f wide

float fRadius = 0.5f;

int iThreshold = 100; // Minimum weight is needed to trigger the plate (lb)

int iOldWeight   = 0; // How much weight was on the trigger last time

location lSource; // The location of the trigger this script is called on

vector   vCenter; // A vector to describe the plate's center (default facing)

object oIntruder; // The object that triggered this script.

// Main event.

void main()

{

  oIntruder = GetEnteringObject();

  lSource = GetLocation( OBJECT_SELF);

  vCenter = GetPositionFromLocation( lSource) + Vector(0.5f, -0.5f, 0.0f);

  lSource = Location( GetAreaFromLocation(lSource), vCenter,0.0f);

  iOldWeight = GetLocalInt( OBJECT_SELF, "weighted");

  int iNew = GetPlateWeight();                // Calculate the new weight

  SetLocalInt( OBJECT_SELF, "weighted", iNew); // Store it

  if (iOldWeight >= iThreshold) return; // can't be less... this is onEnter

  if ( (iNew >= iThreshold) && (iOldWeight < iThreshold) ) {

    // ***   CHANGE THE NAME OF THIS SCRIPT, OR AssignCommand()   ***

    // *** AN ActionOpenDoor TO YOUR DOOR HERE - OR IT WON'T WORK ***

    ExecuteScript( "open_door", OBJECT_SELF);

    effect eVis = EffectVisualEffect( VFX_FNF_SMOKE_PUFF);

    ApplyEffectAtLocation( DURATION_TYPE_TEMPORARY, eVis, lSource);

  }

}

int GetPlateWeight() {

  // Weigh the intruder, because we know from testing that the

  // sphere is too small to find PCs. YOU MIGHT WANT TO CHANGE THIS

  // OR USE THE GetCubeWeight, depending on the shape of your trigger.

  int iWeightSoFar = GetCreatureWeight( oIntruder );

  iWeightSoFar += GetInventoryWeight( oIntruder );

  iWeightSoFar += GetSphereWeight( lSource, fRadius, iThreshold);

  return iWeightSoFar;

}

// Speak some debugging text

void say(string sMsg) {

  SendMessageToPC( GetFirstPC(),sMsg);  

  SendMessageToAllDMs( GetFirstPC(),sMsg);

}



tr_exit_weight

NWScript:
//:: ///////////// ///////////////// /////////////////

//:: Name: Pressure Plate weight trigger onExit

//:: FileName: tr_exit_weight

//:: ///////////// ///////////////// /////////////////

/*

  This is to turn a trigger into a pressure plate, and

  requires my lib_weight library.

  Things you might (will) want to change:

  iThreshold - the weight at which the trigger is activated.

  fRadius    - the radius of the cylinder in which objects

               are weighed. You may want to rescript this

               geometry completely.

  say()      - I used this mostly for debugging, but also

               to make a 'click' when the trigger works.

  eVis       - A puff of smoke (steam)

  others     - SEE THE CAPITALS BELOW FOR IMPORTANT BITS

*/

//:: ///////////// ///////////////// /////////////////

//:: Created By: Iskander

//:: Created On: 24 June, 2002

//:: ///////////// ///////////////// /////////////////

#include "lib_weight"

// Function declarations

// Makes the Pressure Plate talk, since the trigger can't.

// You should modify this function for the behaviour you want.

void say(string sMessage);

// A round pressure plate is 1.0f wide

float fRadius = 0.5f;

int iThreshold = 100; // Minimum weight is needed to trigger the plate (lb)

int iOldWeight   = 0; // How much weight was on the trigger last time

int bExited  = FALSE; // Was the trigger entered (false) or exited (true)?

int bChanged = FALSE; // Has the trigger's state changed?



location lSource; // The location of the trigger this script is called on

vector   vCenter; // A vector to describe the plate's center (default facing)

// Main event.

void main()

{

  lSource = GetLocation( OBJECT_SELF);

  vCenter = GetPositionFromLocation( lSource) + Vector(0.5f, -0.5f, 0.0f);

  lSource = Location( GetAreaFromLocation(lSource), vCenter,0.0f);

  iOldWeight = GetLocalInt( OBJECT_SELF, "weighted");

  int iNew = GetSphereWeight( lSource, fRadius, iThreshold);

  SetLocalInt(OBJECT_SELF, "weighted", iNew);

  bChanged = (((iOldWeight >= iThreshold) && (iNew <  iThreshold)) ||

              ((iOldWeight <  iThreshold) && (iNew >= iThreshold)));

  if ( bChanged ) {

    if (iOldWeight >= iThreshold) {

    // ***   CHANGE THE NAME OF THIS SCRIPT, OR AssignCommand()    ***

    // *** AN ActionCloseDoor TO YOUR DOOR HERE - OR IT WON'T WORK ***

      ExecuteScript("close_door", OBJECT_SELF);

    }

    if (iOldWeight < iThreshold) {

    // ***   CHANGE THE NAME OF THIS SCRIPT, OR AssignCommand()   ***

    // *** AN ActionOpenDoor TO YOUR DOOR HERE - OR IT WON'T WORK ***

      ExecuteScript("open_door", OBJECT_SELF);

    }

    effect eVis = EffectVisualEffect( VFX_FNF_SMOKE_PUFF);

    ApplyEffectAtLocation( DURATION_TYPE_TEMPORARY, eVis, lSource);

  }

}

// Speak some debugging text

void say(string sMsg) {

  // Try to float over a nearby creature. Hee hee.

  object oSpeaker = GetNearestObject( OBJECT_TYPE_CREATURE, OBJECT_SELF );

  if ( (GetDistanceBetweenLocations( GetLocation( oSpeaker ), lSource ) < 3 * fRadius) && GetIsObjectValid( oSpeaker) )

    FloatingTextStringOnCreature( sMsg, oSpeaker, FALSE );

  else {

    SendMessageToPC( GetFirstPC(),sMsg);

    AssignCommand( GetObjectByTag( "plateDoorOpen"),

                ActionSpeakString( sMsg, TALKVOLUME_TALK));

  }

}



Cheers,
Iskander


________________
Simple Quest Creation Walkthrough - no scripting required
Korak's NWNScript reference