Join us in Outworldz at www.outworldz.com:9000 or follow us:

Search dozens of selected web sites for OpenSim and LSL script

New! Script Meta-Search will search thousands of scripts here and at other sites for LSL or Opensim scripts.
Loading

Want to add a script or a project? Upload it and a half million people will see it and your name here this year.

Home   Show All
Category: Contributor: Creator
ChatBot NPC Chatbot for Opensim  

NPC Chatbot for Opensim

Sample Apperance notecard

Category: ChatBot
By : Ferd Frederix
Created: 2016-07-27 Edited: 2016-07-27
Worlds: Second Life

the Zip file

Download all files for NPC Chatbot for Opensim
Contents are in zip format, with .LSL (text) source code and LSLEdit (text + Solution) formats.
Get file # 1. avatar_type.bvh
Get file # 2. Chatbot Controller.lsl
Get file # 3. [81181 bytes] Contents.png
Get file # 4. express_anger.bvh
Get file # 5. express_bored.bvh
Get file # 6. express_cry.bvh
Get file # 7. express_embarrased.bvh
Get file # 8. express_laugh.bvh
Get file # 9. express_repulsed.bvh
Get file # 10. express_sad.bvh
Get file # 11. express_shrug.bvh
Get file # 12. express_surprise.bvh
Get file # 13. express_wink.bvh
Get file # 14. express_worry.bvh
Get file # 15. NPC Control Script.lsl
Get file # 16. README.txt
Get file # 17. Sit.bvh
Get file # 18. Stand.bvh
Get file # 19. Walk.bvh

This script by Ferd Frederix may be used in any manner, modified, and republished.  Unless specified otherwise, my scripts are always free and open source.  Objects made with these scripts may be sold with no restrictions.  All I ask is that you point others to this location should they ask you about it and to not sell this script, unless it is for $0 L. Please help improve my work by reporting bugs and improvements.

1 Appearance notecard will be made here

NPC Chatbot for Opensim

This chatbot is for OpenSim Only. It only works on NPC's with a modified All-In-One NPC script, which is also included
5,000 chats are free.

Category: ChatBot
By : Ferd Frederix
Created: 2016-07-27 Edited: 2016-07-27
Worlds: Second Life


This script by Ferd Frederix may be used in any manner, modified, and republished.  Unless specified otherwise, my scripts are always free and open source.  Objects made with these scripts may be sold with no restrictions.  All I ask is that you point others to this location should they ask you about it and to not sell this script, unless it is for $0 L. Please help improve my work by reporting bugs and improvements.

1 // Chatbot uses PersonalityForge. Get a free account at http://www.personalityforge.com.
2 // first, get a free account at http://www.personalityforge.com.
3 // Get an API ID, and add it to the apiKey :
4
5 string apiKey = "BlahBlah"; // your supplied apiKey from your Chat Bot API subscription
6
7
8 // Add the domain or IP of your OpenSim server to the list of authorized domains at http://www.personalityforge.com/botland/myapi.php
9 // Add a checkmark to the "Enable Simple API" in your account.
10 // Click on the Simple API tab and pick a chatbot ID from the list of chatbots under the heading "Selecting A Chat Bot ID"
11 // for example, Countess Elvira is 99232. Put that in chatBot ID below.
12 // Sex Bot Ciran is 100387.
13 // 754 is Liddora a sexy tart
14
15 string chatBotID = "23958"; // the ID of the chat bot you're talking to
16
17 // The first time, you may want to test it with this set to TRUE
18 integer debug = FALSE; // Set this TRUE to see the gory details
19
20 // more constants below
21 integer greeting = TRUE; // if TRUE, say hello when anyone comes up.
22 integer lookat = FALSE;
23
24 ////////// REMOVE THESE IN WORLD ////////////////
25 ///////// LSLEDIT DEBUG ONLY////////////////////
26 //osNpcSetRot(key npc, rotation rot) {llSay(0,"Roatating to " + (vector) llRot2Euler(rot));}
27 //osNpcStopAnimation(key npc, string animation) {llSay(0,"Stopped " + animation);}
28 //osNpcPlayAnimation(key npc, string animation) {llSay(0,"Playing " + animation);}
29 //osNpcSay(string speach) {llSay(0,speach);}
30 /////////////////////////////////////////////////////////////////////
31
32 // various tuneable code bits
33 float range = 35; // haw far away an avatar is before we greet them/. No point in making this more than 20, that cannot hear us after that
34 float wpm = 85; // 33 wpm = 2.75 cps @ 5 chars per word for a typical avatar to type with.
35 // Larger numbers make your NPC answer quicker.
36 float cps;
37 integer emotionchannel = 199; // a secret channel that the chatbot sends emotion strings on.
38 // You can listen for these with other scripts worn by your chatbot, and animate something to show how your chat bot is feeling.
39 // this is also sent on Link Message Number 1 to all scripts in your chatbox prim
40
41 // global variables
42 key npcKey ; // the NPC wearing this
43 string npcName; // ditto
44 integer starttime; // the time we started typing
45 key requestid; // check for out HTTP answer
46 integer AvatarPresent;// true is someone is here
47
48 // first of stride is the response from the bot, the second is the built-in animation
49 list gAnimations = ["normal", "hello",
50 "happy","express_smile",
51 "angry","express_anger",
52 "averse","express_embarrased",
53 "sad","express_sad",
54 "evil","express_repulsed",
55 "fuming","express_worry",
56 "hurt","express_cry",
57 "surprised","express_surprise",
58 "insulted","express_afraid",
59 "confused","express_shrug",
60 "amused","express_laugh",
61 "asking","express_shrug"];
62
63
64 list lAvatars; // a list of visitors
65
66 DEBUG(string msg)
67 {
68 if(debug) llSay(0,msg);
69 }
70
71 string strReplace(string str, string search, string replace) {
72 return llDumpList2String(llParseStringKeepNulls(str, [search], []), replace);
73 }
74
75 string KeyValueGet(string var) {
76 list dVars = llParseString2List(llGetObjectDesc(), ["&"], []);
77 do {
78 list data = llParseString2List(llList2String(dVars, 0), ["="], []);
79 string k = llList2String(data, 0);
80 if(k != var) jump continue;
81 //DEBUG("got " + var + " = " + llList2String(data, 1));
82 return llList2String(data, 1);
83 @continue;
84 dVars = llDeleteSubList(dVars, 0, 0);
85 } while(llGetListLength(dVars));
86 return "";
87 }
88
89
90 default
91 {
92 on_rez(integer param)
93 {
95 }
96
98 {
99 llOwnerSay("Running");
100 npcKey = KeyValueGet("key");
101 npcName = llGetObjectName();
102 DEBUG("npc is named " + npcName);
103
104 llListen(0,"","","");
105 cps = wpm * 5 / 60; // change from words per minute to cps.
106 DEBUG("CPS = " + (string) cps);
107
108 }
109
110 link_message (integer sender, integer num, string str, key id) {
111 if(num == 2) {
112 npcKey = id;
113 DEBUG("Chatbot npcKey = " + (string) npcKey);
114 if(greeting)
115 llSensorRepeat("","",AGENT,range,PI,10);
116 }
117 }
118
119 sensor(integer N) {
120 integer i;
121 for (i = 0 ; i < N; i ++) {
122 key avatarName = llDetectedName(i);
123
124 if(llListFindList(lAvatars,[avatarName]) == -1) {
125 osNpcSay(npcKey,"Hi there, " + llDetectedName(i)+ ", the Rapunzel sim is new. Try it" );
126
127 lAvatars += avatarName;
128
129 if(llGetListLength(lAvatars) > 20) {
130 lAvatars = llDeleteSubList(lAvatars, 0,0);
131 }
132 }
133 }
134 }
135
136 no_sensor()
137 {
138 AvatarPresent = FALSE; }
139
140 listen(integer channel, string name, key id, string message)
141 {
142 DEBUG("I Heard from :" + name);
143 // DEBUG("ID: " + (string) id);
144 // DEBUG("npcKey: " + (string) npcKey);
145
146 if( id == npcKey) {
147 // DEBUG("ignoring");
148 return;
149 }
150
151 // DEBUG("Continuing");
152 // if the speaker is a prim, it will have a creator. Avatars do not have a creator
154 key spkrKey = llList2Key(what,0);
155
156 if(spkrKey != NULL_KEY && !debug)
157 {
158 if(! debug)
159 DEBUG("ignoring object ");
160 return; // we do not want to listen to objects
161 }
162
163 list names = llParseString2List(name,[" "],[]);
164 string firstname = llList2String(names,0);
165 string lastname = llList2String(names,1);
166
167 requestid = llHTTPRequest("http://www.personalityforge.com/api/chat"
168 + "?apiKey=" + llEscapeURL(apiKey)
169 + "&message=" + llEscapeURL(message)
170 + "&chatBotID=" + llEscapeURL(chatBotID)
171 + "&externalID=" + llEscapeURL(firstname)
172 + "&firstName=" + llEscapeURL(firstname)
173 + "&lastName=" + llEscapeURL(lastname)
174 ,[HTTP_METHOD,"GET"],"");
175
176 llSleep(llFrand(3)+ 1); // think for two to five seconds before we type - for realism
177
178 osNpcPlayAnimation(npcKey,"avatar_type");
179 starttime = llGetUnixTime();
180 if(lookat) {
181
182 vector vspeaker = llList2Vector(what,1);
183 rotation rdelta = llRotBetween( llGetPos(), vspeaker );
184
185 rotation newRot = rdelta * llGetRot();
186 //-- rotate the offset to be relative to npc rotation - vector now points to speaker
187
188 osNpcSetRot(npcKey,newRot); // * = add for quats
189 }
190
191 llSetTimerEvent(20); // for safety in case web site is down.
192 }
193
194 timer()
195 {
196 osNpcStopAnimation(npcKey,"avatar_type");
198 }
199
200 http_response(key request_id, integer status, list metadata, string body)
201 {
202 DEBUG(body);
203 // typical body:
204 // Checking origin: '71.252.253.290' (regex: '71\.252\.253\.290')<br>Matched!<br>{"success":1,"errorMessage":"","message":{"chatBotName":"Liddora","chatBotID":"754","message":"Look up. It's Liddora. So how have you been lately, hello?","emotion":"asking"}}
205
206 if(request_id == requestid)
207 {
208 llSetTimerEvent(0); // shut off the error handler
209
210 // get the name of the bot from the reponse
211 integer botname = llSubStringIndex(body,"chatBotName");
212 string namestr = llGetSubString(body,botname,-1);
213 integer botnameend = llSubStringIndex(namestr,"\",\"");
214
215 string botName = llGetSubString(namestr,14, botnameend-1);
216 DEBUG("Bot Name:" + (string) botName);
217
218 integer begin = llSubStringIndex(body, "message\":\"");
219 string msg = llGetSubString(body, begin +10, -1);
220 integer msgend = llSubStringIndex(msg, "emotion");
221 string reply = llGetSubString(msg, 0, msgend-4);
222 DEBUG("reponse:" + reply);
223
224 // change the name in the reply to the bots real name.
225 DEBUG("reply:" + reply);
226 DEBUG("botName:" + botName);
227 DEBUG("npcName:" + npcName);
228
229 reply = strReplace(reply,"<br>"," ");
230 reply = strReplace(reply,"<BR>"," ");
231 reply = strReplace(reply,"Laurel",npcName);
232 reply = strReplace(reply,"Problem",npcName);
233
234 DEBUG("after nameswap:" + reply);
235 DEBUG("Len:" + llStringLength(reply));
236 // calculate how long it would take for a person to type the answer in cps or wpm
237 float delay = (float) llStringLength(reply) / cps;
238
239 DEBUG("delay = " + (string) delay);
240
241 integer t = llGetUnixTime() - starttime ; // subtract how long it has taken to look up the bots answer since we started typing.
242
243 DEBUG("t = " + (string) t);
244 delay -= t;
245
246 DEBUG("delay = " + (string) delay);
247 if(delay > 0) {
248 DEBUG("delay:" + (string) delay);
249 llSleep(delay) ; // fake out the delay that happens when an avatar is typing an answer
250 }
251
252
253 // Emotion Logic - speak on a chat channel what emotional stste the bot is in, for other scripts to use.
254 string emotion = llGetSubString(msg, msgend,-1);
255 DEBUG((string) emotion);
256
257 msgend = llSubStringIndex(emotion, "\"}");
258 emotion = llGetSubString(emotion, 10,msgend-1);
259 DEBUG("Emotion:" + (string) emotion);
260
261 // sends a link animate to prim animator, attempts to play an animation
262 // normal, happy, angry, averse, sad, evil, fuming,
263 // hurt, surprised, insulted, confused, amused, asking.
264
265 llMessageLinked(LINK_SET,1,emotion,"");
266
267 // and also chats it on channel "emotionchannel" for external gadgetry to respond with.
268 llSay(emotionchannel,emotion); // for controlling external gadgets based on emotes
269
270 osNpcStopAnimation(npcKey,"avatar_type");
271
272 // emotional state output
273
274 // you can override the built-in emotion by adding an animation
275 // with any of the following names to the inventory
276 // normal, happy, angry, averse, sad, evil, fuming,
277 // hurt, surprised, insulted, confused, amused, asking.
278
280 DEBUG("Playing animation from inventory named " + emotion);
281 osNpcPlayAnimation(npcKey,emotion);
282 } else {
283 integer index = llListFindList(gAnimations,[emotion]);
284 if(index != -1) {
285 string toPlay = llList2String(gAnimations,index + 1);
286 DEBUG("Playing built-in animation named " + toPlay);
287 osNpcPlayAnimation(npcKey,toPlay);
288 }
289 }
290 osNpcSay(npcKey,reply); // now speak it.
291
292 }
293 }
294 }

