My last 2 script to "end" the module [HELP]
Hello, I've almost finished my module (with a lot of help from the community members of this forum! special thanks to ForSerious).
Now I'am missing just a couple of scripts!
The first one is a persistance script to record just the position and the hp of charcaters, I've tried this one I've found on the vault with no luck: https://neverwintervault.org/project/nwn1/script/easy-persistent-locations
The second one is a script to destroy a placeble conteiners if a player places specific item in it (I want player to be able to destroy a rock and open a passage revealing a transition if they craft a specific item and then "use it" on the rock).
Thanks for reading, hope you can help me!
Now I'am missing just a couple of scripts!
The first one is a persistance script to record just the position and the hp of charcaters, I've tried this one I've found on the vault with no luck: https://neverwintervault.org/project/nwn1/script/easy-persistent-locations
The second one is a script to destroy a placeble conteiners if a player places specific item in it (I want player to be able to destroy a rock and open a passage revealing a transition if they craft a specific item and then "use it" on the rock).
Thanks for reading, hope you can help me!
0
Comments
I have scripts for the location and HP. (The location doesn't always work though. I know how to make it work, just haven't done it yet.)
For the second thing, it sounds conflicting. Are they going to use the crafted item on the rock, or place the item in the inventory of the rock? Both are possible.
I agree that this needs some clarification but it sounds entirely doable.
Does this pseudocode roughly sound like what you're going for?
if (LastItemPlacedInContainer == KeyItem){ DestroyContainer; DestroyRock; }thanks!
You need a way to have unique identifiers for players. I like to use the tag of the player. By default it's empty.
Here's the include script to make unique tags for players:
// The main database name const string DB = "datum"; // The PlayerHP prefix const string HP = "hp"; // The Tag index const string TAG = "tag"; // Makes a unique tag to be assigned on a player. string MakePlayerTag(object oPC); string MakePlayerTag(object oPC) { // Pull the last number that was used to make a tag from the database. int iTag = GetCampaignInt(DB, TAG); // Add one to it. That makes it unique. iTag = iTag + 1; // Store that in the database as the last number used. SetCampaignInt(DB, TAG, iTag); // Convert the integer into a string for manipulation. string sParsed = IntToString(iTag); int iLen; // Add a bunch of zeros to the front of the number. // The goal here is to have a unique tag for each player that is always the same length. for(iLen = GetStringLength(sParsed); iLen <= 6; iLen++) { sParsed = "0" + sParsed; } // Add a unique prefix. This can be used to check if a player somehow (Hacking?) added a tag to their character. sParsed = "ZZ" + sParsed; // Set the unique tag to the player object. SetTag(oPC, sParsed); // Return the tag for use in the script that calls this function. return sParsed; }This script will be called database_stuff in all the other scripts.Next we have the OnExit script. Though you can use this in the area OnExit, don't. Put it in the module OnExit script.
//That first script #include "database_stuff" //Used to save player HP without temporary HP. void TrySavingHPWithoutTempHP(object oPC, string sName); void main() { // The player that is leaving the game. object oPC = GetExitingObject(); // The area where the player left. object oArea = GetArea(oPC); // That unique tag. string sName = GetTag(oPC); // HP at time of leaving. int iHP = GetCurrentHitPoints(oPC); // The x y and z values of the location the player left from. vector v = GetPosition(oPC); // The value of the ratation that player was at. float o = GetFacing(oPC); float x = v.x; float y = v.y; float z = v.z; // You can skip this if you don't care. Without it, players can heal themselves with temperary HP spells and abilities. // Check if the player has temp HP. if(GetHasEffect(EFFECT_TYPE_TEMPORARY_HITPOINTS, oPC) == TRUE) { // Make a copy of the player object because the original will become OBJECT_INVALID before this runs all the way. // You need to make a waypoint with the tag "The_Point" in some area that players cannot access. object oCopy = CopyObject(oPC, GetLocation(GetObjectByTag("The_Point"))); // Make a new temp HP effect. effect eTempAdd = EffectTemporaryHitpoints(1); // Apply it to the copy object. This will overright the effect the player had. ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eTempAdd, oCopy, 5.0f); // After that effect wears off, save the HP value. DelayCommand(6.2, TrySavingHPWithoutTempHP(oCopy, sName)); } //If the player leaves the game at 0 HP, when they come back, it looks like they have never had their HP saved before. if(iHP == 0) { // I do have things changed so that player death doesn't trigger until -10 HP. Default is 0. // So this may not matter. iHP = -1; } // The the HP value to the database. SetCampaignInt(DB, HP + sName, iHP); WriteTimestampedLogEntry("Player left: " + sName); // Let us save the player location while we're here. // This is an exception for the starting area. No reason to save. You can add more areas players should not be able to save location in. if(GetTag(oArea) != "StartAreaTag") { // Save all the details individually SetCampaignFloat(DB, "LOC_X_" + sName, x); SetCampaignFloat(DB, "LOC_Y_" + sName, y); SetCampaignFloat(DB, "LOC_Z_" + sName, z); // Facing SetCampaignFloat(DB, "LOC_O_" + sName, o); // Area tag. SetCampaignString(DB, "LOC_A_" + sName, GetTag(oArea)); // This function randomly saves a random placeable object as the area. That's why we're doing it the complecated way. //SetCampaignLocation(DB, PLAYER_LOC + sName, lPC); } } void TrySavingHPWithoutTempHP(object oPC, string sName) { if(GetIsObjectValid(oPC)) { // At this point the temp HP effect has been removed. int iHP = GetCurrentHitPoints(oPC); WriteTimestampedLogEntry("Leaving HP got reset to: " + IntToString(iHP)); WriteTimestampedLogEntry(sName); SetCampaignInt(DB, HP + sName, iHP); // Get rid of the copied player object. DestroyObject(oPC); } else { WriteTimestampedLogEntry("Object was made invalid by the time this got called."); } }#include "database_stuff" void main() { // Get entering player object oPC = GetEnteringObject(); // Ignore DMs if(GetIsDM(oPC)) { return; } // Create unique string that can be got after the player logs. string sName = GetTag(oPC); int iXP = GetXP(oPC); // If new... // Check if they have no tag. Or if they have a tag that does not start with ZZ if(GetStringLength(sName) <= 0 || GetSubString(sName, 0, 2) != "ZZ") { // Mark that they are no longer new. MakePlayerTag(oPC); int iGold = GetGold(oPC); } //get the players saved HP int iHP = GetCampaignInt(DB, HP + sName); //Set the players HP to its saved value. int iHPmax = GetMaxHitPoints(oPC); // We need to get how much HP to take away from the PC. int iHPold = iHPmax - iHP; if (iHP != 0) { // Take the HP away with devine damage. You can change the type. effect eDamage = EffectDamage(iHPold, DAMAGE_TYPE_DIVINE, DAMAGE_POWER_NORMAL); ApplyEffectToObject(DURATION_TYPE_INSTANT, eDamage, oPC); } }#include "database_stuff" void main() { // Basically reverse the process of how the location was saved in the database. object oPC = GetPCSpeaker(); string sName = GetTag(oPC); float o = GetCampaignFloat(DB, "LOC_O_" + sName); float x = GetCampaignFloat(DB, "LOC_X_" + sName); float y = GetCampaignFloat(DB, "LOC_Y_" + sName); float z = GetCampaignFloat(DB, "LOC_Z_" + sName); string sArea = GetCampaignString(DB, "LOC_A_" + sName); vector vLoc = Vector(x, y, z); object oRoughArea = GetObjectByTag(sArea); location lPC = Location(oRoughArea, vLoc, o); if(GetIsObjectValid(oCheck)) { AssignCommand(oPC, ClearAllActions()); AssignCommand(oPC, JumpToLocation(lPC)); } else { FloatingTextStringOnCreature("OOPS!", oPC, FALSE); SendMessageToPC(oPC, "Sorry dude. You have no saved location."); } }I have this set up to use in a conversation. You can change that to how you want.Sorry if any of these don't compile right away. I just took them from what I have and edited out all the parts you don't need. I'm also sorry if you thought it was going to be easy.
So the easiest option for the exploding obstacle, is to place an item in the inventory.
I put this script in the OnClosed slot.
// Para Bilar La Bomba… void main() { object oItem = GetFirstItemInInventory(); while(GetIsObjectValid(oItem)) { if(GetTag(oItem) == "CraftedItemTag") { DestroyObject(oItem); DelayCommand(0.5, SpeakString("5")); DelayCommand(1.5, SpeakString("4")); DelayCommand(2.5, SpeakString("3")); DelayCommand(3.5, SpeakString("2")); DelayCommand(4.5, SpeakString("1")); DelayCommand(0.5, PlaySound("gui_trapsetoff")); DelayCommand(1.5, PlaySound("gui_trapsetoff")); DelayCommand(2.5, PlaySound("gui_trapsetoff")); DelayCommand(3.5, PlaySound("gui_trapsetoff")); DelayCommand(4.5, PlaySound("gui_trapsetoff")); effect eExplode = EffectVisualEffect(VFX_FNF_FIREBALL); effect eExplode1 = EffectVisualEffect(VFX_FNF_ELECTRIC_EXPLOSION); DelayCommand(5.5,(ApplyEffectToObject(DURATION_TYPE_INSTANT, eExplode, OBJECT_SELF))); DelayCommand(6.0,(ApplyEffectToObject(DURATION_TYPE_INSTANT, eExplode, OBJECT_SELF))); DelayCommand(6.5,(ApplyEffectToObject(DURATION_TYPE_INSTANT, eExplode1, OBJECT_SELF))); DelayCommand(6.5, DestroyObject(OBJECT_SELF)); return; } oItem = GetNextItemInInventory(); } }teatv apk
I've been using Set/GetCampaignLocation for a few months, and quite randomly it saves a random placeable object instead of an area object. My code above, has not had that issue... yet.
In the server I used to play on most, it was heavily recommended protocol to re-log your character after transferring items to them. The reason being that if the server crashed, you wouldn't lose your gear. It would have saved the character that just lost the gear, but not saved the one that just picked it up. Not sure on the exact timings, but the server only saves characters to disc like once every half hour or so, unless they log off.
Because of that, I try to save critical things to the database, and lesser things to the PC Properties skin. (Like settings the player can change.)
A location is made of three things.
1) The coordinate of the location
2) The Facing/rotation of the location
3) The area of the location - saved by objectID.
The issue comes that ObjectID is not a permanent assignation, but instead simply counts from 0-up every time the module is loaded. The first object (the module) gets objectID0, the next object loaded (the first area) gets 1, next object 2 etc.etc.etc
This makes it naturally volatile. If any changes are made to the area list, the order in which they load will change, which means different objectIDs, which means the location no longer makes sense.
Most persistence functions instead save the three components above themselves, usually using the area Tag instead of ObjectID to resolve the abovementioned issue.
string MODULENAME = "YourModuleName"; void StoreLocation(object oPC) { object oArea = GetArea(oPC); vector vPos = GetPosition(oPC); float fFace = GetFacing(oPC); location lPC = Location(oArea, vPos, fFace); if(GetLocalInt(GetArea(oPC), "NORECALL") >= 1) { SendMessageToPC(oPC, "Your location cannot be saved in this area."); return; } if(GetIsObjectValid(GetNearestObjectByTag("NORECALL", oPC))){ SendMessageToPC(oPC, "Your location cannot be saved in this area."); return; } if(GetAreaFromLocation(lPC) == OBJECT_INVALID){ //Make sure the PC isn't transitioning! SendMessageToPC(oPC, "Your location cannot be saved, the location is NOT Valid!"); return; } SetLocalLocation(oPC, "CURRENTLOC", lPC); SetCampaignLocation(MODULENAME, "GENPCLOC", lPC, oPC); SendMessageToPC(oPC, "Location Saved"); }That's what I use in my location scripting, works beautifully IF the module hasn't changed, e.g. I've edited the module, and that's where locations suddenly go bad...