Heres the script, ton o files:The Bulletin Board System allows players to read and post messages.
The system consists of a Scribe (NPC) and the Bulletin Board (Signpost placeable).
Players can talk to the Scribe and get an item called Bulletin Board Notice. During conversation, the Scribe will ask a player to speak a title and a message, clicking Done after each. To speak, the player has to type a message and press Enter, as if addressing another player. Upon successful input, the item is given to the player. The player can then walk up to the Bulletin Board and select the Post Message dialogue option to have their message entered on the Bulletin Board.
The Bulletin Board displays messages in reverse chronological order so the latest message appears first. Players can page through the listings which consist of the title and name of the poster. The default page length is 5 entries, but it can be set for any number between 1 and 10. When a player posts a message, the Bulletin Board Notice disappears from their inventory. If the player has more than one, one will be taken at random. Once the maximum number of messages for the Bulletin Board has been reached, older messages will be deleted to make space for new ones. The default maximum is 25 messages. To change these defaults,
look at the "void bbs_initiate" function inside the "bbs_include" script.
All Bulletin Boards with the same tag share the messages. If you change the tags you end up with separate Bulletin Boards. For example, you could have it so the Bulletin Boards in the same city show the same messages, but be different from city to city.
The Bulletin Board System uses custom tokens in the range 3671 - 3699. Hopefully these won't conflict with any you might be using.
The "bbs_include" script contains a function "void bbs_add_notice". You can use it to post messages from NPCs to the Bulletin Board and make it seem more lively, perhaps with minor quests. For more information read the comments above the function.
Because the "bbs_include" script is used as an include file, it does not have a "void main", so whenever you compile that script or build your module you will get a compiling error in it. This is normal and does not affect operation in any way.
The code for capturing a PC spoken string came from a post by gsmithcat in the NWN Scripting forum.
Serban Oprescu
serban@pixelsharp.com
http://www.pixelsharp.com/projects/nwn/
bbs_have_notice
int StartingConditional()
{
if (GetItemPossessedBy(GetPCSpeaker(), "bbs_notice") != OBJECT_INVALID) {
return TRUE;
}
return FALSE;
}
bbs_include
//This is an include file. Upon building your module you will get
//a compile error in this file. That is normal and does not
//affect the operation of the bulletin board.
void bbs_do_board_stats();
void bbs_initiate(object oBBS);
int bbs_can_show(int WhichEntry);
void bbs_change_page(int PageChange);
void bbs_select_entry(int WhichEntry);
void bbs_add_notice(object oBBS, string sPoster, string sTitle, string sMessage, string sDate);
//Loads into tokens the stats for a board
void bbs_do_board_stats() {
object oBBS = GetLocalObject(GetModule(), "BBS_" + GetTag(OBJECT_SELF));
int PageSize = GetLocalInt(oBBS, "PageSize");
int TotalItems = GetLocalInt(oBBS, "TotalItems");
int PageIndex = GetLocalInt(oBBS, "PageIndex") + 1;
SetCustomToken(3671, IntToString(TotalItems));
if (TotalItems == 0) {PageIndex = 0;}
SetCustomToken(3672, IntToString(PageIndex));
SetCustomToken(3673, IntToString((TotalItems + PageSize - 1) / PageSize));
}
//Initiates a bulletin board's settings if neccessary
void bbs_initiate(object oBBS) {
string sBBS = "BBS_" + GetTag(oBBS);
object myBBS = GetLocalObject(GetModule(), sBBS);
if (!GetIsObjectValid(myBBS)) {
SetLocalObject(GetModule(), sBBS, oBBS);
myBBS = oBBS;
}
if (GetLocalInt(myBBS, "PageSize") == 0) {
SetLocalInt(myBBS, "MaxItems", 25);
SetLocalInt(myBBS, "PageSize", 5);
}
}
//Determines whether a dialogue option is visible in conversation
int bbs_can_show(int WhichEntry) {
object oBBS = GetLocalObject(GetModule(), "BBS_" + GetTag(OBJECT_SELF));
int PageSize = GetLocalInt(oBBS, "PageSize");
int nSpot = GetLocalInt(oBBS, "PageIndex") * PageSize + WhichEntry;
if(nSpot <= GetLocalInt(oBBS, "TotalItems") && WhichEntry <= PageSize) {return TRUE;}
return FALSE;
}
//Moves the page by the required PageFlip:
//0 to reload page, -1 for previous page, 1 for next page
void bbs_change_page(int PageFlip) {
object oBBS = GetLocalObject(GetModule(), "BBS_" + GetTag(OBJECT_SELF));
int PageSize = GetLocalInt(oBBS, "PageSize");
int TotalItems = GetLocalInt(oBBS, "TotalItems");
int MaxItems = GetLocalInt(oBBS, "MaxItems");
int LatestItem = GetLocalInt(oBBS, "LatestItem");
int PageIndex = GetLocalInt(oBBS, "PageIndex") + 1 * PageFlip;
if (PageIndex < 0) {PageIndex = 0;}
SetLocalInt(oBBS, "PageIndex", PageIndex);
string sInfo;
int iLoop;
int iNotice;
for (iLoop = 0; iLoop < PageSize; iLoop++) {
iNotice = LatestItem - PageIndex * PageSize - iLoop;
if (iNotice < 1) {iNotice = MaxItems + iNotice;}
sInfo = GetLocalString(oBBS, "Title" + IntToString(iNotice));
SetCustomToken(3680 + iLoop, sInfo);
sInfo = GetLocalString(oBBS, "Poster" + IntToString(iNotice));
if (((PageIndex * PageSize + iLoop + 2) > TotalItems) || (iLoop == PageSize - 1)){
sInfo = sInfo + "\n ";
}
SetCustomToken(3690 + iLoop, sInfo);
}
bbs_do_board_stats();
SetCustomToken(3674, "");
SetCustomToken(3675, "");
SetCustomToken(3676, "");
SetCustomToken(3677, "");
SetCustomToken(3678, "");
}
//Displays the selected post
void bbs_select_entry(int WhichEntry) {
object oBBS = GetLocalObject(GetModule(), "BBS_" + GetTag(OBJECT_SELF));
int PageSize = GetLocalInt(oBBS, "PageSize");
int MaxItems = GetLocalInt(oBBS, "MaxItems");
int LatestItem = GetLocalInt(oBBS, "LatestItem");
int PageIndex = GetLocalInt(oBBS, "PageIndex");
int iNotice = LatestItem - PageIndex * PageSize - WhichEntry + 1;
if (iNotice < 1) {iNotice = MaxItems + iNotice;}
string sNotice = IntToString(iNotice);
bbs_do_board_stats();
SetCustomToken(3674, "\n\n" + GetLocalString(oBBS, "Title" + sNotice) + "\nBy: ");
SetCustomToken(3675, GetLocalString(oBBS, "Poster" + sNotice));
SetCustomToken(3676, " On: ");
SetCustomToken(3677, GetLocalString(oBBS, "Date" + sNotice));
SetCustomToken(3678, "\n" + GetLocalString(oBBS, "Message" + sNotice));
}
//Adds a post to the bulletin board. This can be called at any time
//so you can insert your own notices. If you don't specify a sDate,
//it will use the current game time. The proper format for sDate is
//something like "6/30/1373 11:58". If you do call this function
//on your own, make sure you do a bbs_initiate() first.
void bbs_add_notice(object oBBS, string sPoster, string sTitle, string sMessage, string sDate)
{
oBBS = GetLocalObject(GetModule(), "BBS_" + GetTag(oBBS));
if (sDate == "") {
sDate = IntToString(GetTimeMinute());
if (GetStringLength(sDate) == 1) {sDate = "0" + sDate;}
sDate = IntToString(GetCalendarMonth()) + "/" + IntToString(GetCalendarDay()) + "/" + IntToString(GetCalendarYear()) + " " + IntToString(GetTimeHour()) + ":" + sDate;
}
int MaxItems = GetLocalInt(oBBS, "MaxItems");
int TotalItems = GetLocalInt(oBBS, "TotalItems");
int nSpot = TotalItems + 1;
if (nSpot > MaxItems) {
nSpot = GetLocalInt(oBBS, "LatestItem") + 1;
if (nSpot > MaxItems) {nSpot = nSpot - MaxItems;}
}
SetLocalString(oBBS, "Poster" + IntToString(nSpot), sPoster);
SetLocalString(oBBS, "Date" + IntToString(nSpot), sDate);
SetLocalString(oBBS, "Title" + IntToString(nSpot), sTitle);
SetLocalString(oBBS, "Message" + IntToString(nSpot), sMessage);
SetLocalInt(oBBS, "LatestItem", nSpot);
if (MaxItems > TotalItems) {SetLocalInt(oBBS, "TotalItems", TotalItems + 1);}
}
bbs_page_back
#include "bbs_include"
void main()
{
bbs_change_page(-1);
}
bbs_page_next
#include "bbs_include"
void main()
{
bbs_change_page(1);
}
bbs_post_notice
#include "bbs_include"
void main()
{
object oPC = GetPCSpeaker();
object oNotice = GetItemPossessedBy(oPC, "bbs_notice");
if (GetIsObjectValid(oNotice)) {
string nPoster = GetName(oPC) + " (" + GetPCPlayerName(oPC) + ")";
string nTitle = GetLocalString(oNotice, "Title");
string nMessage = GetLocalString(oNotice, "Message");
ActionTakeItem(oNotice, oPC);
bbs_add_notice(OBJECT_SELF, nPoster, nTitle, nMessage, "");
bbs_change_page(-1000);
}
}
bbs_scrive_conv
//::///////////////////////////////////////////////
//:: SetListeningPatterns
//:: NW_C2_DEFAULT4
//:: Copyright (c) 2001 Bioware Corp.
//:://////////////////////////////////////////////
/*
Determines the course of action to be taken
by the generic script after dialogue or a
shout is initiated.
*/
//:://////////////////////////////////////////////
//:: Created By: Preston Watamaniuk
//:: Created On: Oct 24, 2001
//:://////////////////////////////////////////////
#include "NW_I0_GENERIC"
void main()
{
int nMatch = GetListenPatternNumber();
object oShouter = GetLastSpeaker();
object oIntruder;
if (nMatch == -1 && GetCommandable(OBJECT_SELF))
{
ClearAllActions();
BeginConversation();
}
else
if(nMatch == 777 && GetIsObjectValid(oShouter) && GetIsPC(oShouter))
// && GetIsFriend(oShouter)
{
if (oShouter == GetLocalObject(OBJECT_SELF, "Customer")) {
string sSaid = GetMatchedSubstring(0);
SetLocalString(OBJECT_SELF, "Stack", sSaid);
}
}
}
bbs_scribe_give
void main()
{
object Notice = CreateItemOnObject("bbs_notice_bp", GetPCSpeaker());
if (Notice != OBJECT_INVALID) {
SetLocalString(Notice, "Title", GetLocalString(OBJECT_SELF, "Title"));
SetLocalString(Notice, "Message", GetLocalString(OBJECT_SELF, "Message"));
}
}
bbs_scribe_m_get
void main()
{
string sTalk = GetLocalString(OBJECT_SELF, "Stack");
if (sTalk != "") {
if (GetStringLength(sTalk) > 200) {sTalk = GetStringLeft(sTalk, 200);}
SetLocalString(OBJECT_SELF, "Message", sTalk);
SetLocalString(OBJECT_SELF, "Stack", "");
}
}
bbs_scribe_m_yes
int StartingConditional()
{
if (GetLocalString(OBJECT_SELF, "Message") != "") {
return TRUE;
}
return FALSE;
}
bbs_scribe_spawn
//::///////////////////////////////////////////////
//:: Default: On Spawn In
//:: NW_C2_DEFAULT9
//:: Copyright (c) 2001 Bioware Corp.
//:://////////////////////////////////////////////
/*
Determines the course of action to be taken
after having just been spawned in
*/
//:://////////////////////////////////////////////
//:: Created By: Preston Watamaniuk
//:: Created On: Oct 25, 2001
//:://////////////////////////////////////////////
#include "NW_O2_CONINCLUDE"
#include "NW_I0_GENERIC"
void main()
{
// OPTIONAL BEHAVIORS (Comment In or Out to Activate ) ****************************************************************************
//SetSpawnInCondition(NW_FLAG_SPECIAL_CONVERSATION);
//SetSpawnInCondition(NW_FLAG_SPECIAL_COMBAT_CONVERSATION);
// This causes the creature to say a special greeting in their conversation file
// upon Perceiving the player. Attach the [NW_D2_GenCheck.nss] script to the desired
// greeting in order to designate it. As the creature is actually saying this to
// himself, don't attach any player responses to the greeting.
//SetSpawnInCondition(NW_FLAG_SHOUT_ATTACK_MY_TARGET);
// This will set the listening pattern on the NPC to attack when allies call
//SetSpawnInCondition(NW_FLAG_STEALTH);
// If the NPC has stealth and they are a rogue go into stealth mode
//SetSpawnInCondition(NW_FLAG_SEARCH);
// If the NPC has Search go into Search Mode
//SetSpawnInCondition(NW_FLAG_SET_WARNINGS);
// This will set the NPC to give a warning to non-enemies before attacking
//SetSpawnInCondition(NW_FLAG_SLEEP);
//Creatures that spawn in during the night will be asleep.
//SetSpawnInCondition(NW_FLAG_DAY_NIGHT_POSTING);
//SetSpawnInCondition(NW_FLAG_APPEAR_SPAWN_IN_ANIMATION);
//SetSpawnInCondition(NW_FLAG_IMMOBILE_AMBIENT_ANIMATIONS);
//SetSpawnInCondition(NW_FLAG_AMBIENT_ANIMATIONS);
//This will play Ambient Animations until the NPC sees an enemy or is cleared.
//NOTE that these animations will play automatically for Encounter Creatures.
// NOTE: ONLY ONE OF THE FOLOOWING ESCAPE COMMANDS SHOULD EVER BE ACTIVATED AT ANY ONE TIME.
//SetSpawnInCondition(NW_FLAG_ESCAPE_RETURN); // OPTIONAL BEHAVIOR (Flee to a way point and return a short time later.)
//SetSpawnInCondition(NW_FLAG_ESCAPE_LEAVE); // OPTIONAL BEHAVIOR (Flee to a way point and do not return.)
//SetSpawnInCondition(NW_FLAG_TELEPORT_LEAVE); // OPTIONAL BEHAVIOR (Teleport to safety and do not return.)
//SetSpawnInCondition(NW_FLAG_TELEPORT_RETURN); // OPTIONAL BEHAVIOR (Teleport to safety and return a short time later.)
// CUSTOM USER DEFINED EVENTS
/*
The following settings will allow the user to fire one of the blank user defined events in the NW_D2_DefaultD. Like the
On Spawn In script this script is meant to be customized by the end user to allow for unique behaviors. The user defined
events user 1000 - 1010
*/
//SetSpawnInCondition(NW_FLAG_HEARTBEAT_EVENT); //OPTIONAL BEHAVIOR - Fire User Defined Event 1001
//SetSpawnInCondition(NW_FLAG_PERCIEVE_EVENT); //OPTIONAL BEHAVIOR - Fire User Defined Event 1002
//SetSpawnInCondition(NW_FLAG_ATTACK_EVENT); //OPTIONAL BEHAVIOR - Fire User Defined Event 1005
//SetSpawnInCondition(NW_FLAG_DAMAGED_EVENT); //OPTIONAL BEHAVIOR - Fire User Defined Event 1006
//SetSpawnInCondition(NW_FLAG_DISTURBED_EVENT); //OPTIONAL BEHAVIOR - Fire User Defined Event 1008
//SetSpawnInCondition(NW_FLAG_END_COMBAT_ROUND_EVENT); //OPTIONAL BEHAVIOR - Fire User Defined Event 1003
//SetSpawnInCondition(NW_FLAG_ON_DIALOGUE_EVENT); //OPTIONAL BEHAVIOR - Fire User Defined Event 1004
//SetSpawnInCondition(NW_FLAG_DEATH_EVENT); //OPTIONAL BEHAVIOR - Fire User Defined Event 1007
// DEFAULT GENERIC BEHAVIOR (DO NOT TOUCH) *****************************************************************************************
SetListenPattern(OBJECT_SELF, "**", 777); //listen to all text
SetListening(OBJECT_SELF, TRUE); //be sure NPC is listening
//SetListeningPatterns(); // Goes through and sets up which shouts the NPC will listen to.
WalkWayPoints(); // Optional Parameter: void WalkWayPoints(int nRun = FALSE, float fPause = 1.0)
// 1. Looks to see if any Way Points in the module have the tag "WP_" + NPC TAG + "_0X", if so walk them
// 2. If the tag of the Way Point is "POST_" + NPC TAG the creature will return this way point after
// combat.
GenerateNPCTreasure(); //* Use this to create a small amount of treasure on the creature
}
bbs_scribe_start
void main()
{
SetLocalObject(OBJECT_SELF, "Customer", GetPCSpeaker());
SetLocalString(OBJECT_SELF, "Stack", "");
SetLocalString(OBJECT_SELF, "Title", "");
SetLocalString(OBJECT_SELF, "Message", "");
}
bbs_scribe_stop
void main()
{
SetLocalObject(OBJECT_SELF, "Customer", OBJECT_INVALID);
}
bbs_scribe_t_get
void main()
{
string sTalk = GetLocalString(OBJECT_SELF, "Stack");
if (sTalk != "") {
if (GetStringLength(sTalk) > 30) {sTalk = GetStringLeft(sTalk, 30);}
SetLocalString(OBJECT_SELF, "Title", sTalk);
SetLocalString(OBJECT_SELF, "Stack", "");
}
}
bbs_scribe_t_yes
int StartingConditional()
{
if (GetLocalString(OBJECT_SELF, "Title") != "") {
return TRUE;
}
return FALSE;
}
bbs_select_01
#include "bbs_include"
void main()
{
bbs_select_entry(1);
}
bbs_select_02
#include "bbs_include"
void main()
{
bbs_select_entry(2);
}
bbs_select_03
#include "bbs_include"
void main()
{
bbs_select_entry(3);
}
bbs_select_04
#include "bbs_include"
void main()
{
bbs_select_entry(4);
}
bbs_select_05
#include "bbs_include"
void main()
{
bbs_select_entry(5);
}
bbs_select_06
#include "bbs_include"
void main()
{
bbs_select_entry(6);
}
bbs_select_07
#include "bbs_include"
void main()
{
bbs_select_entry(7);
}
bbs_select_08
#include "bbs_include"
void main()
{
bbs_select_entry(8);
}
bbs_select_09
#include "bbs_include"
void main()
{
bbs_select_entry(9);
}
bbs_select_10
#include "bbs_include"
void main()
{
bbs_select_entry(10);
}
bbs_show_01
#include "bbs_include"
int StartingConditional()
{
return bbs_can_show(1);
}
bbs_show_02
#include "bbs_include"
int StartingConditional()
{
return bbs_can_show(2);
}
bbs_show_03
#include "bbs_include"
int StartingConditional()
{
return bbs_can_show(3);
}
bbs_show_04
#include "bbs_include"
int StartingConditional()
{
return bbs_can_show(4);
}
bbs_show_05
#include "bbs_include"
int StartingConditional()
{
return bbs_can_show(5);
}
bbs_show_06
#include "bbs_include"
int StartingConditional()
{
return bbs_can_show(6);
}
bbs_show_07
#include "bbs_include"
int StartingConditional()
{
return bbs_can_show(7);
}
bbs_show_08
#include "bbs_include"
int StartingConditional()
{
return bbs_can_show(8);
}
bbs_show_09
#include "bbs_include"
int StartingConditional()
{
return bbs_can_show(9);
}
bbs_show_10
#include "bbs_include"
int StartingConditional()
{
return bbs_can_show(10);
}
bbs_show_back
int StartingConditional()
{
object oBBS = GetLocalObject(GetModule(), "BBS_" + GetTag(OBJECT_SELF));
int PageIndex = GetLocalInt(oBBS, "PageIndex");
if (PageIndex == 0) {
return FALSE;
}
return TRUE;
}
bbs_show_next
int StartingConditional()
{
object oBBS = GetLocalObject(GetModule(), "BBS_" + GetTag(OBJECT_SELF));
int PageSize = GetLocalInt(oBBS, "PageSize");
int PageIndex = GetLocalInt(oBBS, "PageIndex");
int TotalItems = GetLocalInt(oBBS, "TotalItems");
if (TotalItems > (PageIndex + 1) * PageSize) {
return TRUE;
}
return FALSE;
}
bbs_start
#include "bbs_include"
void main()
{
object oPC = GetLastUsedBy();
if (GetIsPC(oPC)) {
bbs_initiate(OBJECT_SELF);
//SetLocalInt(GetLocalObject(GetModule(), "BBS_" + GetTag(OBJECT_SELF)), "PageIndex", 0);
bbs_change_page(-1000);
ActionStartConversation(oPC);
}
}