NPC Chatbot for Opensim

Modified All in one NPC recorder player for NPC use
Click Prim to use.

Category: ChatBot
By : Ferd Frederix
Created: 2016-07-27 Edited: 2016-07-27
Worlds: Second Life


This script by Ferd Frederix may be used in any manner, modified, and republished.  Unless specified otherwise, my scripts are always free and open source.  Objects made with these scripts may be sold with no restrictions.  All I ask is that you point others to this location should they ask you about it and to not sell this script, unless it is for $0 L. Please help improve my work by reporting bugs and improvements.

1
2 //*******************************************************************//
3 // Instructions on how to use this is at http://www.free-lsl-scripts.com/opensim/posts/NPC/
4 // This is an OpenSim-only script.
5 // Author: Ferd Frederix
6
7
8 ////////////////////////////////////////////////////////////////////////////////////////////
9 // Original code was Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 //
10 ///////////////////////////////////////////////////////////////////////////////////////////
11 // Please see: http://www.gnu.org/licenses/gpl.html for legal details, //
12 // rights of fair usage, the disclaimer and warranty conditions. //
13 ///////////////////////////////////////////////////////////////////////////////////////////
14 // The original NPC controller was from http://was.fm/opensim:npc
15 // Extensive additions and bug fixes by Fred Beckhusem, aka Ferd Frederix, fred@mitsi.com
16 // llSensor had two params swapped
17 // @Wander would wander where it had rezzed, not where it was.
18 // There was no 'no_sensor' event in sit, so if a @sit failed, the NPC got stuck
19 // The animation and walks always stopped old, then started new. It should be start new, then stop old so the default stand would be suppressed.
20 // New code:
21 // Merged with new Route recorder and notecard writer
22 // If the NPC failed to reach a destination it never moved on. Added WAIT global to tune this
23 // Exposed many tunable variables and ported the code to LSLEditor.
24 // Added floating point to times in notecard.
25
26 // Added @sound, @randsound, @whisper, @shout, and @cmd controls.
27 //
28 // notecards integers are not floats for better control
29 //
30 // Link Messages may be used to perform external control by injecting @commands into the stream of actions
31 // Example:
32 // To chat something, such as with a chat robot
33 // llMessageLinked(LINK_SET,0,"@npc_say=Hello","");
34
35 // This script assumes that NPCs and OSSl scripting is enabled in the OpenSim configuration.
36 // In order to enable them, the following changes must be made in the OpenSim.ini configuration file:
37 //
38 // ; Turn on OSSL
39 // AllowOSFunctions = true
40 // OSFunctionThreatLevel = Severe
41
42 //[NPC]
43 // ;# {Enabled} {} {Enable Non Player Character (NPC) facilities} {true false}
44 // Enabled = true
45 //
46 // and then the server has to be restarted.
47
48
49 // Commands: All commands begin with an @ sign. All other lines are ignored
50 // @commands may have optional parameters. The syntax is always:
51 // @cmd=parm1|parm2
52 // NaN in the table below meand Not a Number. This means there is no parameter
53
54 //Command First Parameter Second Parameter Description
55 //@spawn name location (vector) Rezzes an NPC with name at a location.
56 //@walk destination (vector) NaN Makes the NPC walk to destination.
57 //@fly destination (vector) NaN Makes the NPC fly to destination.
58 //@land destination (vector) NaN Makes the NPC land at destination.
59 //@say string NaN Makes the NPC speak a phrase.
60 //@whisper string NaN Makes the NPC whisper a phrase.
61 //@shout string NaN Makes the NPC shout a phrase.
62 //@pause seconds (float) NaN Makes the NPC wait for a multiple of seconds.
63 //@wander radius (float) cycles (integer) Makes the NPC wander in radius, for cycles seconds.
64 //@delete NaN NaN Removes the NPC. Rerquires a link message of @npc_start to continue
65 //@npc_start NaN NaN Starts the NPC at the beginning
66 //@animate animation (string) time (float) Makes the NPC trigger the animation animation for time seconds.
67 //@goto label (string) NaN Jump to the label label in the script.
68 //@rotate degrees (float) NaN Rotate the NPC degrees around the Z axis.
69 //@sit primitive name NaN Sit on a primitive with a given name.
70 //@stand NaN NaN If sitting on a primitive, stand up.
71 //@sound sound_name NaN plays a sound from inventory
72 //@randsound NaN NaN Plays a random sound from inventory
73 //@cmd channel (integer) string Says string on channel, for controlling external gadgets
74
75 //@stop NaN NaN Halts the NPC script indefinitely. Can be started with a link message
76 //@go NaN NaN Continues on next notecard line, for use in link messages
77
78 //////////////////////////////////////////////////////////
79 // DEBUG //
80 //////////////////////////////////////////////////////////
81 integer debug = FALSE; // set to TRUE or FALSE for debug chat on various actions
82 integer Editor = FALSE; // set to to TRUE to working in LSLEditor, FALSE for in-world.
83 // you must also include the NPC commands found in the other script since LSLEditor does not support OpenSim
84 integer iTitleText = FALSE; // set to TRUE to see debug info in text above the controller
85
86 //////////////////////////////////////////////////////////
87 // TUNABLE CONFIGURATION //
88 //////////////////////////////////////////////////////////
89 integer allowUsers = TRUE; // If true, any user can get a Start NPC and Stop NPC menu. Only groups and owners canb get all commands if TRUE, or FALSE
90 float MAXDIST = 2.0; // how close a NPC has to get to a dest pos to continue to next state. Do not lower this too much, as it may miss the target
91 float TIMER = 0.5; // how often the system checks the distance traveled. Fastest you can go is 0.5 seconds
92 integer WANDERRAND = TRUE; // set to TRUE and they will pause during wanders a random number of seconds
93 float WANDERTIME = 10.0; // how long they stand after each @wander,if WANDERRAND is FALSE. If WANDERRAND is TRUE, this is the max time
94 integer WAIT = 10; // wait for this number of seconds for the NPC to reach a destination (for safety). If it fails to reach a target, it will move on after this time.
95 float RANGE = 35.0; // 1 to 96.0 meters - anyone this close to the controller will start NPCS if Sensor button is clicked
96 float REZTIME = 10.0; // wait this lng for NPC to rez in, then start the process
97 string STAND = "Stand"; // the name of the default Stand animation
98 string WALK = "Walk"; // the name of the default Walk animation
99 string FLY = "Fly"; // the name of the default Fly animation
100 string RUN = "Run"; // the name of the default Run animation
101 string LAND = "Land"; // the name of the default land animation ( for birds only)
102 float OffsetZ = 0.5; // appear 0.5 meter above ground, this is added to all destinations to keep them from sinking in. For fun, make this large and watch them fall out of the sky
103
104
105 // DESCRIPTIONS FIELDS HAVE TO SURVIVE A RESET
106 // These vars are stored by saving them with KeyValueSet
107 // "pr" is a 0 if it is set for Owner Only, 1 for Group control
108 // "se" is "on" if sensors are enabled
109 // "co" = "R" or "A" for relative or absolute addressing mode
110 // "key" = NPC key
111
112 // These Globals used to be stored in description. Moved to RAM in V1.6
113 float RAMPause; // @pause param
114 float RAMwd ; // @wander distance
115 integer RAMwc; // @wander count
116 float RAMrot; // @rotate
117 string RAMsit; // @sit primname
118 string RAManimationName; // @animate animation (string) time (float)
119 float RAManimationTime;
120
121 // other globals section
122 integer iChannel; // a listen channel, randomly assigned
123 integer iHandle; // the handle to it
124
125
126
127 // NPC controls
128 vector newDest ; // tmp storage for the walks
129 integer iWaitCounter ; // wait for this number of seconds for the NPC to reach a desrtination
130 string sNPCName; // the name of the NPC that may be in world. So we can remove it.
131 integer bNPC_STOP = FALSE; // boolean to reuse a listener
132 integer bForget = FALSE; // set to TRUE by link messages so we do not remember them
133 float fTimerVal ; // how long we wait when wandering (calculated)
134
135 // OS_NPC_CREATOR_OWNED will create an 'owned' NPC that will only respond to osNpc* commands issued from scripts that have the same owner as the one that created the NPC.
136 // OS_NPC_NOT_OWNED will create an 'unowned' NPC that will respond to any script that has OSSL permissions to call osNpc* commands.
137 integer NPCOptions = OS_NPC_CREATOR_OWNED; // only yhe owner of this box can control this NPC.
138
139 integer walkstate = 0; // helps us reshare the walk state for run, fly and land - a bit of a hack, but it saves RAM. Has to be done this way because some bits of NPCWalkOption are asserted as 0
140
141 integer NPCWalkOption; // Some notes for what happens to NPCWalkOption:
142 // OS_NPC_FLY - Fly the avatar to the given position. The avatar will not land unless the OS_NPC_LAND_AT_TARGET option is also given.
143 // OS_NPC_NO_FLY - Do not fly to the target. The NPC will attempt to walk to the location. If it's up in the air then the avatar will keep bouncing hopeless until another move target is given or the move is stopped
144 //OS_NPC_LAND_AT_TARGET - If given and the avatar is flying, then it will land when it reaches the target. If OS_NPC_NO_FLY is given then this option has no effect.
145 // OS_NPC_RUNNING - if given, NPC avatar moves at running/fast flying speed, otherwise moves at walking/slow flying speed.
146
147 // menus
148 integer showMenu = FALSE; // when we switch states, we need to bring up a menu
149 list lAtButtons = ["Menu","-", "More", "@run","@walk","@fly", "@land","@wander","@sit", "@stand","@animate","@rotate"];
150 list lMenu2 = ["<<", "@comment", "@stop", "@say","@whisper","@shout", "@sound","@randsound", "-", "@cmd", "@pause", "@delete" ];
151 string sCommand; // place to store a command for two-prompted ones
152 string sParam2; // place to store a prompt for two-prompted ones
153 string priPub = "Owner Only"; // Private or Group
154 key kUserKey; // the person who is controlling the avatar, not the Owner
155
156 // the command lists
157 list lCommands; // commands are stored here
158 list lNPCScript; // Storage for the NPC script.
159 string npcAction; // Storage for the next action. @cmd=0|hello, this becomes @cmd
160 string npcParams; // Storage for the param, @cmd=0|hello, this becomes 0|hello
161
162 // misc vars
163 string sNotecard; // commands are stored here temporarily for dumping
164 vector vWanderPos; // a place to wander to
165 string lastANIM ; // last animation run
166 // Sensor
167 integer avatarPresent = TRUE; // Sensor sets this flag when people are within Range.
168 integer Sensor; // set to true if we are running a Sensor for avatars
169
170 // Coordinate control
171 vector vInitialPos ; // Vector that will be filled by the script with the initial starting position in region coordinates.
172 vector vDestPos = ZERO_VECTOR; // Storage for destination position.
173 string relAbs = "Absolute"; // absolute vs relative positioning
174
175 ///////////////////////////////////////////////////////////////////////////
176 // FUNCTIONS //
177 ///////////////////////////////////////////////////////////////////////////
178
179
180 // DEBUG(string) will chat a string or display it as hovertext if debug == TRUE
181 DEBUG(string str)
182 {
183 if(debug)
184 llOwnerSay( str); // Send the owner debug info so you can chase NPCS
185 if(iTitleText)
186 {
187 llSleep(0.1);
188 llSetText(str,<1.0,1.0,1.0>,1.0); // show hovertext
189 }
190 }
191
192
193 // common subroutines
194
195
196
197 // return TRUE if the avatar is owner when private is set, or TRUE if the avatar is in th same group and GROUP is set.
198 integer checkPerms() {
199
200 integer group = (integer) KeyValueGet("pr");
201 if(! group)
202 priPub = "Owner Only";
203 else
204 priPub = "Group";
205
206
208 kUserKey = llDetectedKey(0);
209 return TRUE;
210 }
211
212 if( group && llDetectedGroup(0)) {
213 kUserKey = llDetectedKey(0);
214 return TRUE;
215 }
216 kUserKey = llDetectedKey(0);
217 return FALSE;
218 }
219
220
221
222 NPCStart(string anim)
223 {
224 DEBUG(" Start Anim: " + anim);
226
227 if(lastANIM != anim) {
228 osNpcPlayAnimation(NPCKey(), anim);
229
230 if(llStringLength(lastANIM) && llGetInventoryType(lastANIM) == INVENTORY_ANIMATION) {
231 osNpcStopAnimation(NPCKey(), lastANIM) ;
232 }
233
234 lastANIM = anim;
235 }
236 } else {
237 llSay(DEBUG_CHANNEL, "No animation named " + anim);
238 }
239 }
240
241
242 TimerEvent(float timesent)
243 {
244 //DEBUG("Setting timer: " + (string) timesent);
245 llSetTimerEvent(timesent);
246 }
247
248
249
250 ProcessLink(string str)
251 {
252 // DEBUG("Processing exern cmd : " + str);
253 bForget = TRUE; // tell the NPCProcess state to forget this command after processing it.
254 lNPCScript = llListInsertList(lNPCScript,[str],0); // add this command to the beginning of the list of commands
255 //NPCStart(STAND);
256 }
257
258 // Kill a NPC by Name
259 Kill(string someNPCName)
260 {
261 list avatars = osGetAvatarList(); // Returns a strided list of the UUID, position, and name of each avatar in the region except the owner.
262
263
264 //DEBUG(llDumpList2String(avatars,","));
265 integer i;
266 integer count;
267 integer j = llGetListLength(avatars);
268 for (; i < j; i++){
269 if(llList2String(avatars,i) == someNPCName){
270 vector v = llList2Vector(avatars,i-1);
271 key target = llList2Key(avatars,i-2); // get the UUID of the avatar
272 osNpcRemove(target);
273 llOwnerSay("Removed " + someNPCName + " at location " + (string) v);
274 count++;
275 }
276 }
277 if(count)
278 llOwnerSay("Removed " + (string) count + " NPC's");
279 else
280 llOwnerSay("Could not locate " + someNPCName);
281 }
282
283
284 // return a String for the position we are at. Strings used as the caller wants strings
285 string Pos()
286 {
287 vector where = llGetPos(); // find the box position
288
289 where.z += OffsetZ; // use the ground position + an offset
290
291 if(Editor)
292 where = <128,128,31 + llFrand(1)>; // center of sim for editing
293
294 // if attached the height will be too high by 1/2 the agent size
295 if(llGetAttached()) {
297 float Z = size.z;
298 where.z -= Z/2;
299 }
300
301 // DEBUG("Pos= " + (string) where);
302 return (string) where;
303 }
304
305 Expire()
306 {
307 llOwnerSay("Menu expired");
308 iHandle = 0;
309 TimerEvent(0.0);
310 }
311
312 // setup a menu with a timer for timeouts, called by all make*()
313 menu()
314 {
315 llListenRemove(iHandle);
316 iChannel = llCeil(llFrand(100000) + 20000);
317 iHandle = llListen(iChannel,"","","");
318 TimerEvent(120.0);
319 }
320
321
322 // make a text box
323 makeText(string Param)
324 {
325 menu();
326 llTextBox(kUserKey, Param, iChannel);
327 }
328
329 // top level menu
330 makeMainMenu()
331 {
332 menu();
333 list buttons = ["Appearance","Recording","Save","Help","-","Erase RAM", priPub,relAbs,"-","Stop NPC","Sensor","Start NPC"];
334 llDialog(kUserKey,(string) llGetListLength(lCommands) + "Recordings",buttons,iChannel);
335 }
336
337
338 // Rev 1.4
339 // top level menu for non group/ non owners
340 makeUserMenu()
341 {
342 if(!allowUsers)
343 return;
344 menu();
345 list buttons = ["Start NPC","Stop NPC"];
346 llDialog(kUserKey,"Choose",buttons,iChannel);
347 }
348
349
350
351 // programmable menu for @commands
352 makeMenu(list buttons)
353 {
354 menu();
355 llDialog(kUserKey,(string) llGetListLength(lCommands) + "Recordings",buttons,iChannel);
356 }
357
358
359
360
361 // make one or two text boxes with prompts
362 Text(string cmd, string p1, string p2)
363 {
364 sCommand = cmd;
365 sParam2 = "";
367 sParam2 = p2;
368
369 makeText(p1);
370 }
371
372 ProcessSensor(integer n)
373 {
374 DEBUG("Sensor:" + (string) n);
375 if(Sensor && n)
376 avatarPresent = TRUE; // someone is here and we need to tell the system to run
377 else if(Sensor && !n)
378 avatarPresent = FALSE; // someone is not here and we need to tell the system to stop
379 else
380 avatarPresent = TRUE; // someone is effectivly always here
381 }
382
383 vector CirclePoint(float radius) {
384 float x = llFrand(radius *2) - radius; // +/- radius, randomized
385 float y = llFrand(radius *2) - radius; // +/- radius, randomized
386 return <x, y, 0>; // so this should always happen
387 }
388
389 string KeyValueGet(string var) {
390 list dVars = llParseString2List(llGetObjectDesc(), ["&"], []);
391 do {
392 list data = llParseString2List(llList2String(dVars, 0), ["="], []);
393 string k = llList2String(data, 0);
394 if(k != var) jump continue;
395 //DEBUG("got " + var + " = " + llList2String(data, 1));
396 return llList2String(data, 1);
397 @continue;
398 dVars = llDeleteSubList(dVars, 0, 0);
399 } while(llGetListLength(dVars));
400 return "";
401 }
402
403 KeyValueSet(string var, string val) {
404
405 //DEBUG("set " + var + " = " + val);
406 list dVars = llParseString2List(llGetObjectDesc(), ["&"], []);
407 if(llGetListLength(dVars) == 0)
408 {
409 llSetObjectDesc(var + "=" + val);
410 return;
411 }
412 list result = [];
413 do {
414 list data = llParseString2List(llList2String(dVars, 0), ["="], []);
415 string k = llList2String(data, 0);
416 if(k == "") jump continue;
417 if(k == var && val == "") jump continue;
418 if(k == var) {
419 result += k + "=" + val;
420 val = "";
421 jump continue;
422 }
423 string v = llList2String(data, 1);
424 if(v == "") jump continue;
425 result += k + "=" + v;
426 @continue;
427 dVars = llDeleteSubList(dVars, 0, 0);
428 } while(llGetListLength(dVars));
429 if(val != "") result += var + "=" + val;
431 }
432
433
434 // clear RAM
435 Clr() {
436
437 lCommands = [];
438 llOwnerSay("RAM Memory cleared. Notecards, if any, are not modified.");
439 makeMainMenu();
440 }
441
442 integer checkNoteCards()
443 {
444 // Check that they have saved an Appeaance and Path notecard
445 integer num = llGetInventoryNumber(INVENTORY_NOTECARD); // how many notecards overall
446
447 integer i;
448 integer count;
449 for (; i < num; i++){
451 count++;
452 if(llGetInventoryName(INVENTORY_NOTECARD,i) == "Appearance")
453 count++;
454 }
455 // if we have both, run the NPC
456 return count;
457 }
458 // saves a few bytes per call
459 key NPCKey()
460 {
461 return KeyValueGet("key");
462 }
463
464 // Notes:
465 // No llResetScript() used so we can retain memory between rezzes and sim restarts. NPC info and stateful info is held in the Description
466 //
467
468
469 // This state is the first main menu
470 default
471 {
473 {
474 llSetText("",<1,1,1>,1.0); // clr all hovertext- we may not be using it.
475
476 string rA = KeyValueGet("co"); // Get the remembered menu setting for Abs Vs Relative
477 if(rA == "A")
478 relAbs = "Absolute";
479 else if(rA == "R")
480 relAbs = "Relative";
481 else
482 relAbs = "Absolute";
483
484
485 if(showMenu) {
486 makeMainMenu();
487 return;
488 }
489
490
491 // reenable NPC is sensor is on.
492 if("on" == KeyValueGet("se"))
493 {
494 ProcessSensor(1); // fake 1 avatar to get it rezzed
495 Sensor = TRUE; // we need to scan for avatars
496 state NPCGo;
497 }
498 }
499
500
501 touch_start(integer n) { // if touched, make a menu
502 if(checkPerms())
503 makeMainMenu();
504 else
505 makeUserMenu();
506 }
507
508 // no changed event needed
509
510 // menu listener
511 listen(integer iChannel, string name, key id, string message) {
512 TimerEvent(0.0); /// kill the menu expiration timer
513
514 if(message == "Stop NPC")
515 {
516 if(llStringLength(sNPCName)){
517 Kill(sNPCName);
518 sNPCName = "";
519 } else {
520 bNPC_STOP = TRUE;
521 makeText("Enter name of an NPC to stop");
522 }
523 }
524 else if(message == "Erase RAM"){
525 Clr();
526 }
527 else if(message == "Relative"){
528 relAbs = "Absolute";
529 KeyValueSet("co","A"); // remember coordinates = A
530 Clr();
531 }
532 else if(message == "Absolute"){
533 relAbs = "Relative";
534 KeyValueSet("co","R"); // remember coordinates = R
535 Clr();
536 }
537 else if(message == "Recording"){
538 state Commands; // show them the recording menu
539 }
540 else if(message == "Owner Only") {
541 priPub = "Group";
542 KeyValueSet("pr","1");
543
544 llOwnerSay("Group members have control");
545 makeMainMenu();
546 }
547 else if(message == "Group") {
548 priPub = "Owner Only";
549 KeyValueSet("pr","0");
550 llOwnerSay("Only you have control");
551 makeMainMenu();
552 }
553 else if(message == "Sensor") {
554 integer count = checkNoteCards();
555
556 if(count >= 2) {
557 KeyValueSet("se", "on");
558 ProcessSensor(1); // fake 1 avatar to get it rezzed
559 Sensor = TRUE; // we need to scan for avatars
560 state NPCGo;
561 }
562
563 // lslEditor does not handle the above, so I hack it in
564 if(Editor) {
565 Sensor = TRUE; // we need to scan for avatars
566 state NPCGo;
567 }
568
569 llOwnerSay("You have not saved a recording and/or appearance, so you cannot start a NPC");
570 makeMainMenu();
571 }
572 else if(message == "Appearance") {
573 llRemoveInventory("Appearance"); // delete the notecard
574 osAgentSaveAppearance(kUserKey, "Appearance"); // make the ntecard
575 llOwnerSay("Your Appearance has been recorded in notecard 'Appearance'");
576 makeMainMenu();
577 }
578 else if(message == "Save") {
579 if(llGetListLength(lCommands) == 0) {
580 llOwnerSay("Nothing recorded, you need to make some Recodings first");
581 makeMainMenu();
582 return;
583 }
584 state Save;
585 }
586 else if(message == "Help"){
587 llLoadURL(kUserKey,"Click to view help","http://www.outworldz.com/opensim/posts/NPC/");
588 makeMainMenu();
589 }
590 else if(message == "Start NPC") {
591 integer count = checkNoteCards();
592 if(Editor) state NPCGo;
593 if(count == 2)
594 state NPCGo;
595
596 llOwnerSay("You have not saved a either recording or and appearance, so we cannot start a NPC");
597
598 }
599 else if(bNPC_STOP){
600 bNPC_STOP = FALSE;
601 Kill(message);
602 }
603 }
604 timer(){
605 Expire();
606 }
607 }
608
609
610 // This state is used to save a Path notecard
611 state Save
612 {
614 makeText("Stand where you want the NPC to appear, and enter the NPC Name");
615 }
616 listen(integer iChannel, string name, key id, string message) {
617 TimerEvent(0.0); /// kill the menu expiration timer
618
619 sNPCName = message; // in case we need to kill it.
620 vector vDest = (vector) Pos();
621
622 if(relAbs == "Relative")
623 {
624 vDest -= llGetPos(); // just an offset for relative
625 }
626 sNotecard = "@spawn=" + message + "|" + (string) vDest + "\n";
627 integer i;
628 integer j = llGetListLength(lCommands);
629 for (; i < j; i++){
630 // get the command to save to the notecard
631 string line = llList2String(lCommands,i);
632 if(relAbs == "Absolute") {
633 sNotecard += line; // add the un-modified string to the notecard
634 } else {
635 // since we have to record absolute coords since we do not know where the box goes until they press Save,
636 // we process the absolute to relative conversion for walks here
637 list parts = llParseString2List(line,["="],[]); //get the @command
638
639 if(llList2String(parts,0) == "@walk") {
640 vector vec = (vector) llList2String(parts,1) - llGetPos();
641 sNotecard += "@walk=" + (string) vec + "\n";
642 }
643 else if(llList2String(parts,0) == "@fly") {
644 vector vec = (vector) llList2String(parts,1) - llGetPos();
645 sNotecard += "@fly=" + (string) vec + "\n";
646 }
647 else if(llList2String(parts,0) == "@run") {
648 vector vec = (vector) llList2String(parts,1) - llGetPos();
649 sNotecard += "@run=" + (string) vec + "\n";
650 }
651 else if(llList2String(parts,0) == "@land") {
652 vector vec = (vector) llList2String(parts,1) - llGetPos();
653 sNotecard += "@land=" + (string) vec + "\n";
654 }
655 else {
656 sNotecard += line; // add the un-modified string to the notecard
657 }
658 }
659 }
660 llRemoveInventory("Path"); // delete the old notecard
661 osMakeNotecard("Path",sNotecard); // Makes the notecard.
662 llOwnerSay("'Path' notecard has been written");
663 state default;
664 }
665 timer(){
666 Expire();
667 }
668
669 }
670
671 // This state is used to record the path for the NPC
672 // Each command can take 0, 1, or 2 params
673 state Commands
674 {
675 state_entry() {
676 makeMenu(lAtButtons);
677 }
678
679 on_rez(integer p) {
680 showMenu= TRUE;
681 state default;
682 }
684 if(checkPerms())
685 makeMenu(lAtButtons);
686 else
687 makeUserMenu();
688
689 }
690
691 listen(integer iChannel, string name, key id, string message)
692 {
693 TimerEvent(0.0); /// kill the menu expiration timer
694
695 if(message == "Menu"){
696 showMenu= TRUE;
697 state default;
698 }
699 else if(message == "More"){
700 makeMenu(lMenu2);
701 }
702 else if(message == "<<") {
703 makeMenu(lAtButtons);
704 }
705 else if(message == "@comment"){
706 Text("# ","Enter a comment","");
707 }
708 else if(message == "@stop"){
709 lCommands += "@stop"+ "\n";
710 makeMenu(lAtButtons);
711 }
712 else if(message == "@run"){
713 lCommands += "@run=" + Pos() + "\n";
714 llOwnerSay("Recorded position: " + Pos());
715 makeMenu(lAtButtons);
716 }
717 else if(message == "@fly"){
718 lCommands += "@fly=" + Pos() + "\n";
719 llOwnerSay("Recorded position: " + Pos());
720 makeMenu(lAtButtons);
721 }
722 else if(message == "@land"){
723 lCommands += "@land=" + Pos() + "\n";
724 llOwnerSay("Recorded position: " + Pos());
725 makeMenu(lAtButtons);
726 }
727 else if(message == "@walk") {
728 lCommands += "@walk=" + Pos() + "\n";
729 llOwnerSay("Recorded position: " + Pos());
730 makeMenu(lAtButtons);
731 }
732 else if(message == "@stop"){
733 lCommands += "@stop"+ "\n";
734 makeMenu(lAtButtons);
735 }
736 else if(message == "@sound"){
737 Text("@sound=","Enter a sound name or UUID to trigger","");
738 }
739 else if(message == "@randsound"){
740 lCommands += "@randsound"+ "\n";
741 makeMenu(lAtButtons);
742 }
743 else if(message == "@say") {
744 Text("@say=","Enter what the NPC will say","");
745 }
746 else if(message == "@whisper"){
747 Text("@whisper=","Enter what the NPC will whisper","");
748 }
749 else if(message == "@shout"){
750 Text("@shout=","Enter what the NPC will shout","");
751 }
752 else if(message == "@wander") {
753 Text("@wander=","Enter radius to wander","Enter number of wanders");
754 }
755 else if(message == "@pause") {
756 Text("@pause=","Enter time to pause","");
757 }
758 else if(message == "@rotate") {
759 Text("@rotate=","Enter degrees to rotate","");
760 }
761 else if(message == "@sit"){
762 Text("@sit=","Enter name of object to sit on","");
763 }
764 else if(message == "@cmd"){
765 Text("@cmd=","Enter cjhannel to speak on","Enter text to speak");
766 }
767 else if(message == "@stand"){
768 lCommands += "@stand\n";
769 llOwnerSay("Stand Recorded");
770 makeMenu(lAtButtons);
771 }
772 else if(message == "@animate"){
773 Text("@animate=","Enter animation name to play","Enter time to play the animation");
774 }
775 else if(! llStringLength(sParam2)) {
776 lCommands += sCommand + message + "\n";
777 llOwnerSay("Recorded");
778 makeMenu(lAtButtons);
779 }
780 else if(llStringLength(sParam2)){
781 sCommand = sCommand + message + "|";
782 llOwnerSay("Recorded");
783 makeText(sParam2);
784 sParam2 = "";
785 }
786 }
787 timer() {
788 Expire();
789 }
790
791
792
793 }
794
795
796 // This state will create an NPC in world
797 state NPCGo {
798 state_entry() {
799 // DEBUG("NPCGo");
800 ProcessSensor(1); // assert that someone is home so we can get rezzed.
801 osNpcRemove(NPCKey());
802 TimerEvent(5);
803 }
804 timer() {
805 lNPCScript = llParseString2List(osGetNotecard("Path"), ["\n"], []);
806 if(llGetListLength(lNPCScript) == 0) {
807 llSay(DEBUG_CHANNEL, "No Path notecard found.");
808 TimerEvent(0.0);
809 return;
810 }
811 state ProcessNPCLine;
812 }
813 changed(integer change) {
814 if(change & CHANGED_REGION_RESTART)
815 state ProcessNPCLine;
816 }
817 on_rez(integer num) {
819 }
820 state_exit() {
821 TimerEvent(0.0);
822 }
823 }
824
825
826 // This state loops over the notecard, processing each command
827 state ProcessNPCLine
828 {
830 {
831 // DEBUG("ProcessNPCLine");
832 @ignore;
833
834 string next = llList2String(lNPCScript, 0); // get the next command
835 // DEBUG("Cmd:" + next);
836 lNPCScript = llDeleteSubList(lNPCScript, 0, 0); // delete it
837 if(! bForget) {
838 lNPCScript += next; // put it on the end unless we are told to forget it from a Link Message
839 bForget = FALSE;
840 }
841 if(llGetSubString(next, 0, 0) != "@") jump ignore; // ignore non-@ commands
842 list data = llParseString2List(next, ["="], []);
843 npcAction = llToLower(llStringTrim(llList2String(data, 0), STRING_TRIM));
844 npcParams = llStringTrim(llList2String(data, 1), STRING_TRIM);
845
846 @commands;
847
848 if(! avatarPresent){
849 state nobodyHome;
850 }
851 if(npcAction == "@spawn") {
852 // DEBUG("Spawning");
853 integer lastIdx = llGetListLength(lNPCScript)-1;
854 lNPCScript = llDeleteSubList(lNPCScript, lastIdx, lastIdx); // remove spawn commands, we do them only once
855 list spawnData = llParseString2List(npcParams, ["|"], []);
856 sNPCName =llList2String(spawnData, 0); // V 1.6 name in RAM
857
858 list spawnDest = llParseString2List(llList2String(spawnData, 1), ["<", ",", ">"], []);
859 vInitialPos.x = llList2Float(spawnDest, 0);
860 vInitialPos.y = llList2Float(spawnDest, 1);
861 vInitialPos.z = llList2Float(spawnDest, 2);
862 state spawn;
863 }
864 if(npcAction == "@stop") {
865 state stop;
866 }
867 if(npcAction == "@goto") {
868 // DEBUG("goto");
869 integer lastIdx = llGetListLength(lNPCScript)-1;
870 lNPCScript = llDeleteSubList(lNPCScript, lastIdx, lastIdx);
871 // Wind commands till goto label.
872 @wind;
873 string next1 = llList2String(lNPCScript, 0);
874 lNPCScript = llDeleteSubList(lNPCScript, 0, 0);
875 lNPCScript += next1;
876 if(next1 != npcParams) jump wind;
877 // Wind the label too.
878 next1 = llList2String(lNPCScript, 0);
879 lNPCScript = llDeleteSubList(lNPCScript, 0, 0);
880 lNPCScript += next1;
881 // Get next command.
882 list data1 = llParseString2List(next1, ["="], []);
883 npcAction = llToLower(llStringTrim(llList2String(data1, 0), STRING_TRIM));
884 npcParams = llStringTrim(llList2String(data1, 1), STRING_TRIM);
885 // Reschedule.
886 jump commands;
887 }
888
889 if(npcAction == "@sound") {
890 // DEBUG("sound");
891 llTriggerSound(npcParams,1.0);
892 jump ignore; // process next line
893 }
894 if(npcAction == "@randsound") {
895 // DEBUG("random sound");
897 integer rand = llCeil(llFrand(N)) -1; // pick a random sound
899 llTriggerSound(toPlay,1.0);
900 jump ignore; // process next line
901 }
902
903 if(npcAction == "@walk") {
904 list dest = llParseString2List(npcParams, ["<", ",", ">"], []);
905 vDestPos.x = llList2Float(dest, 0);
906 vDestPos.y = llList2Float(dest, 1);
907 vDestPos.z = llList2Float(dest, 2);
908
909 if(vDestPos == ZERO_VECTOR) {
910 llSay(DEBUG_CHANNEL,"Bad (zeros) position for @walk");
911 state ProcessNPCLine;
912 }
913 walkstate = 1;// walking
914 NPCWalkOption = OS_NPC_NO_FLY ;
915 state walk;
916 }
917
918 if(npcAction == "@fly") {
919 list dest = llParseString2List(npcParams, ["<", ",", ">"], []);
920 vDestPos.x = llList2Float(dest, 0);
921 vDestPos.y = llList2Float(dest, 1);
922 vDestPos.z = llList2Float(dest, 2);
923
924 if(vDestPos == ZERO_VECTOR) {
925 llSay(DEBUG_CHANNEL,"Bad (zeros) position for @fly");
926 state ProcessNPCLine;
927 }
928
929 walkstate = 2;// flying
930 NPCWalkOption = OS_NPC_FLY ;
931 state walk;
932 }
933
934 if(npcAction == "@run") {
935 list dest = llParseString2List(npcParams, ["<", ",", ">"], []);
936 vDestPos.x = llList2Float(dest, 0);
937 vDestPos.y = llList2Float(dest, 1);
938 vDestPos.z = llList2Float(dest, 2);
939
940 if(vDestPos == ZERO_VECTOR) {
941 llSay(DEBUG_CHANNEL,"Bad (zeros) position for @walk");
942 state ProcessNPCLine;
943 }
944 walkstate = 3;// running
945 NPCWalkOption = OS_NPC_NO_FLY | OS_NPC_RUNNING;
946 state walk;
947 }
948
949 if(npcAction == "@land") {
950 list dest = llParseString2List(npcParams, ["<", ",", ">"], []);
951 vDestPos.x = llList2Float(dest, 0);
952 vDestPos.y = llList2Float(dest, 1);
953 vDestPos.z = llList2Float(dest, 2);
954
955 if(vDestPos == ZERO_VECTOR) {
956 llSay(DEBUG_CHANNEL,"Bad (zeros) position for @walk");
957 state ProcessNPCLine;
958 }
959
960 walkstate = 4;// landing
961
962
963 NPCWalkOption= OS_NPC_FLY | OS_NPC_LAND_AT_TARGET ;
964 state walk;
965 }
966
967
968
969 // chat commands
970 // speak in white text
971
972 if(npcAction == "@say") {
973 // DEBUG("say");
974 osNpcSay(NPCKey(),0, npcParams);
975 jump ignore; // process next line
976 }
977 if(npcAction == "@shout") {
978 // DEBUG("shout");
979 osNpcShout(NPCKey(),0, npcParams);
980 jump ignore; // process next line
981 }
982 if(npcAction == "@whisper") {
983 // DEBUG("whisper");
984 osNpcWhisper(NPCKey(),0, npcParams);
985 jump ignore; // process next line
986 }
987 // speak a command on a channel, so you can open doors and control stuff.
988 if(npcAction == "@cmd") {
989 // DEBUG("cmd");
990 list dataToSpeak = llParseString2List(npcParams, ["|"], []);
991 integer iChannel = (integer) llList2String(dataToSpeak,0);
992 string stringToSpeak = llList2String(dataToSpeak,1);
993 llRegionSay(iChannel, stringToSpeak); // V 1.2
994
995 jump ignore; // process next line
996 }
997 // stop everything
998 if(npcAction == "@pause") {
999 // DEBUG("pause");
1000 RAMPause = (float) npcParams;
1001 state pause;
1002 }
1003 if(npcAction == "@wander") {
1004 // DEBUG("wander");
1005 list wanderData = llParseString2List(npcParams, ["|"], []);
1006 RAMwd = (float) llList2String(wanderData, 0);
1007 RAMwc = (integer) llList2String(wanderData, 1);
1008
1009 vDestPos = osNpcGetPos(NPCKey()); // set the wander start
1010 DEBUG("Starting at " + (string) vDestPos);
1011 state wander;
1012 }
1013 if(npcAction == "@rotate") {
1014 // DEBUG("rotate");
1015 RAMrot = (float) npcParams;
1016 state rotate;
1017 }
1018 if(npcAction == "@sit") {
1019 // DEBUG("sit");
1020 RAMsit= npcParams;
1021 state sit;
1022 }
1023 if(npcAction == "@stand") {
1024 // DEBUG("stand");
1025 state stand;
1026 }
1027 if(npcAction == "@delete") {
1028 state delete;
1029 }
1030 if(npcAction == "@animate") {
1031 // DEBUG("animate");
1032 list animateData = llParseString2List(npcParams, ["|"], []);
1033 RAManimationName = llList2String(animateData, 0);
1034 RAManimationTime = (float) llList2String(animateData, 1);
1035 state animate;
1036 }
1037 llSay(DEBUG_CHANNEL, "ERROR: Unrecognized script line: " + npcAction + "=" + npcParams);
1038 jump ignore;
1039
1040 }
1041 changed(integer change) {
1042 if(change & CHANGED_REGION_RESTART)
1043 state NPCGo;
1044 }
1045 on_rez(integer num) {
1047 }
1048 link_message(integer sender, integer num, string str, key id) {
1049 ProcessLink(str);
1050 state ProcessNPCLine;
1051 }
1052 // if touched by owner while running code, make a menu
1054
1055 if(checkPerms()) {
1056 TimerEvent(0); // stop the NPC from ticking
1057 showMenu = TRUE;
1058 state default;
1059 } else {
1060 makeUserMenu();
1061 }
1062
1063 }
1064 }
1065
1066
1067 state nobodyHome
1068 {
1069 state_entry() {
1070 // DEBUG("Removing NPC");
1071 osNpcRemove(NPCKey());
1072 llSensorRepeat("","",AGENT,RANGE,TWO_PI, 5);
1073 }
1074 sensor(integer n) {
1076 state NPCGo;
1077 }
1078 // if touched by owner while running code, make a menu
1080 if(checkPerms()) {
1081 TimerEvent(0); // stop the NPC from ticking
1082 showMenu = TRUE;
1083 state default;
1084 } else {
1085 makeUserMenu();
1086 }
1087
1088 }
1089 }
1090
1091
1092 state spawn
1093 {
1094 state_entry() {
1095 // DEBUG("state spawn");
1096 list name = llParseString2List(sNPCName, [" "], []);
1097 // notecard is stored as offsets from this box with relative addressing. Convert to absolute
1098 if(relAbs == "Relative"){
1099 vInitialPos += llGetPos();
1100 }
1101
1102 // DEBUG("rez:" + (string) vInitialPos);
1103
1104 KeyValueSet("key", osNpcCreate(llList2String(name, 0), llList2String(name, 1), vInitialPos, "Appearance", NPCOptions)); // no OS_NPC_SENSE_AS_AGENT allowed due to llSensor Use
1105
1106 llMessageLinked(LINK_SET,2,"Key",NPCKey());
1107
1108 osNpcLoadAppearance(NPCKey(), "Appearance");
1109 TimerEvent(REZTIME);
1110 NPCStart(STAND);
1111 }
1112 link_message(integer sender, integer num, string str, key id) {
1113 ProcessLink(str);
1114 state ProcessNPCLine;
1115 }
1116 timer() {
1117 state ProcessNPCLine;
1118 }
1119 changed(integer change) {
1120 if(change & CHANGED_REGION_RESTART)
1121 state NPCGo;
1122 }
1123 on_rez(integer num) {
1125 }
1126 state_exit(){
1127 TimerEvent(0.0);
1128 }
1129 // if touched by owner while running code, make a menu
1131 if(checkPerms()) {
1132 TimerEvent(0); // stop the NPC from ticking
1133 showMenu = TRUE;
1134 state default;
1135 }else {
1136 makeUserMenu();
1137 }
1138
1139 }
1140 }
1141
1142
1143 state rotate {
1144 state_entry() {
1145 // DEBUG("state rotate");
1146 osNpcSetRot(NPCKey(), llEuler2Rot(<0,0,RAMrot> * DEG_TO_RAD));
1147 TimerEvent(TIMER);
1148 }
1149 link_message(integer sender, integer num, string str, key id) {
1150 ProcessLink(str);
1151 state ProcessNPCLine;
1152 }
1153 timer() {
1154 state ProcessNPCLine;
1155 }
1156 changed(integer change) {
1157 if(change & CHANGED_REGION_RESTART)
1158 state NPCGo;
1159 }
1160 on_rez(integer num) {
1162 }
1163 state_exit() {
1164 TimerEvent(0.0);
1165 }
1166 // if touched by owner while running code, make a menu
1168 if(checkPerms()) {
1169 TimerEvent(0); // stop the NPC from ticking
1170 showMenu = TRUE;
1171 state default;
1172 }else {
1173 makeUserMenu();
1174 }
1175 }
1176 }
1177
1178 state sit {
1179 state_entry() {
1180 // DEBUG ("state sit");
1181 llSensorRepeat(RAMsit, "", PASSIVE|ACTIVE, 96, TWO_PI, 1);
1182 }
1183 sensor(integer num) {
1185 osNpcSit(NPCKey(), llDetectedKey(0), OS_NPC_SIT_NOW); //V1.2
1186 TimerEvent(TIMER);
1187 }
1188 no_sensor(){
1189 state ProcessNPCLine;
1190
1191 }
1192 timer() {
1193 state ProcessNPCLine;
1194 }
1195 link_message(integer sender, integer num, string str, key id) {
1196 ProcessLink(str);
1197 state ProcessNPCLine;
1198 }
1199 changed(integer change) {
1200 if(change & CHANGED_REGION_RESTART)
1201 state NPCGo;
1202 }
1203 on_rez(integer num) {
1205 }
1206 // if touched by owner while running code, make a menu
1208 if(checkPerms()) {
1209 TimerEvent(0); // stop the NPC from ticking
1210 showMenu = TRUE;
1211 state default;
1212 } else {
1213 makeUserMenu();
1214 }
1215
1216 }
1217
1218
1219 state_exit(){
1220 TimerEvent(0.0);
1221 }
1222
1223
1224 }
1225
1226 state stand {
1227 state_entry() {
1228 // DEBUG("state stand");
1229 osNpcStand(NPCKey());
1230 state ProcessNPCLine;
1231 }
1232 changed(integer change) {
1233 if(change & CHANGED_REGION_RESTART)
1234 state NPCGo;
1235 }
1236 // if touched by owner while running code, make a menu
1238 if(checkPerms()) {
1239 TimerEvent(0); // stop the NPC from ticking
1240 showMenu = TRUE;
1241 state default;
1242 } else {
1243 makeUserMenu();
1244 }
1245
1246 }
1247
1248
1249 on_rez(integer num) {
1251 }
1252
1253 }
1254
1255 state animate {
1256 state_entry() {
1257 // DEBUG("state animate");
1258 NPCStart(RAManimationName);
1259 TimerEvent(RAManimationTime);
1260 }
1261 link_message(integer sender, integer num, string str, key id) {
1262 ProcessLink(str);
1263 state ProcessNPCLine;
1264 }
1265 timer() {
1266 NPCStart(STAND);
1267 state ProcessNPCLine;
1268 }
1269 changed(integer change) {
1270 if(change & CHANGED_REGION_RESTART)
1271 state NPCGo;
1272 }
1273 // if touched by owner while running code, make a menu
1275 if(checkPerms()) {
1276 TimerEvent(0); // stop the NPC from ticking
1277 showMenu = TRUE;
1278 state default;
1279 } else {
1280 makeUserMenu();
1281 }
1282
1283 }
1284
1285
1286 on_rez(integer num) {
1288 }
1289 state_exit() {
1290 TimerEvent(0.0);
1291 }
1292
1293 }
1294 state walk {
1295 state_entry() {
1296
1297 DEBUG("NPCWalkOption = " + (string) NPCWalkOption);
1298
1299 // walk, fly, run, land
1300 if(walkstate == 1) {
1301 NPCStart(WALK);
1302 } else if(walkstate == 2) {
1303 llShout(299,"on");
1304 NPCStart(FLY);
1305 } else if(walkstate == 3) {
1306 NPCStart(RUN);
1307 } else if(walkstate == 4) {
1308 NPCStart(LAND);
1309 } else {
1310 state ProcessNPCLine;
1311 }
1312
1313 if(Sensor) {
1314 // DEBUG("Sensor on");
1315 llSensor("","",AGENT,RANGE,TWO_PI); // sensor survive state switches.
1316 }
1317
1318 newDest = vDestPos ;
1319 // notecard is stored as offsets from this box with relative addressing. Convert to absolute
1320 if(relAbs == "Relative"){
1321 newDest += llGetPos();
1322 }
1323
1324 DEBUG("Moveto:" + (string) newDest);
1325 iWaitCounter = WAIT; // wait 60 seconds to get to a destination.
1326 osNpcMoveToTarget(NPCKey(), newDest, NPCWalkOption);
1327 TimerEvent(TIMER);
1328 }
1329 link_message(integer sender, integer num, string str, key id) {
1330 ProcessLink(str);
1331 state ProcessNPCLine;
1332 }
1333 timer() {
1334 if(--iWaitCounter) {
1335
1336 if(llVecDist(osNpcGetPos(NPCKey()), newDest) > MAXDIST) {
1337 return;
1338 }
1339 }
1340
1341 // walk, fly, run, land
1342 if(walkstate == 1) {
1343 NPCStart(STAND);
1344 } else if(walkstate == 2) {
1345 // nothing
1346 } else if(walkstate == 3) {
1347 NPCStart(STAND);
1348 } else if(walkstate == 4) {
1349 llShout(299,"off");
1350 NPCStart(STAND);
1351 } else {
1352 state ProcessNPCLine;
1353 }
1354
1355 state ProcessNPCLine;
1356 }
1357 sensor(integer n) {
1358 ProcessSensor(n);
1359 }
1360 no_sensor(){
1361 ProcessSensor(0);
1362 }
1363 // if touched by owner while running code, make a menu
1365 if(checkPerms()) {
1366 TimerEvent(0); // stop the NPC from ticking
1367 showMenu = TRUE;
1368 state default;
1369 } else {
1370 makeUserMenu();
1371 }
1372
1373 }
1374
1375
1376 changed(integer change) {
1377 if(change & CHANGED_REGION_RESTART)
1378 state NPCGo;
1379 }
1380 on_rez(integer num) {
1382 }
1383 state_exit() {
1384 TimerEvent(0.0);
1385 }
1386
1387 }
1388
1389 state wander
1390 {
1391 state_entry() {
1392 DEBUG("state wander");
1393 if(Sensor)
1394 {
1395 // DEBUG("Sensor on");
1396 llSensor("","",AGENT,RANGE,TWO_PI); // sensor survive state switches.
1397 }
1398
1399 vector point = CirclePoint(RAMwd);
1400 DEBUG("CirclePoint:" + (string) point);
1401 vWanderPos = vDestPos + point;
1402 DEBUG("vWanderPos:" + (string) vWanderPos);
1403
1404 fTimerVal = WANDERTIME; // default time to pause after each wander
1405 if(WANDERRAND)
1406 fTimerVal = llFrand(WANDERTIME); // override, they want random times
1407
1408 NPCStart(WALK);
1409
1410 DEBUG("Wander to:" + (string) vWanderPos);
1411
1412 osNpcMoveToTarget(NPCKey(), vWanderPos, NPCWalkOption);
1413 iWaitCounter = WAIT; // wait 60 seconds to get to a destination.
1414
1415 TimerEvent(TIMER); // first time we wait for the short timer.
1416 }
1417 link_message(integer sender, integer num, string str, key id) {
1418 ProcessLink(str);
1419 NPCStart(STAND);
1420 state ProcessNPCLine;
1421 }
1422 timer() {
1423
1424 if(--iWaitCounter) // wait 60 seconds to get to a destination.
1425 if(llVecDist(osNpcGetPos(NPCKey()), vWanderPos) > MAXDIST) return;
1426
1427
1428 // see if wander counter == 0, if so, stop walking, go to stand and process next line
1429 if(RAMwc == 0) {
1430 NPCStart(STAND);
1431 state ProcessNPCLine;
1432 }
1433
1434 // one less time to wander around
1435 RAMwc--;
1436
1437 NPCStart(STAND);
1438 state wanderhold;
1439 }
1440 sensor(integer n) {
1441 ProcessSensor(n);
1442 }
1443 no_sensor() {
1444 ProcessSensor(0);
1445 }
1446 // if touched by owner while running code, make a menu
1448 if(checkPerms()) {
1449 TimerEvent(0); // stop the NPC from ticking
1450 showMenu = TRUE;
1451 state default;
1452 } else {
1453 makeUserMenu();
1454 }
1455 }
1456
1457
1458 changed(integer change) {
1459 if(change & CHANGED_REGION_RESTART)
1460 state NPCGo;
1461 }
1462 on_rez(integer num) {
1464 }
1465 state_exit() {
1466 TimerEvent(0.0);
1467 }
1468 }
1469
1470
1471 state wanderhold
1472 {
1473 state_entry(){
1474 // now that we have reached a wander spot, slow the timer down to the desired value
1475 TimerEvent(fTimerVal);
1476 if(Sensor)
1477 {
1478 // DEBUG("Sensor on");
1479 llSensor("","",AGENT,RANGE,TWO_PI); // sensor survive state switches.
1480 }
1481 }
1482 timer() {
1483 state wander;
1484 }
1485 sensor(integer n){
1486 ProcessSensor(n);
1487 }
1488 no_sensor(){
1489 ProcessSensor(0);
1490 }
1491 // if touched by owner while running code, make a menu
1493 if(checkPerms()) {
1494 TimerEvent(0); // stop the NPC from ticking
1495 showMenu = TRUE;
1496 state default;
1497 } else {
1498 makeUserMenu();
1499 }
1500
1501 }
1502
1503
1504 changed(integer change) {
1505 if(change & CHANGED_REGION_RESTART)
1506 state NPCGo;
1507 }
1508 on_rez(integer num) {
1510 }
1511 state_exit() {
1512 TimerEvent(0.0);
1513 }
1514
1515
1516 }
1517
1518
1519
1520 // @pause=10 will stand for 10 seconds
1521 state pause {
1522 state_entry() {
1523 DEBUG("state pause");
1524 NPCStart(STAND);
1525 if(Sensor)
1526 {
1527 // DEBUG("Sensor on");
1528 llSensor("","",AGENT,RANGE,TWO_PI); // sensor survive state switches.
1529 }
1530 llSetTimerEvent(RAMPause);
1531 }
1532 link_message(integer sender, integer num, string str, key id){
1533 ProcessLink(str);
1534 state ProcessNPCLine;
1535 }
1536 timer() {
1537 NPCStart(STAND);
1538 state ProcessNPCLine;
1539 }
1540 sensor(integer n)
1541 {
1542 ProcessSensor(n);
1543 }
1544 no_sensor()
1545 {
1546 ProcessSensor(0);
1547 }
1548 // if touched by owner while running code, make a menu
1550 if(checkPerms()) {
1551 TimerEvent(0); // stop the NPC from ticking
1552 showMenu = TRUE;
1553 state default;
1554 } else
1555 makeUserMenu();
1556
1557 }
1558 changed(integer change) {
1559 if(change & CHANGED_REGION_RESTART)
1560 state NPCGo;
1561 }
1562 on_rez(integer num) {
1564 }
1565 state_exit()
1566 {
1567 TimerEvent(0.0);
1568 }
1569 }
1570
1571 // @stop makes the NPC stand there. You have to linkmessage to get moving again
1572 state stop {
1573 state_entry() {
1574 if(Sensor)
1575 {
1576 // DEBUG("Sensor on");
1577 llSensor("","",AGENT,RANGE,TWO_PI); // sensor survive state switches.
1578 }
1579 }
1580 link_message(integer sender, integer num, string str, key id){
1581 if(str=="@go")
1582 state ProcessNPCLine;
1583 ProcessLink(str);
1584 }
1585 // if touched by owner while running code, make a menu
1587 if(checkPerms()) {
1588 TimerEvent(0); // stop the NPC from ticking
1589 showMenu = TRUE;
1590 state default;
1591 } else
1592 makeUserMenu();
1593
1594 }
1595 sensor(integer n)
1596 {
1597 ProcessSensor(n);
1598 }
1599 no_sensor()
1600 {
1601 ProcessSensor(0);
1602 }
1603 changed(integer change) {
1604 if(change & CHANGED_REGION_RESTART)
1605 state NPCGo;
1606 }
1607 on_rez(integer num) {
1609 }
1610 }
1611
1612
1613
1614 state delete {
1615 state_entry() {
1616 // DEBUG("state delete");
1617 osNpcRemove(NPCKey());
1618 }
1619 link_message(integer sender, integer num, string str, key id) {
1620 if(str == "@npc_start")
1621 {
1622 state NPCGo;
1623 }
1624 }
1625
1626 // No on_rez or changed event needed, the only way out is a link message
1627 }

