// Created by Zunath //#include "amp_include" #include "gun_include" #include "colors_inc" #include "rwt_include" #include "dcy_include" // Resref and tag of the player corpse placeable const string PC_CORPSE_PLC = "pc_corpse"; // Message which displays on the Respawn pop up menu const string DTH_RESPAWN_MSG = "Press 'Respawn' to return to the last place you stored your record."; // Name of the database table that stores PC corpses const string PC_CORPSE_TABLE = "pc_corpse"; // The tag of the waypoint which the storage creatures are temporarily spawned to. const string CORPSE_TEMP_WAYPOINT = "PC_CORPSES_TEMP"; // The tag and resref of the storage creature const string STORAGE_CREATURE_RESREF = "pc_storage_npc"; // This function will store a corpse's name, location and all items into a MySQL table. // On death, a player's items are all dropped into a placeable corpse. When this happens, // all of those items are copied to a temporary creature. At that point, the creature // is stored into the MySQL table. This is done because you can only store items and // creatures using MySQL tables. Rather than saving all of the items individually, this // method provides for a cleaner way for persistence. void DTH_StoreCorpse(object oCorpse); // Place on module's OnPlayerDeath event. // Handles all coding related to player death. void DTH_OnModuleDeath(); // Place on module's OnPlayerRespawn event. // Handles all coding related to player respawning. void DTH_OnModuleRespawn(); // Place on placeable corpse's OnClose event. // If the corpse's inventory is empty, destroy the placeable. void DTH_CorpseClose(); // Place on a placeable corpse's OnDisturb event. // Prevents players from placing items inside of the corpse and updates the // database entry if an item is removed from the corpse. void DTH_CorpseDisturb(); // Returns the number of corpses in the database // Used to give ID numbers to new corpses int DTH_GetCorpseCount(); // Place this script on a placeable's OnUsed event. // It will cause the player's soul to be "bound" which means when they die, they // will respawn at this location. // Their actual location is stored so there's no need for any of that nonsense with // setting tags and whatnot. void DTH_BindSoul(); void DTH_StoreCorpse(object oCorpse) { location lStorageWaypointLocation = GetLocation(GetWaypointByTag(CORPSE_TEMP_WAYPOINT)); object oStorage = CreateObject(OBJECT_TYPE_CREATURE, STORAGE_CREATURE_RESREF, lStorageWaypointLocation); int iCorpseID = GetLocalInt(oCorpse, "CORPSE_ID"); string sSQL; object oItem = GetFirstItemInInventory(oCorpse); // If there is not a single valid item in the corpse's inventory, destroy it. if(!GetIsObjectValid(oItem)) { DestroyObject(oStorage); DestroyObject(oCorpse); // Remove row from table sSQL = "DELETE FROM " + PC_CORPSE_TABLE + " WHERE ID=" +IntToString(iCorpseID); SQLExecDirect(sSQL); return; } while(GetIsObjectValid(oItem)) { // Creates the object on storage NPC and marks it as undroppable // Probably unnecessary to curse it, but included for security's sake object oCopy = CopyItem(oItem, oStorage, TRUE); SetItemCursedFlag(oCopy, TRUE); oItem = GetNextItemInInventory(oCorpse); } // Store NPC storage into the database row sSQL = "UPDATE " + PC_CORPSE_TABLE + " SET StorageNPC=%s WHERE ID=" + IntToString(iCorpseID) + ";"; SetLocalString(GetModule(), "NWNX!ODBC!SETSCORCOSQL", sSQL); StoreCampaignObject ("NWNX", "-", oStorage); // We're done with the NPC - destroy him DestroyObject(oStorage); } int DTH_GetCorpseCount() { return GetPersistentInt(GetModule(), "PC_CORPSE_COUNT", "pwdata"); } void DTH_OnModuleDeath() { object oPC = GetLastPlayerDied(); object oMod = GetModule(); object oDatabase = GetItemPossessedBy(oPC, PC_DATABASE); int iNoItems = TRUE; int iCorpseID = DTH_GetCorpseCount() + 1; location lLocation = GetLocation(oPC); string sSQL; int iLoop; int iNumDeaths = GetLocalInt(oDatabase, "DTH_NUMBER_OF_DEATHS") + 1; SetLocalInt(oDatabase, "DTH_NUMBER_OF_DEATHS", iNumDeaths); string sCorpseName = GetName(oPC) + "'s Corpse"; ClearAllFactionMembers(GetLastHostileActor(oPC), oPC); // Pop up death GUI PopUpDeathGUIPanel(oPC, TRUE, TRUE, 0, DTH_RESPAWN_MSG); object oCorpse = CreateObject(OBJECT_TYPE_PLACEABLE, PC_CORPSE_PLC, lLocation, FALSE); // Also drop their gold, if any. if(GetGold(oPC) > 0) { AssignCommand(oCorpse, TakeGoldFromCreature(GetGold(oPC), oPC, FALSE)); iNoItems = FALSE; } // Drop all inventory items into a corpse placeable on the ground object oInventory = GetFirstItemInInventory(oPC); while(GetIsObjectValid(oInventory)) { // Copy if item isn't cursed (undroppable) if(!GetItemCursedFlag(oInventory)) { string sResref = GetResRef(oInventory); // Radios - Remove variables from oPC and the radio so the next // person to pick it up can use it. if(sResref == RADIO_RESREF) { DeleteLocalInt(oDatabase, RADIO_CHANNEL); DeleteLocalInt(oInventory, RADIO_POWER); DeleteLocalInt(oInventory, RADIO_PC_ID_ENABLED_BY); SetName(oInventory, GetName(oInventory, TRUE)); } // Reduce durability, if the item has durability - REMOVED BECAUSE IT CAUSES A DUPE BUG. Fix it later //DCY_RunItemDecay(oPC, oInventory, 100, d10(1), FALSE); CopyItem(oInventory, oCorpse, TRUE); DestroyObject(oInventory); iNoItems = FALSE; } oInventory = GetNextItemInInventory(oPC); } // No items and no gold are on PC - destroy the corpse immediately. if(iNoItems == TRUE) { DestroyObject(oCorpse); return; } // Change the name of the corpse SetName(oCorpse, sCorpseName); // Change the description of the corpse placeable SetDescription(oCorpse, GetName(oPC) + "'s Corpse"); // Update corpse counter SetPersistentInt(oMod, "PC_CORPSE_COUNT", iCorpseID, 0, "pwdata"); // Mark corpse ID number SetLocalInt(oCorpse, "CORPSE_ID", iCorpseID); sCorpseName = SQLEncodeSpecialChars(sCorpseName); // Since this is a new corpse, we need to CREATE a new line in the PC_CORPSE table sSQL = "INSERT INTO " + PC_CORPSE_TABLE + " (ID, CorpseName, Location) " + " VALUES (" + IntToString(iCorpseID) + ", '" + sCorpseName + "', '" + APSLocationToString(lLocation) + "');"; SQLExecDirect(sSQL); // Now we store the items in MySQL DTH_StoreCorpse(oCorpse); } void DTH_OnModuleLoad() { location lStorageWaypointLocation = GetLocation(GetWaypointByTag(CORPSE_TEMP_WAYPOINT)); string sSQL = "SELECT ID FROM " + PC_CORPSE_TABLE + " WHERE ID > 0 ORDER BY ID ASC LIMIT 1"; SQLExecDirect(sSQL); int iCurrentID; if(SQLFetch() == SQL_SUCCESS) { iCurrentID = StringToInt(SQLGetData(1)); } else if(SQLFetch() == SQL_ERROR){return;} object oItem, oCorpse, oStorage; location lLocation; // We loop through the table until there are no more rows to get. while(iCurrentID > 0) { sSQL = "SELECT Location FROM " + PC_CORPSE_TABLE + " WHERE ID=" +IntToString(iCurrentID); SQLExecDirect(sSQL); SQLFetch(); lLocation = APSStringToLocation(SQLGetData(1)); // Create the PC corpse placeable at the stored location. oCorpse = CreateObject(OBJECT_TYPE_PLACEABLE, PC_CORPSE_PLC, lLocation); SetLocalInt(oCorpse, "CORPSE_ID", iCurrentID); // Now create the temporary storage NPC sSQL = "SELECT StorageNPC FROM " + PC_CORPSE_TABLE + " WHERE ID=" + IntToString(iCurrentID) +";"; SetLocalString(GetModule(), "NWNX!ODBC!SETSCORCOSQL", sSQL); oStorage = RetrieveCampaignObject ("NWNX", "-", lStorageWaypointLocation); // Spawn gold int iGold = GetGold(oStorage); CreateItemOnObject("nw_it_gold001", oCorpse, iGold); // Now loop through the storage NPC and copy all items to the PC corpse placeable. oItem = GetFirstItemInInventory(oStorage); while(GetIsObjectValid(oItem)) { CopyItem(oItem, oCorpse, TRUE); oItem = GetNextItemInInventory(oStorage); } // We're done with the storage NPC so let's destroy it. DestroyObject(oStorage); // Now set the name of the corpse placeable SetName(oCorpse, SQLDecodeSpecialChars(GetMySQLData(PC_CORPSE_TABLE, "CorpseName", iCurrentID))); // Now we move to the next row of the table, if possible. sSQL = "SELECT ID FROM " + PC_CORPSE_TABLE + " WHERE ID > " + IntToString(iCurrentID) + " ORDER BY ID ASC LIMIT 1"; SQLExecDirect(sSQL); if(SQLFetch() == SQL_SUCCESS) { iCurrentID = StringToInt(SQLGetData(1)); } else if(SQLFetch() == SQL_ERROR){break;} iCurrentID = StringToInt(SQLGetData(1)); } } void DTH_CorpseClose() { object oCorpse = OBJECT_SELF; object oItem = GetFirstItemInInventory(oCorpse); if(!GetIsObjectValid(oItem)) { DestroyObject(oCorpse); } } void DTH_CorpseDisturb() { object oPC = GetLastDisturbed(); if(!GetIsPC(oPC)) return; object oCorpse = OBJECT_SELF; object oItem = GetInventoryDisturbItem(); int iType = GetInventoryDisturbType(); // Player attempts to add an item to the corpse. if(iType == INVENTORY_DISTURB_TYPE_ADDED) { ActionGiveItem(oItem, oPC); FloatingTextStringOnCreature("You cannot put items into corpses.", oPC, FALSE); return; } // Otherwise, someone removed an item - update the database else { DTH_StoreCorpse(oCorpse); return; } } void DTH_BindSoul() { object oPC = GetLastUsedBy(); object oStone = OBJECT_SELF; object oDatabase = GetItemPossessedBy(oPC, PC_DATABASE); location lLocation = GetLocation(oPC); string sLocation = APSLocationToString(lLocation); SetLocalString(oDatabase, "DTH_RESPAWN_LOCATION", sLocation); FloatingTextStringOnCreature(ColorTokenGreen() + "Your record has been stored. You will respawn here if you die.", oPC, FALSE); // Play the sweet ass typewriter sound that Jez made :D PlaySound("as_sw_typewriter"); } void DTH_OnModuleRespawn() { object oPC = GetLastRespawnButtonPresser(); object oDatabase = GetItemPossessedBy(oPC, PC_DATABASE); string sLocation = GetLocalString(oDatabase, "DTH_RESPAWN_LOCATION"); location lLocation = APSStringToLocation(sLocation); // PC hasn't set a respawn point yet - send them to the default location. if(sLocation == "") { lLocation = GetLocation(GetWaypointByTag("DTH_DEFAULT_RESPAWN_POINT")); } ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectResurrection(), oPC); ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectHeal(GetMaxHitPoints(oPC) / 2), oPC); AssignCommand(oPC, ActionJumpToLocation(lLocation)); } // Error checking //void main(){}