Could Use Help
IronActual
Member Posts: 24
Hello everyone! I created some modules back in 2002-2004 that ran online in that time period. I had a lot of fun back then and want to create something new in NWN:EE. It's been a long time since I've been in the toolset, but have slowly found my way back around and started creating my next project. I'm getting close to getting something that will be playable, but could use a little help. There are a few scripts I've pulled from my past work that I know were unorganized and probably redundant. I was and still am very novice at this, so would love to find someone that can look over the scripts and help clean them up. I also have a few new ideas, that I could use help getting to work. Is there anyone out there that wouldn't mind me picking their brain and helping out?
1
Comments
My first set of scripts go into the Module Properties "OnActivateItem"
void main()
{
object oItem = GetItemActivated();
object oPC = GetItemActivator();
object oTarget = GetItemActivatedTarget();
string sTag = GetTag(oItem);
int oObject = GetObjectType(oTarget);
if (sTag == "gem")
{
if (!GetIsPC(oPC)) return;
location lTarget = GetItemActivatedTargetLocation();
AssignCommand(oPC, ClearAllActions());
effect eVis = EffectVisualEffect(VFX_IMP_HEALING_X, FALSE);
effect eEffect = EffectAbilityIncrease(ABILITY_CONSTITUTION,1);
int iConstitution = GetAbilityScore(oPC,ABILITY_CONSTITUTION);
ApplyEffectAtLocation(DURATION_TYPE_TEMPORARY, eVis, lTarget, 6.0);
CreateObject(OBJECT_TYPE_PLACEABLE,"townportal",lTarget,TRUE);
DeleteLocalLocation(oPC, "Portal"+GetName(oPC));
SetLocalLocation(oPC, "Portal"+GetName(oPC), GetItemActivatedTargetLocation());
}
if (sTag == "destroyer")
{
if (oObject==OBJECT_TYPE_ITEM)
{
if(GetTag(oTarget)=="destroyer")
{
}
if(GetTag(oTarget)=="gem")
{
}
if(GetStringLeft(GetTag(oTarget),3)=="off")
{
}
if(GetStringLeft(GetTag(oTarget),2)=="NW")
{
DestroyObject(oTarget);
}
if(GetStringLeft(GetTag(oTarget),3)=="sta")
{
DestroyObject(oTarget);
}
if(GetStringLeft(GetTag(oTarget),3)=="blu")
{
GiveGoldToCreature(oPC,10);
DestroyObject(oTarget);
}
if(GetStringLeft(GetTag(oTarget),3)=="pur")
{
GiveGoldToCreature(oPC,25);
DestroyObject(oTarget);
}
if(GetStringLeft(GetTag(oTarget),3)=="yel")
{
GiveGoldToCreature(oPC,100);
DestroyObject(oTarget);
}
if(GetStringLeft(GetTag(oTarget),3)=="red")
{
GiveGoldToCreature(oPC,250);
DestroyObject(oTarget);
}
}
}
}
The first part "if (sTag == "gem")" spawns a portal for players to use to return to town and then from town there's another portal that teleports players back to this portal.
The script that goes into the portal that spawns:
void main()
{
object oPC = GetLastUsedBy();
location lLoc = GetLocation(GetWaypointByTag("return"));
{
if(GetIsPC(oPC) == 1)
{
object oTarget = oPC;
effect eVis = EffectVisualEffect(VFX_IMP_DISPEL, FALSE);
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eVis, oTarget, 6.0);
AssignCommand(oPC, ActionJumpToLocation(lLoc));
DeleteLocalLocation(oPC, "Portal"+GetName(oPC));
SetLocalLocation(oPC, "Portal"+GetName(oPC), GetLocation(OBJECT_SELF));
DestroyObject(OBJECT_SELF,30.0);
}
}
}
The script in the Town Portal to return players:
void main()
{
object oPC = GetLastUsedBy();
location lLoc = GetLocalLocation(oPC, "Portal"+GetName(oPC));
if(GetIsPC(oPC) == 1)
{
object oTarget = oPC;
effect eVis = EffectVisualEffect(VFX_IMP_DISPEL, FALSE);
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eVis, oTarget, 6.0);
AssignCommand(oPC, ActionJumpToLocation(lLoc));
DeleteLocalLocation(oPC, "Portal"+GetName(oPC));
}
}
The second part in the OnActivateItem script "if (sTag == "destroyer")" is used by players to delete items in their inventory. There's a few items I don't want deleted (the destroyer item itself and the portal gem) and a few that give gold in return. This acts like a mobile merchant.
I know these are a little messy, so hopefully someone can help clean them up!
The best way to clean up this script is to replace it with tag-based scripts. This is a technique which allows you to make one script that will work for each activated item. This will help you keep scripts for the "gem" item and "destroyer" item separate. It will also make it simpler to add new scripted items in the future.
To do this, set the following module scripts:
// destroyer.nss // Destroys the targeted item. May give gold depending on the item. #include "x2_inc_switches" void main() { // Only function OnActivateItem int nEvent = GetUserDefinedItemEventNumber(); if (nEvent != X2_ITEM_EVENT_ACTIVATE) return; // Only function if used by a PC object oPC = GetItemActivator(); if (!GetIsPC(oPC)) return; // Only destroy items. object oTarget = GetItemActivatedTarget(); if (GetObjectType(oTarget) != OBJECT_TYPE_ITEM) { SendMessageToPC(oPC, "You can only use this on an item."); return; } // Do not allow these items to be destroyed. // Note: a better way to do this might be to check for the plot flag. string sTarget = GetTag(oTarget); string sPrefix = GetStringLeft(sTarget, 3); if (sTarget == "destroyer" || sTarget == "gem" || sPrefix == "off") { SendMessageToPC(oPC, "This item cannot be destroyed."); return; } // Some items give gold when destroyed int nGold; if (sPrefix == "blu") nGold = 10; else if (sPrefix == "pur") nGold = 25; else if (sPrefix == "yel") nGold = 100; else if (sPrefix == "red") nGold = 250; GiveGoldToCreature(oPC, nGold); DestroyObject(oTarget); }
// gem.nss // Creates a portal to town at the location the PC targets. The portal will // despawn after 30 seconds. This item will not do anything in areas with tags // beginning with "dun". #include "x2_inc_switches" void main() { // Only function OnActivateItem int nEvent = GetUserDefinedItemEventNumber(); if (nEvent != X2_ITEM_EVENT_ACTIVATE) return; // Only function if used by a PC object oPC = GetItemActivator(); if (!GetIsPC(oPC)) return; // Do not function in areas with tags beginning with "dun" string sArea = GetTag(GetArea(oPC)); if (GetStringLeft(sArea, 3) == "dun") { SendMessageToPC(oPC, "You cannot use this item in this area."); return; } // Create the portal to town location lPortal = GetItemActivatedTargetLocation(); object oPortal = CreateObject(OBJECT_TYPE_PLACEABLE, "townportal", lPortal, TRUE); // Apply a nifty VFX at the location effect eVis = EffectVisualEffect(VFX_IMP_HEALING_X); ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oPortal); // Set the portal to self-destruct in 30 seconds DestroyObject(oPortal, 30.0); }
// Town Portal OnUsed script: // Jumps the PC that uses the portal into town. void main() { // Only function if used by a PC object oPC = GetLastUsedBy(); if (!GetIsPC(oPC)) return; // Apply a nifty VFX to the PC effect eVis = EffectVisualEffect(VFX_IMP_DISPEL); ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oPC); // Save the portal location on the PC SetLocalLocation(oPC, "Portal", GetLocation(OBJECT_SELF)); // Jump to the town waypoint object oWaypoint = GetWaypointByTag("return"); AssignCommand(oPC, ActionJumpToObject(oWaypoint)); }
// Return portal OnUsed script: // Jumps the PC that uses the portal back to his last townportal spawn location. void main() { // Only function if used by a PC object oPC = GetLastUsedBy(); if (!GetIsPC(oPC)) return; // Make sure the PC has a valid return location location lPortal = GetLocalLocation(oPC, "Portal"); if (!GetIsObjectValid(GetAreaFromLocation(lPortal))) { SendMessageToPC(oPC, "You cannot use this object."); return; } // Apply a nifty VFX to the PC effect eVis = EffectVisualEffect(VFX_IMP_DISPEL); ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, oPC); // Jump to the return location AssignCommand(oPC, ActionJumpToLocation(lPortal)); }
So here is my next script I'm trying to figure out.
First, I'll need a script that will spawn 1 of 3 things on a creature's OnSpawn.
60% chance that 10 gold will spawn on the creature. 35% chance that 1 item from a list of 20 items (will probably grow to more items in the future) will spawn on the creature. 5% chance that 1 item from a different list of 20 items (will probably grow to more items in the future) will spawn on the creature.
Then, if 1 of those items spawn on the creature, I'd like random properties to be added.
In the past, I had these properties added to the items from the module's OnAcquireItem for when the player's looted them. But, I'd like these properties added when they spawn on the creature instead. I think it will be more interesting to see the creatures using these random items against the players!
Here's what I used in the past as an example of the random properties being added to items. So, the items gets 2 properties added. 1 being an Enhancement bonus, and the 2nd being a Elemental Damage. The list of properties will be much larger, but this is small example to give an idea.
itemproperty en1 = ItemPropertyEnhancementBonus(1); itemproperty en2 = ItemPropertyEnhancementBonus(2); itemproperty en3 = ItemPropertyEnhancementBonus(3); switch (Random(3) + 1) { case 1: IPSafeAddItemProperty(oItem, en1, 0.0f, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING); break; case 2: IPSafeAddItemProperty(oItem, en2, 0.0f, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING); break; case 3: IPSafeAddItemProperty(oItem, en3, 0.0f, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING); break; } itemproperty acid1 = ItemPropertyDamageBonus(IP_CONST_DAMAGETYPE_ACID,IP_CONST_DAMAGEBONUS_1); itemproperty acid2 = ItemPropertyDamageBonus(IP_CONST_DAMAGETYPE_ACID,IP_CONST_DAMAGEBONUS_2); itemproperty acid3 = ItemPropertyDamageBonus(IP_CONST_DAMAGETYPE_ACID,IP_CONST_DAMAGEBONUS_3); switch (Random(3) + 1) { case 1: IPSafeAddItemProperty(oItem, acid1, 0.0f, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING); break; case 2: IPSafeAddItemProperty(oItem, acid2, 0.0f, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING); break; case 3: IPSafeAddItemProperty(oItem, acid3, 0.0f, X2_IP_ADDPROP_POLICY_REPLACE_EXISTING); break; }
// Place this in the OnSpawn of a creature. It will generate treasure: // - 60% chance that 10 gold will spawn on the creature. // - 35% chance that 1 item from a list of 20 items (will probably grow to more // items in the future) will spawn on the creature. // - 5% chance that 1 item from a different list of 20 items (will probably grow // to more items in the future) will spawn on the creature. // Any spawned items will also receive a random enhancement bonus and a random // acid damage bonus. #include "x2_inc_itemprop" void main() { string sResRef; int nRoll = d100(); if (nRoll <= 60) GiveGoldToCreature(OBJECT_SELF, 10); else if (nRoll <= 95) { // Choose a random resref switch (Random(20)) { case 0: sResRef = "my_item_1"; break; case 1: sResRef = "my_item_2"; break; case 2: sResRef = "my_item_3"; break; // ... case 19: sResRef = "my_item_20"; break; } } else { // Choose a random resref switch (Random(20)) { case 0: sResRef = "my_other_item_1"; break; case 1: sResRef = "my_other_item_2"; break; case 2: sResRef = "my_other_item_3"; break; // ... case 19: sResRef = "my_other_item_20"; break; } } // Give the creature an item if we ought to. object oItem = CreateItemOnObject(sResRef); // If the creature was given an item, add a random item property to it. if (GetIsObjectValid(oItem)) { itemproperty ipBonus = ItemPropertyEnhancementBonus(d3()); itemproperty ipAcid = ItemPropertyDamageBonus(IP_CONST_DAMAGETYPE_ACID, d3()); IPSafeAddItemProperty(oItem, ipBonus); IPSafeAddItemProperty(oItem, ipAcid); } }
// If the creature was given an item, add a random item property to it. if (GetIsObjectValid(oItem)) { itemproperty ipBonus = ItemPropertyEnhancementBonus(d3()); itemproperty ipAcid = ItemPropertyDamageBonus(IP_CONST_DAMAGETYPE_ACID, d3()); IPSafeAddItemProperty(oItem, ipBonus); IPSafeAddItemProperty(oItem, ipAcid); }
Some of the items will be armor and ranged weapons. Items blue001 - blue034 are all melee weapons, so the above will work for those. But, blue035 - blue039 are ranged and blue040 - blue043 are armors. Not to mention adding shields, boots, cloaks, etc to the mix. I'll need to add an extra "if" for each item type. Something like this?
if (sResRef == "blue001" || sResRef == "blue002" || sResRef == "blue003" etc...) { if (GetIsObjectValid(oItem)) { itemproperty ipBonus = ItemPropertyEnhancementBonus(d3()); itemproperty ipAcid = ItemPropertyDamageBonus(IP_CONST_DAMAGETYPE_ACID, d3()); IPSafeAddItemProperty(oItem, ipBonus); IPSafeAddItemProperty(oItem, ipAcid); } } if (sResRef == "blue035" || sResRef == "blue036" || sResRef == "blue037" etc...) { if (GetIsObjectValid(oItem)) { itemproperty ipAttack = ItemPropertyAttackBonus(d3()); IPSafeAddItemProperty(oItem, ipAttack); } }
https://neverwintervault.org/project/nwn1/script/siliconscouts-treasure-spawn-system-18a
I do not, however, know what's available for adding random enchantments.
ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_BREACH), OBJECT_SELF)
I even tried adding a delay to the effect, but I only notice the effect working half the time.It is not worth it imo to script your own system.
You can modify the treasure system easily for whatever custom stuff you want.
This, combined with NESS spawn system is just really everything you need.
The integrated loot system in NESS isn't really that great, so i replaced it with siliconscouts treasure system.
// Give the creature an item if we ought to. object oItem = CreateItemOnObject(sResRef);
Would it be easier to add an "if" this item "then" create stack?
Or would it be easier to just set the max stack size for all items, since regular melee weapons can't stack anyway?
object oItem = CreateItemOnObject(sResRef, OBJECT_SELF, nStackSize);
It looks like it'd be easier to just set the max stack size. Only one of non-stackable items should be created.
object oItem = CreateItemOnObject(sResRef); SetItemStackSize(oItem, 50); if (GetIsObjectValid(oItem))
potion.nss That when used by the player (once a day) gives the player 25% of their total HP back.
Thank you again!
// potion.nss // Heals the PC who drinks it for 25% of his total HP. #include "x2_inc_switches" void main() { // Only function OnActivateItem int nEvent = GetUserDefinedItemEventNumber(); if (nEvent != X2_ITEM_EVENT_ACTIVATE) return; object oPC = GetItemActivator(); int nHeal = GetMaxHitPoints(oPC) / 4; effect eVis = EffectVisualEffect(VFX_IMP_HEALING_G); effect eHeal = EffectHeal(nHeal); eHeal = EffectLinkEffects(eVis, eHeal); ApplyEffectToObject(DURATION_TYPE_INSTANT, eHeal, oPC); }
As far as making it once per day, you just control that with the item property.
void main() { if(!GetIsPC(GetExitingObject())) { return; } object oPC = GetExitingObject(); if (!GetIsPC(oPC)) return; oPC = GetFirstPC(); while (oPC != OBJECT_INVALID) { if (OBJECT_SELF == GetArea(oPC)) return; else oPC = GetNextPC(); } object oObject = GetFirstObjectInArea(OBJECT_SELF); while (oObject != OBJECT_INVALID) { if (GetIsEncounterCreature(oObject)) DestroyObject(oObject); oObject = GetNextObjectInArea(OBJECT_SELF); } }
All creatures in the area have tags that start with "mummy" (mummy001, mummy002, etc.) And the portal's tag is "cryptportal".
Some hints though:
When a PC leaves, mark an area to be cleaned.
Use a delaycommand to schedule the clean, in say 60 to 90 seconds.
if a PC enters the area cancel the mark
if clean finds PC in area, cancel it.
So basically, PC leaves, you mark it to be cleaned up in 90 seconds. If they come back in, cancel it.
In your loop:
Destroy objects at random interval so you don't get a bit lag spike!
float fDelay = Random(10) * 1.0;
DelayCommand(fDelay, DestroyObjectAndInventory(oObject));
Use a DestroyObjectAndInventory type of function. If you are cleaning up BodyBags (default loop drops) you can't just delete them, you need to delete inventory first.
To check for Creature, do this and !GetIsPC()
if (GetObjectType(oObject) == OBJECT_TYPE_CREATURE) {
to clean up Portal by tag:
if (GetObjectType(oObject) == OBJECT_TYPE_PLACEABLE && GetTag(oObject) == "PortalTag") {
void main() { if(!GetIsPC(GetExitingObject())) { return; } object oPC = GetExitingObject(); if (!GetIsPC(oPC)) return; oPC = GetFirstPC(); while (oPC != OBJECT_INVALID) { if (OBJECT_SELF == GetArea(oPC)) return; else oPC = GetNextPC(); } object oObject = GetFirstObjectInArea(OBJECT_SELF); while (oObject != OBJECT_INVALID) { if (GetObjectType(oObject) == OBJECT_TYPE_CREATURE) DestroyObject(oObject); oObject = GetNextObjectInArea(OBJECT_SELF); } DestroyObject(GetObjectByTag("cryptportal")); }
Otherwise you might end up deleting a merchant or quest giver!
I have an area that when players enter, a creature spawns (boss001). If another player enters the area and the creature (boss001) already exists, do nothing.
When all players have left the area, only that creature (boss001) is deleted (not the others in the area).
For example, if I'm the only player in the area and I kill the boss, can I then leave the area and return to immediately respawn the boss with no delay?
Here's what I'm working on and could use help making it work correctly. Thanks for the direction Proleric!
This is for the onenter of my area.
void main() { object oTarget; object oSpawn; object oPC = GetEnteringObject(); int iBoss = 1; while (iBoss > 0) { eVFX = EffectVisualEffect(VFX_IMP_HARM); oTarget = GetWaypointByTag("boss001waypoint"); oSpawn = CreateObject(OBJECT_TYPE_CREATURE, "boss001", GetLocation(oTarget)); iBoss--; } }