I'm still pretty new to scripting for NWN2. I know a majoritiy of the scripting is the same as NWN1 but there are some subtle differences and then there are some big changes. Like the OnChat event.
I keep seeing the StartingConditional script example but what I don't understand is how you define the parameters for it:
int StartingConditional(object oSender, object oTarget, int iChannel, string sMessage)
{
//stuff return FALSE;
return TRUE;
}
I'm sorry if this is a rediculous question but for the life of me I just don't understand how this works. Are these parameters predefined somewhere?
Thanks in advance for any info.
OnChat troubles
Débuté par
GhostOfGod
, mars 21 2011 08:04
#1
Posté 21 mars 2011 - 08:04
#2
Posté 21 mars 2011 - 08:14
Check out this page that Pain put together on Referencing Objects in Scripts. It talks about how certain event scripts are defined. If you search that page for "OnChat" it's on there.
Having said that, I haven't found it to be useful to try to write one's own OnChat event script as it's fairly powerful and therefore easy to get wrong. I've had this end up crashing the game or not being able to chat at all. It's probably preferable to use the chat plugin for NWNX4 if you're doing this for multiplayer and specify certain stuff it should listen for. If you want to do something for SP, there might be other ways.
Having said that, I haven't found it to be useful to try to write one's own OnChat event script as it's fairly powerful and therefore easy to get wrong. I've had this end up crashing the game or not being able to chat at all. It's probably preferable to use the chat plugin for NWNX4 if you're doing this for multiplayer and specify certain stuff it should listen for. If you want to do something for SP, there might be other ways.
#3
Posté 21 mars 2011 - 08:29
Thank you very much for the info MasterChanger. There is a possibility of using that chat plugin. In the meantime though I would still like to be able to understand and use all the events that are already available. I still haven't tried to actually do anything in this particular event yet. I was just very confused by it.
Could you or anyone else provide and example of what could go wrong or crash a PW using this event? I did read something about that on the wiki page.
Thanks again.
Could you or anyone else provide and example of what could go wrong or crash a PW using this event? I did read something about that on the wiki page.
Thanks again.
Modifié par GhostOfGod, 21 mars 2011 - 08:30 .
#4
Posté 21 mars 2011 - 10:00
GhostOfGod wrote...
Could you or anyone else provide and example of what could go wrong or crash a PW using this event? I did read something about that on the wiki page.
Thanks again.
As far as I understand it (which isn't very far at all) the purpose of the OnChat event is to intercept what's being chatted. Then, using something like this plugin, or Pain's very extensive system*, you can check whether a chat command has been used, a non-common language has been spoken, or what-have-you.
However, this powerful opportunity means that you've taken a responsibility onto yourself: I'm almost positive that once you've done all you want to do with the message (filter it, chop it, mash it) you then have to actually send the message on its way. That's why you have that info on the sender, the recipient, the channel, and so forth. That's not necessarily trivial.
This isn't to discourage you from scripting things yourself or trying to understand them. I looked through a bunch of NWNX code, when I have no hope of improving any of it, because I wanted to understand it. But there are packages out there that probably already do what you want and could be customizable.
Pain has a hugely extensive language system that, though it's still in development on some of the fancier aspects, might be what you're looking for. I don't know, maybe you want this for something else entirely.
#5
Posté 21 mars 2011 - 03:02
Don't use xp chat. There is no real difference between how the in game chat and xp_chat works, except the onchat is a lot less complicated to get working, and the xp_chat is not really supported anymore.
It's simple, the game defines those parameters. You have to use that conditional, and its values magically get populated by the engine. Just use that conditional and know those values will be there, anything else is overthinking it.
If you don't process the message, the game will ignore your event. You return a boolean to control that. You only have to be careful if you send chat messages inside of chat to not end up getting circular loops where it resends them, which will basically lock up the game.
It's simple, the game defines those parameters. You have to use that conditional, and its values magically get populated by the engine. Just use that conditional and know those values will be there, anything else is overthinking it.
If you don't process the message, the game will ignore your event. You return a boolean to control that. You only have to be careful if you send chat messages inside of chat to not end up getting circular loops where it resends them, which will basically lock up the game.
#6
Posté 21 mars 2011 - 03:57
Gotcha. Thanks Pain. Yes I was waaay over thinking this. haha. I was just wanting a way to do some chat commands. One in particular for setting the description of an object(player created merchant stall). I should be able to get it working via the OnChat. Which means I now need to see if there are already some string parsing functions in NWN2 that I can use so I don't have to get crazy with GetSubString loop. But that's neither here nor there : )
Thanks for the info guys.
Thanks for the info guys.
#7
Posté 21 mars 2011 - 05:50
I have implemented onchat commands via tag based scripting, would make it a lot easier to implement commands. I can do a new chat command very quickly and it does not have a negative effect on performance.
This is all my codebase basically,but it's the latest version of everything. ( some UI files, 2da's, haks and the like are needed if all is used which are not in that link ) but i am think you would only need the CSLChat folder and the one file from bare events that does on chat, and the CSLLibrary anyway if you just use it as a script library.
http://dl.dropbox.co...riptsPrelim.zip
This includes a complete string parsing library, with a complete reference here to the above codebase, and use the link to "_CSLCore_Strings" for just the info on strings. I also keep notes of what i am working on, and releases at the NW Citadel Project- Scripting and Functions
subforum.
Example chat tag script is as follows to list the spells a player has which uses my CSLNth_GetNthElement function to parse the string the player used. All you need is the one string library file named _CSLCore_Strings.nss if you just want to have string parsing and roll your own, this script uses more advanced functions, cached data objects, etc to get around some engine limitations. Regardless it might be helpful for you to look thru something already working just to pillage things for your own use or see how things others do things, and i have multiple methods of parsing, pushing, popping, shifting, unshifting, sorting and the like for strings all of which are vetted and documented for use on a pw in a single file which makes them easy to find.
This is all my codebase basically,but it's the latest version of everything. ( some UI files, 2da's, haks and the like are needed if all is used which are not in that link ) but i am think you would only need the CSLChat folder and the one file from bare events that does on chat, and the CSLLibrary anyway if you just use it as a script library.
http://dl.dropbox.co...riptsPrelim.zip
This includes a complete string parsing library, with a complete reference here to the above codebase, and use the link to "_CSLCore_Strings" for just the info on strings. I also keep notes of what i am working on, and releases at the NW Citadel Project- Scripting and Functions
subforum.
Example chat tag script is as follows to list the spells a player has which uses my CSLNth_GetNthElement function to parse the string the player used. All you need is the one string library file named _CSLCore_Strings.nss if you just want to have string parsing and roll your own, this script uses more advanced functions, cached data objects, etc to get around some engine limitations. Regardless it might be helpful for you to look thru something already working just to pillage things for your own use or see how things others do things, and i have multiple methods of parsing, pushing, popping, shifting, unshifting, sorting and the like for strings all of which are vetted and documented for use on a pw in a single file which makes them easy to find.
//::////////////////////////////////////////////////////////////////////////:://
//:: Chat Handler Script :://
//::////////////////////////////////////////////////////////////////////////:://
/*
This code assumes a player does not have a really complicated builds, and will focus on the first class they took which can actually use the given spell which makes they syntax very easy.
*/
#include "_SCInclude_Chat"
#include "_HkSpell"
void main()
{
object oDM = CSLGetChatSender();
object oTarget = CSLGetChatTarget();
string sParameters = CSLGetChatParameters();
if ( !CSLCheckPermissions( oDM, CSL_PERM_DMONLY ) )
{
return;
}
int iclass = 255 ;
string sclass = GetStringLowerCase(CSLNth_GetNthElement( sParameters, 1, " "));
string sLevel = CSLNth_GetNthElement( sParameters, 2, " ");
if ( sclass == "help" )
{
SendMessageToPC( oDM,"Usage: listspell [class] [levels]");
SendMessageToPC( oDM,"Example: listspell wizard 3-6");
SendMessageToPC( oDM," shows wizard spells from levels 3 to 6");
SendMessageToPC( oDM,"Example: listspell bard 5");
SendMessageToPC( oDM," shows bard spells at level 5");
SendMessageToPC( oDM,"levels can be 1 to 10");
SendMessageToPC( oDM,"classes: wizard, sorcerer, cleric, favoredsoul, druid, spiritshaman, paladin, ranger, warlock");
SendMessageToPC( oDM,"No parameters will show all spells the current target can use out of their first class that can cast spells spell book");
return;
}
if ( sclass == "" )
{
SCCacheStats( oTarget );
iclass = GetLocalInt(oTarget, "SC_iBestCasterclass" );
sclass = CSLGetclassesDataName( iclass );
}
else if ( sclass == "wizard" ) { iclass = class_TYPE_WIZARD; }
else if ( sclass == "sorcerer" ) { iclass = class_TYPE_SORCERER; }
else if ( sclass == "sorceror" ) { iclass = class_TYPE_SORCERER; }
else if ( sclass == "cleric" ) { iclass = class_TYPE_CLERIC; }
else if ( sclass == "favoredsoul" ) { iclass = class_TYPE_FAVORED_SOUL; }
else if ( sclass == "druid" ) { iclass = class_TYPE_DRUID; }
else if ( sclass == "spiritshaman" ) { iclass = class_TYPE_SPIRIT_SHAMAN; }
else if ( sclass == "paladin" ) { iclass = class_TYPE_PALADIN; }
else if ( sclass == "ranger" ) { iclass = class_TYPE_RANGER; }
else if ( sclass == "warlock" ) { iclass = class_TYPE_WARLOCK; }
object oCurrentSpellBook = CSLGetSpellBookByclass( iclass );
if ( !GetIsObjectValid( oCurrentSpellBook ) )
{
SendMessageToPC( oDM, "No Valid Spell Book Available" );
return;
}
int iStartRow = 0;
int iEndRow = CSLDataTableCount( oCurrentSpellBook )-2;
int iStartingLevel = 0;
int iEndingLevel = 10;
if ( GetLocalInt(oCurrentSpellBook, "DATATABLE_FULLYSORTED" ) )
{
if ( sLevel == "" && GetIsObjectValid(oTarget) )
{
iStartingLevel = 0;
SCCacheStats( oTarget );
int iMaxLevel = GetLocalInt(oTarget, "SC_iBestCasterMaxSpellLevel" );
if ( iMaxLevel > 0 )
{
iEndingLevel = iMaxLevel;
}
}
else if ( sLevel == "" )
{
iStartingLevel = 0;
iEndingLevel = 10;
}
else if ( FindSubString( sLevel, "-" ) > -1 )
{
string sStartLevel = CSLNth_GetNthElement( sLevel, 1, "-");
string sEndLevel = CSLNth_GetNthElement( sLevel, 1, "-");
if ( sStartLevel != "" && sEndLevel != "" )
{
iStartingLevel = StringToInt(sStartLevel);
iEndingLevel = StringToInt(sEndLevel);
}
else if ( sStartLevel != "" )
{
iStartingLevel = StringToInt(sStartLevel);
iEndingLevel = StringToInt(sStartLevel);
}
else if ( sEndLevel != "" )
{
iStartingLevel = StringToInt(sEndLevel);
iEndingLevel = StringToInt(sEndLevel);
}
}
else if ( CSLGetIsNumber(sLevel) )
{
iStartingLevel = StringToInt(sLevel);
iEndingLevel = StringToInt(sLevel);
}
if ( iStartingLevel < 0 )
{
iStartingLevel = 0;
}
if ( iEndingLevel > 10 ) // 10 is epic
{
iEndingLevel = 10;
}
if ( iEndingLevel < iStartingLevel )
{
iEndingLevel = iStartingLevel;
}
if ( iStartingLevel != 0 && iEndingLevel != 10 )
{
// We only need to get the range to use IF they are not using the entire range, the variables are initialized with the entire spell list from 0 to the count in the table.
// this logic assumes that a random spell level will have no spells in it, like for epic spells,warlock only go up to level 4, and some classes don't have level 0 spells like warlock again
// so what i am doing is if it comes back with -1
// go up first till you find a level with actual spells
int iTempStartRow = GetLocalInt(oCurrentSpellBook, "DATATABLE_SORTSTART_"+IntToString(iStartingLevel) );
while ( iTempStartRow == -1 && iStartingLevel < iEndingLevel && iStartingLevel < 10 ) // note i have this set to 9 and not 10 because i know it's going to ++ it again and put it at 10 when it's 9
{
iStartingLevel++;
iTempStartRow = GetLocalInt(oCurrentSpellBook, "DATATABLE_SORTSTART_"+IntToString(iStartingLevel) );
}
// now descend until you find a level with working spells
int iTempEndRow = GetLocalInt(oCurrentSpellBook, "DATATABLE_SORTSTART_"+IntToString(iEndingLevel) );
while ( iTempEndRow == -1 && iEndingLevel > iStartingLevel && iEndingLevel > 0 ) // note i have this set to be 1 and not zero since i know that it's going to -- it again to put it at zero when it's 1
{
iEndingLevel--;
iTempEndRow = GetLocalInt(oCurrentSpellBook, "DATATABLE_SORTSTART_"+IntToString(iEndingLevel) );
}
if ( iTempStartRow != -1 && iTempEndRow != -1 )
{
iTempEndRow += GetLocalInt(oCurrentSpellBook, "DATATABLE_SORTQUANTITY_"+IntToString(iEndingLevel) )-1;
iStartRow = iTempStartRow;
iEndRow = iTempEndRow;
}
}
}
SendMessageToPC( oDM, "<b>"+CSLStringToProper(sclass)+" Spellbook</b>");
//SendMessageToPC( oDM, "iStartingLevel="+IntToString( iStartingLevel )+" iEndingLevel="+IntToString( iEndingLevel )+" iStartRow="+IntToString( iStartRow )+" iEndRow="+IntToString( iEndRow ) );
DelayCommand( 0.1f, CSLListclassSpellBook( oCurrentSpellBook, oDM, oTarget, iStartRow, iEndRow ) );
}
#8
Posté 22 mars 2011 - 06:46
So I ended up doing this for what I needed:
int StartingConditional(object oSender, object oTarget, int nChannel, string sMessage)
{
// allow the text to be spoken normally for objects other than
// PCs, and for any channel other than the standard 'talk' channel.
if (!GetIsPC(oSender) || nChannel != CHAT_MODE_TALK )
{
return TRUE;
}
string sCheck = GetStringLeft(sMessage, 8);
if (sCheck == "!myshop:")
{
int iChars = 1;
string sSub = GetSubString(sMessage, iChars - 1, iChars);
while (sSub != "")
{
iChars++;
sSub = GetSubString(sMessage, iChars - 1, iChars);
}
string sDescription = GetSubString(sMessage, 8, iChars);
object oItem = GetItemPossessedBy(oSender, "my_stall");
SetDescription(GetLocalObject(oItem, "MY_STALL"), sDescription);
return FALSE;
}
// if we don't find a space, then the PC has spoken only one word.
// in this example, this means we'll assume they want to execute a
// script by the name of whatever they said.
/*if (FindSubString(sMessage, " ") == -1)
{
SendMessageToPC(oSender, "Executing script by name of: " + sMessage);
ExecuteScript(sMessage, oSender);
return FALSE; // do not let the say the text aloud.
}*/
return TRUE; // treat all other cases normally, allowing the text to be spoken aloud.
}
So when a player types "!myshop:I am now selling a +20 Vorpal sword for 1bazillion gold." it sets their merchant stalls description to "I am now selling a +20 Vorpal sword for 1bazillion gold."
It works! So far so good.
Thanks again for the info and explanations.
int StartingConditional(object oSender, object oTarget, int nChannel, string sMessage)
{
// allow the text to be spoken normally for objects other than
// PCs, and for any channel other than the standard 'talk' channel.
if (!GetIsPC(oSender) || nChannel != CHAT_MODE_TALK )
{
return TRUE;
}
string sCheck = GetStringLeft(sMessage, 8);
if (sCheck == "!myshop:")
{
int iChars = 1;
string sSub = GetSubString(sMessage, iChars - 1, iChars);
while (sSub != "")
{
iChars++;
sSub = GetSubString(sMessage, iChars - 1, iChars);
}
string sDescription = GetSubString(sMessage, 8, iChars);
object oItem = GetItemPossessedBy(oSender, "my_stall");
SetDescription(GetLocalObject(oItem, "MY_STALL"), sDescription);
return FALSE;
}
// if we don't find a space, then the PC has spoken only one word.
// in this example, this means we'll assume they want to execute a
// script by the name of whatever they said.
/*if (FindSubString(sMessage, " ") == -1)
{
SendMessageToPC(oSender, "Executing script by name of: " + sMessage);
ExecuteScript(sMessage, oSender);
return FALSE; // do not let the say the text aloud.
}*/
return TRUE; // treat all other cases normally, allowing the text to be spoken aloud.
}
So when a player types "!myshop:I am now selling a +20 Vorpal sword for 1bazillion gold." it sets their merchant stalls description to "I am now selling a +20 Vorpal sword for 1bazillion gold."
It works! So far so good.
Thanks again for the info and explanations.
Modifié par GhostOfGod, 22 mars 2011 - 06:50 .





Retour en haut