NPC Chatbot for Opensim

Sample Path notecard

Category: ChatBot
By : Ferd Frederix
Created: 2016-07-27 Edited: 2016-07-27
Worlds: Second Life


This script by Ferd Frederix may be used in any manner, modified, and republished.  Unless specified otherwise, my scripts are always free and open source.  Objects made with these scripts may be sold with no restrictions.  All I ask is that you point others to this location should they ask you about it and to not sell this script, unless it is for $0 L. Please help improve my work by reporting bugs and improvements.

1 @spawn=Tinker@www.outworldz.com|<0.000000,0.000000,0.500000>
2 @walk=<0,-8,0.5>
3 @sit=LayDown
4 @animate=Sit|100000
5 @stop

NPC Chatbot for Opensim

Read me notecard

Category: ChatBot
By : Ferd Frederix
Created: 2016-07-27 Edited: 2016-07-27
Worlds: Second Life


This script by Ferd Frederix may be used in any manner, modified, and republished.  Unless specified otherwise, my scripts are always free and open source.  Objects made with these scripts may be sold with no restrictions.  All I ask is that you point others to this location should they ask you about it and to not sell this script, unless it is for $0 L. Please help improve my work by reporting bugs and improvements.

1 This chatbot is for OpenSim Only. It only works on NPC's with a modified All-In-One NPC script, which is also included.
2 Upload everything and put it in a prim.
3
4 Step 1:
5 Go to PersonalityForge.com. Get a free account at http://www.personalityforge.com. The first 5,000 chats are free.
6
7 Get an API ID, and add it to the apiKey in the script "Chatbot Contoller.lsl".
8
9 string apiKey = "BlahBlah"; // your supplied apiKey from your Chat Bot API subscription
10
11 Add the domain for your OpenSim server or IP to the list of authorized domains at http://www.personalityforge.com/botland/myapi.php
12 Add a checkmark to the "Enable Simple API" in your account.
13 Click on the Simple API tab and pick a chatbot ID from the list of chatbots under the heading "Selecting A Chat Bot ID"
14 for example, Countess Elvira is 99232. Put that in chatBot ID below.
15
16 Sex Bot Ciran is 100387.
17 754 is Liddora a sexy tart
18
19 Now look for this and add that ID:
20
21 string chatBotID = "23958"; // the ID of the chat bot you're talking to
22
23 Save the script.
24
25 Step 2:
26 You need to make the "Appearance" notecard with one to your liking. Dress up however you want, take it all off and put it on again so the server has it recorded okay. Now touch the box and select "Appearance" to record your shape. The easy way is to just use a pre- recorded Avatar Appearance and switch back and forth in Firestorm. The positions and what is worn only get saved to the server when you take them off.
27
28 Step 3:
29 Touch the box again, and make sure it is set to "Relative" and not "Absolute". This is because of the numbers inside the Route notecard. "Absolute" would be used for world positions, relative means relative to the control prim.
30
31 Make sure that "Sensor" has been clicked. No Sensor will leave the NPC in the world at all times. "Sensor" will remove it when no one is around.
32
33 Then click "Start". You NPC should appear, walk 8 meters to one side, and stand there. If you chat with it, the arms should move, and if you can convince it to be happy, it will laugh and animate the face with various emotions.
34
35 This NPC uses the All-In-One NPC controller. So it has a Route notecard. In that notecard is this, which you should probably edit to your liking:
36
37 @spawn=Tinker@www.outworldz.com|<0.000000,0.000000,0.500000>
38 @walk=<0,-8,0.5>
39 @stop
40
41 This causes my little fairy chatbot "Tinker@www.outworldz.com" to appear 1/5 meter above the prim, walk 8 to the left, and stop. Any animatons the fairy plays after that is based on her emotional states from the chatbot.
42
43 You can have your NPC do whatever you want, like walk around, drive a car, etc.
44 More info on this is at http://www.outworldz.com/opensim/posts/npc/

Back to the Best Free Tools in Second Life and OpenSim.