OSSLNPC
From OpenSimulator
m (→Examples: made plural to encourage other additiions without necesssarily ading into the single monolithc script) |
(→osIsNpc) |
||
(57 intermediate revisions by 12 users not shown) | |||
Line 1: | Line 1: | ||
+ | {{Quicklinks}} | ||
+ | <br /> | ||
=Introduction= | =Introduction= | ||
− | Since | + | Since OpenSimulator 0.7.2, a number of functions are provided for for creating and manipulating server-side NPCs (Non Player Characters). |
− | + | NPCs are controlled via a script which must be in the same region as the NPC. This could be housed in an attachment that is attached to the avatar. | |
− | + | Server-side NPCs cannot leave the region in which they were born. | |
− | + | ||
− | + | Server-side NPC appearance is saved and loaded by serializing the appearance data structure to a notecard present in the same prim as the script. The required textures will be preserved when an OAR is saved and loaded. | |
− | + | The current appearance data format (as seen in notecards created by appearing saving) is the same used for the OpenSimulator wire format and so is not designed to be edited directly. With great care it is possible, see [[Appearance Formats]] for more details but it's not recommended unless you really, really need to do it. | |
− | + | = Enabling = | |
− | + | ||
− | + | ||
− | + | ||
− | =Enabling= | + | |
To use these functions, in the OpenSim.ini file you will need the following config | To use these functions, in the OpenSim.ini file you will need the following config | ||
# Enabled = true set in the [NPC] section. | # Enabled = true set in the [NPC] section. | ||
− | # | + | # Enable one script engine |
− | # | + | |
− | # | + | in config-include/osslEnable.ini |
+ | |||
+ | # AllowOSFunctions = true | ||
+ | # check the value of osslNPC to set the rights of most NPC functions. | ||
+ | |||
+ | you may also need to check rights of osAgentSaveAppearance() | ||
+ | |||
+ | Since 0.9.2.0 the maximum number of NPCs per scene is limited to the value of MaxNumberNPCsPerScene entry on [NPC] section. | ||
+ | |||
+ | See [[Configuring Simulator Parameters#Getting information about parameters]] if you need to double check that these parameters have been set correctly. | ||
+ | |||
+ | = Notes = | ||
+ | |||
+ | * When using your avatar to model appearance before saving, you will need to wait a few seconds before invoking any save appearance command. This is because appearance saving currently operates on a timer in order to manage multiple appearance updates from the viewer. | ||
+ | * When using your avatar to model appearance, if you want to reposition attachments on the NPC you will need to | ||
+ | # Reposition them on yourself | ||
+ | # Detach them to inventory | ||
+ | # Reattach them | ||
+ | # Save the appearance | ||
+ | This is because saving appearance data does not trigger saving of current attachment positions. | ||
+ | |||
+ | = Sensing = | ||
+ | |||
+ | llAgentSensor() can be used to set up a sensor that will detected NPCs instead/of as well as other region entities. | ||
+ | |||
+ | In OpenSimulator 0.7.2, LSL sensors will detect NPCs as ordinary agents. | ||
+ | |||
+ | In OpenSimulator 0.7.3-rc1 onwards, unless otherwise specified, NPCs will only be detected using the OpenSimulator-specific NPC flag, e.g. | ||
+ | |||
+ | <source lang="lsl"> | ||
+ | //Author: mewtwo0641 | ||
+ | |||
+ | list keys = []; | ||
+ | key npc; | ||
+ | string toucher; | ||
+ | key toucherkey; | ||
+ | vector toucherPos; | ||
+ | integer npc_on = FALSE; | ||
+ | |||
+ | default | ||
+ | { | ||
+ | touch_start(integer x) | ||
+ | { | ||
+ | toucherkey = llDetectedKey(0); | ||
+ | toucherPos = llDetectedPos(0); | ||
+ | vector npcPos = llGetPos() + <1,1,1>; | ||
+ | |||
+ | if(npc_on == FALSE) | ||
+ | { | ||
+ | npc = osNpcCreate("Fred", "Flintstone", npcPos, toucherkey); | ||
+ | npc_on = TRUE; | ||
+ | llSensor("", "", AGENT | NPC, 96.0, PI); //Will always return NPC key regardless of npc create option | ||
+ | return; | ||
+ | } | ||
+ | |||
+ | if(npc_on == TRUE) | ||
+ | { | ||
+ | osNpcRemove(npc); | ||
+ | llResetScript(); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | sensor(integer num) | ||
+ | { | ||
+ | keys = []; | ||
+ | integer i = 0; | ||
+ | |||
+ | for(i; i < num; i++) | ||
+ | { | ||
+ | keys += llDetectedKey(i); | ||
+ | } | ||
+ | |||
+ | llOwnerSay(llDumpList2String(keys, "\n")); | ||
+ | } | ||
+ | |||
+ | no_sensor() | ||
+ | { | ||
+ | keys = []; | ||
+ | } | ||
+ | } | ||
+ | </source> | ||
+ | |||
+ | You can get NPCs to be detected as AGENTs again using the OS_NPC_SENSE_AS_AGENT option below in osNpcCreate(). | ||
+ | |||
+ | The justification is that detecting NPCs as AGENTs by default may make some scripts unusable (e.g. radars that know nothing about NPCs). However, conversely other scripts may not behave as expected if NPCs aren't sensed as agents (e.g. doors that open automatically for avatars would not open for NPCs). | ||
− | + | llGetDetectedType() will set the NPC flag if an NPC was detected. If the NPC was created with the OS_NPC_SENSE_AS_AGENT option then the AGENT flag will also be set. | |
− | + | = Data Formats = | |
+ | For more information about the format used to store appearance information in notecards, see [[Appearance Formats]]. | ||
− | =Functions= | + | = Functions = |
− | ==Create and Remove== | + | == Create and Remove == |
− | ===[[osNpcCreate]]=== | + | === [[osNpcCreate]] === |
osNpcCreate(string firstname, string lastname, vector position, string cloneFrom):key | osNpcCreate(string firstname, string lastname, vector position, string cloneFrom):key | ||
Line 44: | Line 126: | ||
If the NPC is successfully created, then its UUID is returned, which is required for all subsequent functions. Example is available at [[osNpcCreate]] | If the NPC is successfully created, then its UUID is returned, which is required for all subsequent functions. Example is available at [[osNpcCreate]] | ||
− | ===[[osNpcRemove]]=== | + | (OpenSimulator 0.7.3-rc1 onwards only). This creates an NPC that is 'owned' by the creating script and sensed using the OpenSimulator-only NPC flag for sensors. |
+ | |||
+ | === [[osNpcCreate]] === | ||
+ | |||
+ | OpenSimulator 0.7.3-rc1 onwards. | ||
+ | |||
+ | osNpcCreate(string firstname, string lastname, vector position, string cloneFrom, integer options):key | ||
+ | |||
+ | cloneFrom may be: | ||
+ | |||
+ | * Name of the notecard containing a serialized avatar appearance, or | ||
+ | * Asset UUID of the Notecard, or | ||
+ | * UUID of an avatar logged into the same region. However, please note that this appearance will not be persisted unless osNpcSaveAppearance() is called. | ||
+ | |||
+ | integer is a set of flags that may be 0 or one or more of | ||
+ | |||
+ | * OS_NPC_NOT_OWNED - create an unowned NPC. | ||
+ | * OS_NPC_SENSE_AS_AGENT - create an NPC that is sensed as an AGENT with LSL sensors | ||
+ | * OS_NPC_OBJECT_GROUP - create an NPC with the group of the object with the script, if that object owner is member of that group. | ||
+ | |||
+ | If the NPC is successfully created, then its UUID is returned, which is required for all subsequent functions. Example is available at [[osNpcCreate]] | ||
+ | |||
+ | === [[osNpcRemove]] === | ||
osNpcRemove(key npc):void | osNpcRemove(key npc):void | ||
− | Remove the given avatar from the region. | + | Remove the given avatar from the region. Example at [[osNpcRemove]]. |
+ | |||
+ | == Get and Set == | ||
+ | === [[osIsNpc]] === | ||
+ | |||
+ | OpenSimulator 0.7.3-rc1 onwards. | ||
+ | |||
+ | osIsNpc(key npc):integer | ||
+ | |||
+ | Returns TRUE if the given key is an NPC, false otherwise. | ||
− | + | === [[osNpcGetRot]] === | |
− | ===[[osNpcGetRot]]=== | + | |
− | Gets the rotation of the avatar. | + | Gets the rotation of the avatar. Only the rotation around the Z plane in Euler rotation (horizontal rotation) has any meaning. |
osNpcGetRot(key npc):rotation | osNpcGetRot(key npc):rotation | ||
− | ===[[osNpcSetRot]]=== | + | === [[osNpcSetRot]] === |
osNpcSetRot(key npc, rotation rot):void | osNpcSetRot(key npc, rotation rot):void | ||
− | Set the rotation of the avatar. | + | Set the rotation of the avatar. Only setting the rotation in the Z plane in Euler rotation will have any meaningful effect (turning the avatar to point in one direction or another). Setting X or Y Euler values will result in the avatar rotating in an undefined manner. |
− | ===[[osNpcGetPos]]=== | + | === [[osNpcGetPos]] === |
osNpcGetPos(key npc):vector | osNpcGetPos(key npc):vector | ||
Line 69: | Line 181: | ||
Return the current position of the NPC. | Return the current position of the NPC. | ||
− | ===[[osNpcMoveTo]]=== | + | === [[osNpcGetOwner]] === |
+ | |||
+ | OpenSimulator 0.7.3-rc1 onwards. | ||
+ | |||
+ | osNpcGetOwner(key npc):key | ||
+ | |||
+ | Return the owner of the given NPC (i.e. the owner of the script that created it). If the NPC is unowned or the input key does not belong to an NPC then returns NULL_KEY. | ||
+ | |||
+ | == Movement == | ||
+ | |||
+ | === [[osNpcMoveTo]] === | ||
osNpcMoveTo(key npc, vector position):void | osNpcMoveTo(key npc, vector position):void | ||
Line 75: | Line 197: | ||
An older function that performs an osNpcMoveToTarget() by flying and landing at the target. | An older function that performs an osNpcMoveToTarget() by flying and landing at the target. | ||
− | ===[[osNpcMoveToTarget]]=== | + | === [[osNpcMoveToTarget]] === |
osNpcMoveToTarget(key npc, vector target, int options):void | osNpcMoveToTarget(key npc, vector target, int options):void | ||
− | Move the avatar to a given target over time. | + | Move the avatar to a given target over time. How the avatar will get there depends on the following options. |
− | * OS_NPC_FLY - Fly the avatar to the given position. | + | * 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. |
− | * OS_NPC_NO_FLY - Do not fly to the target. | + | * 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 |
− | * OS_NPC_LAND_AT_TARGET - If given and the avatar is flying, then it will land when it reaches the target. | + | * 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. |
+ | * OS_NPC_RUNNING - if given, NPC avatar moves at running/fast flying speed, otherwise moves at walking/slow flying speed. | ||
− | OS_NPC_FLY and OS_NPC_NO_FLY are options that cannot be combined - the avatar will end up doing one or the other. | + | OS_NPC_FLY and OS_NPC_NO_FLY are options that cannot be combined - the avatar will end up doing one or the other. If you want the avatar to fly and land at the target, then OS_NPC_LAND_AT_TARGET must be combined with OS_NPC_FLY. For instance, |
osNpcMoveToTarget(npc, llGetPos() + <9,9,5>, OS_NPC_FLY|OS_NPC_LAND_AT_TARGET); | osNpcMoveToTarget(npc, llGetPos() + <9,9,5>, OS_NPC_FLY|OS_NPC_LAND_AT_TARGET); | ||
− | ===[[osNpcStopMoveToTarget]]=== | + | === [[osNpcStopMoveToTarget]] === |
osNpcStopMoveToTarget(key npc):void | osNpcStopMoveToTarget(key npc):void | ||
Line 95: | Line 218: | ||
Stop a current move to a target. | Stop a current move to a target. | ||
− | == | + | == Sitting and standing == |
− | ===[[ | + | === [[osNpcSit]] === |
− | + | OpenSimulator 0.7.3-rc1 onwards. | |
− | + | osNpcSit(key npc, key target, int options):void | |
− | ==Appearance== | + | Sit an NPC on a prim target. |
+ | |||
+ | === [[osNpcStand]] === | ||
+ | |||
+ | OpenSimulator 0.7.3-rc1 onwards. | ||
+ | |||
+ | osNpcStand(key npc):void | ||
+ | |||
+ | Make an npc stand up. | ||
+ | |||
+ | == Communication == | ||
+ | |||
+ | === [[osNpcSay]] === | ||
+ | |||
+ | osNpcSay(key npc, string message):void | ||
+ | osNpcSay(key npc, int channel, string message):void | ||
+ | |||
+ | Get the NPC to say the given message. | ||
+ | |||
+ | If channel is specified then the chat is said on the given channel. The channel parameter is only available in OpenSimulator 0.7.4 development code at this time. | ||
+ | |||
+ | === [[osNpcSayTo]] === | ||
+ | |||
+ | osNpcSayTo(key npc, key target, integer channel, string message):void | ||
+ | |||
+ | Get the NPC to say the given message to the given channel to the specified target. | ||
+ | |||
+ | This function is based on llRegionSayTo, it therefore has no range limitation inside the region. | ||
+ | |||
+ | Added in OpenSimulator 0.9.1.0 Dev | ||
+ | |||
+ | === [[osNpcShout]] === | ||
+ | |||
+ | Only in OpenSimulator 0.7.4 development code at this time. | ||
+ | |||
+ | osNpcShout(key npc, int channel, string message):void | ||
+ | |||
+ | Get the NPC to shout the given message on the given channel. | ||
+ | |||
+ | === [[osNpcWhisper]] === | ||
+ | |||
+ | Only in OpenSimulator 0.7.4 development code at this time. | ||
+ | |||
+ | osNpcWhisper(key npc, int channel, string message):void | ||
+ | |||
+ | Get the NPC to whisper the given message on the given channel. | ||
+ | |||
+ | == Animations == | ||
+ | |||
+ | === [[osNpcPlayAnimation]] === | ||
+ | |||
+ | OpenSimulator 0.7.3-rc1 onwards. | ||
+ | |||
+ | osNpcPlayAnimation(key npc, string animation):void | ||
+ | |||
+ | Get an NPC to play an animation. The animation can either be a key or the name of an animation in the same inventory as the script. | ||
+ | |||
+ | === [[osNpcStopAnimation]] === | ||
+ | |||
+ | In OpenSimulator 0.7.3-rc1 there is a bug which makes this play the animation instead. This will be corrected for the final release but in the mean time please use osAvatarStopAnimation() instead. | ||
+ | |||
+ | osNpcStopAnimation(key npc, string animation):void | ||
+ | |||
+ | Get an NPC to stop playing an animation. The animation can either be a key or the name of an animation in the same inventory as the script. | ||
+ | |||
+ | == Appearance == | ||
NPC appearance is manipulated by saving and loading appearance data to notecards from the same inventory as the invoking script. | NPC appearance is manipulated by saving and loading appearance data to notecards from the same inventory as the invoking script. | ||
− | ===[[osOwnerSaveAppearance]]=== | + | === [[osOwnerSaveAppearance]] === |
osOwnerSaveAppearance(string notecard):key | osOwnerSaveAppearance(string notecard):key | ||
− | Save the owner's current appearance to a notecard in the prim's inventory. | + | Save the owner's current appearance to a notecard in the prim's inventory. This includes body part data, clothing items and attachments. If a notecard with the same name already exists then it is replaced. The owner must be present in the region when this function is invoked. The baked textures for the owner (necessary to recreate appearance on the NPC) are saved permanently. |
− | ===[[osAgentSaveAppearance]]=== | + | === [[osAgentSaveAppearance]] === |
osAgentSaveAppearance(key agentId, string notecard):key | osAgentSaveAppearance(key agentId, string notecard):key | ||
− | Save an arbitrary avatar's appearance to a notecard in the prim's inventory. | + | Save an arbitrary avatar's appearance to a notecard in the prim's inventory. This includes body part data, clothing items and attachments. If a notecard with the same name already exists then it is replaced. The avatar must be present in the region when this function is invoked. The baked textures for the avatar (necessary to recreate appearance on the NPC) are saved permanently. |
− | ===[[osNpcSaveAppearance]]=== | + | === [[osNpcSaveAppearance]] === |
osNpcSaveAppearance(key npc, string notecard):key | osNpcSaveAppearance(key npc, string notecard):key | ||
− | Save the NPC's current appearance to a notecard in the prim's inventory. | + | Save the NPC's current appearance to a notecard in the prim's inventory. This includes body part data, clothing items and attachments. If a notecard with the same name already exists then it is replaced. The avatar must be present in the region when this function is invoked. The baked textures for the avatar (necessary to recreate appearance) are saved permanently. |
− | ===[[osNpcLoadAppearance]]=== | + | === [[osNpcLoadAppearance]] === |
osNpcLoadAppearance(key npc, string notecard):void | osNpcLoadAppearance(key npc, string notecard):void | ||
− | Load appearance from a notecard. | + | Load appearance from a notecard. This notecard must contain appearance data created with one of the save appearance functions. |
− | |||
− | This is a rough example script for most of the current NPC functionality. Please feel free to improve it. | + | == Touch == |
+ | === [[osNpcTouch]] === | ||
+ | |||
+ | OpenSimulator 0.7.4 development code at this time. | ||
+ | |||
+ | osNpcTouch(key npc, key objectKey, integer linkNum) | ||
+ | |||
+ | Allows the NPC to touch objects. | ||
+ | |||
+ | = Examples = | ||
+ | |||
+ | == NPC Automator == | ||
+ | |||
+ | *[[NPC Automator 2.0]] | ||
+ | |||
+ | This is a rough example script for most of the current NPC functionality. One of its major current deficiencies is that it doesn't track more than one created avatar at a time. Please feel free to improve it. | ||
<source lang = "lsl"> | <source lang = "lsl"> | ||
key npc; | key npc; | ||
+ | integer listenChannel = 10; | ||
+ | |||
default | default | ||
{ | { | ||
+ | // NPC manipulator adapted by justincc 0.0.3 released 20121025 | ||
state_entry() | state_entry() | ||
{ | { | ||
− | llListen( | + | llListen(listenChannel,"",NULL_KEY,""); |
+ | llSetText("Listening on " + listenChannel, <0, 255, 0>, 1); | ||
+ | llOwnerSay("Say /" + (string)listenChannel + " help for commands"); | ||
} | } | ||
Line 148: | Line 355: | ||
if (msg != "") | if (msg != "") | ||
{ | { | ||
− | + | list commands = llParseString2List(msg, [ " " ], []); | |
+ | string msg0 = llList2String(commands, 0); | ||
+ | string msg1 = llList2String(commands, 1); | ||
+ | string msg2 = llList2String(commands, 2); | ||
+ | string msg3 = llList2String(commands, 3); | ||
+ | |||
+ | if (msg0 == "create") | ||
+ | { | ||
+ | if (msg1 != "") | ||
+ | { | ||
+ | string notecardName = msg1; | ||
+ | |||
+ | npc = osNpcCreate("Jane", "Doe", llGetPos() + <5, 5, 0>, notecardName); | ||
+ | |||
+ | llOwnerSay("Created npc from notecard " + notecardName); | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | llOwnerSay("Usage: create <notecard-name>"); | ||
+ | } | ||
+ | } | ||
+ | else if (msg0 =="createm" && msg1 != "") | ||
{ | { | ||
osOwnerSaveAppearance("appearance"); | osOwnerSaveAppearance("appearance"); | ||
− | + | vector pos = llGetPos(); | |
− | } | + | integer i; |
− | else if ( | + | for (i = 0; i < (integer)msg1; i++) |
+ | { | ||
+ | osNpcCreate("John", "Doe", pos + <8, 0, 0>, "appearance"); | ||
+ | llSleep(1); | ||
+ | } | ||
+ | } | ||
+ | else if (msg0 == "remove" && npc != NULL_KEY) | ||
{ | { | ||
osNpcSay(npc, "You will pay for this with your liiiiiivvveeessss!!!....."); | osNpcSay(npc, "You will pay for this with your liiiiiivvveeessss!!!....."); | ||
− | osNpcRemove (npc); | + | osNpcRemove(npc); |
} | } | ||
− | else if ( | + | else if (msg0 == "say" && npc != NULL_KEY) |
{ | { | ||
osNpcSay(npc, "I am your worst Nightmare!!!!"); | osNpcSay(npc, "I am your worst Nightmare!!!!"); | ||
} | } | ||
− | else if ( | + | else if (msg0 == "move") |
{ | { | ||
− | osNpcMoveTo(npc, | + | if (msg1 != "" && msg2 != "" && npc != NULL_KEY) |
+ | { | ||
+ | vector delta = <(integer)msg1, (integer)msg2, 0>; | ||
+ | |||
+ | if (msg3 != "") | ||
+ | { | ||
+ | delta.z = (integer)msg3; | ||
+ | } | ||
+ | |||
+ | osNpcMoveTo(npc, osNpcGetPos(npc) + delta); | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | llOwnerSay("Usage: move <x> <y> [<z>]"); | ||
+ | } | ||
} | } | ||
− | else if ( | + | else if (msg0 == "moveto") |
+ | { | ||
+ | if (msg1 != "" && msg2 != "" && npc != NULL_KEY) | ||
+ | { | ||
+ | vector pos = <(integer)msg1, (integer)msg2, 0>; | ||
+ | |||
+ | if (msg3 != "") | ||
+ | { | ||
+ | pos.z = (integer)msg3; | ||
+ | } | ||
+ | |||
+ | osNpcMoveTo(npc, pos); | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | llOwnerSay("Usage: move <x> <y> [<z>]"); | ||
+ | } | ||
+ | } | ||
+ | else if (msg0 == "movetarget" && npc != NULL_KEY) | ||
{ | { | ||
osNpcMoveToTarget(npc, llGetPos() + <9,9,5>, OS_NPC_FLY|OS_NPC_LAND_AT_TARGET); | osNpcMoveToTarget(npc, llGetPos() + <9,9,5>, OS_NPC_FLY|OS_NPC_LAND_AT_TARGET); | ||
} | } | ||
− | else if ( | + | else if (msg0 == "movetargetnoland" && npc != NULL_KEY) |
{ | { | ||
osNpcMoveToTarget(npc, llGetPos() + <9,9,5>, OS_NPC_FLY); | osNpcMoveToTarget(npc, llGetPos() + <9,9,5>, OS_NPC_FLY); | ||
} | } | ||
− | else if ( | + | else if (msg0 == "movetargetwalk" && npc != NULL_KEY) |
{ | { | ||
osNpcMoveToTarget(npc, llGetPos() + <9,9,0>, OS_NPC_NO_FLY); | osNpcMoveToTarget(npc, llGetPos() + <9,9,0>, OS_NPC_NO_FLY); | ||
} | } | ||
− | else if ( | + | else if (msg0 == "rot" && npc != NULL_KEY) |
{ | { | ||
− | vector xyz_angles = <0,0, | + | vector xyz_angles = <0,0,90>; // This is to define a 1 degree change |
vector angles_in_radians = xyz_angles * DEG_TO_RAD; // Change to Radians | vector angles_in_radians = xyz_angles * DEG_TO_RAD; // Change to Radians | ||
rotation rot_xyzq = llEuler2Rot(angles_in_radians); // Change to a Rotation | rotation rot_xyzq = llEuler2Rot(angles_in_radians); // Change to a Rotation | ||
Line 186: | Line 452: | ||
osNpcSetRot(npc, rot * rot_xyzq); | osNpcSetRot(npc, rot * rot_xyzq); | ||
} | } | ||
− | else if ( | + | else if (msg0 == "rotabs" && msg1 != "") |
+ | { | ||
+ | vector xyz_angles = <0, 0, (integer)msg1>; | ||
+ | vector angles_in_radians = xyz_angles * DEG_TO_RAD; // Change to Radians | ||
+ | rotation rot_xyzq = llEuler2Rot(angles_in_radians); // Change to a Rotation | ||
+ | osNpcSetRot(npc, rot_xyzq); | ||
+ | } | ||
+ | else if (msg0 == "animate" && npc != NULL_KEY) | ||
{ | { | ||
osAvatarPlayAnimation(npc, "stabbed+die_2"); | osAvatarPlayAnimation(npc, "stabbed+die_2"); | ||
Line 192: | Line 465: | ||
osAvatarStopAnimation(npc, "stabbed+die_2"); | osAvatarStopAnimation(npc, "stabbed+die_2"); | ||
} | } | ||
− | else if ( | + | else if (msg0 == "save" && msg1 != "" && npc != NULL_KEY) |
{ | { | ||
− | osNpcSaveAppearance(npc, "appearance"); | + | osNpcSaveAppearance(npc, msg1); |
+ | llOwnerSay("Saved appearance " + msg1 + " to " + npc); | ||
} | } | ||
− | else if ( | + | else if (msg0 == "load" && msg1 != "" && npc != NULL_KEY) |
{ | { | ||
− | osNpcLoadAppearance(npc, "appearance"); | + | osNpcLoadAppearance(npc, msg1); |
+ | llOwnerSay("Loaded appearance " + msg1 + " to " + npc); | ||
} | } | ||
− | else if ( | + | else if (msg0 == "clone") |
{ | { | ||
− | osOwnerSaveAppearance("appearance"); | + | if (msg1 != "") |
+ | { | ||
+ | osOwnerSaveAppearance(msg1); | ||
+ | llOwnerSay("Cloned your appearance to " + msg1); | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | llOwnerSay("Usage: clone <notecard-name-to-save>"); | ||
+ | } | ||
} | } | ||
− | else if ( | + | else if (msg0 == "stop" && npc != NULL_KEY) |
{ | { | ||
osNpcStopMoveToTarget(npc); | osNpcStopMoveToTarget(npc); | ||
+ | } | ||
+ | else if (msg0 == "sit" && msg1 != "" && npc != NULL_KEY) | ||
+ | { | ||
+ | osNpcSit(npc, msg1, OS_NPC_SIT_NOW); | ||
+ | } | ||
+ | else if (msg0 == "stand" && npc != NULL_KEY) | ||
+ | { | ||
+ | osNpcStand(npc); | ||
+ | } | ||
+ | else if (msg0 == "help") | ||
+ | { | ||
+ | llOwnerSay("Commands are:"); | ||
+ | llOwnerSay("create <notecard-name> - Create NPC from a stored notecard"); | ||
+ | llOwnerSay("createm"); | ||
+ | llOwnerSay("remove - Remove current NPC"); | ||
+ | llOwnerSay("clone <notecard-name> - Clone own appearance to a notecard"); | ||
+ | llOwnerSay("load <notecard-name> - Load appearance on notecard to current npc"); | ||
+ | llOwnerSay("save <notecard-name> - Save appearance of current NPC to notecard"); | ||
+ | llOwnerSay("animate"); | ||
+ | llOwnerSay("move"); | ||
+ | llOwnerSay("moveto <x> <y> <z> - move to absolute position"); | ||
+ | llOwnerSay("movetarget"); | ||
+ | llOwnerSay("movetargetnoland"); | ||
+ | llOwnerSay("movetargetwalk"); | ||
+ | llOwnerSay("rot"); | ||
+ | llOwnerSay("say"); | ||
+ | llOwnerSay("sit <target-uuid>"); | ||
+ | llOwnerSay("stop"); | ||
+ | llOwnerSay("stand"); | ||
} | } | ||
else | else | ||
Line 217: | Line 529: | ||
</source> | </source> | ||
− | =Questions/Comments= | + | == Wandering and Puppeteer == |
+ | |||
+ | [http://grimore.org Wizardry and Steamworks] provides free-to-use NPC extendable set of scripts, published under the GPLv3 license, that implements most of the NPC functions and allows you to conveniently create NPCs. There are several several scripts available at the time of writing: | ||
+ | |||
+ | * [http://grimore.org/opensim/npc/wandering Wandering NPCs] is a script that just makes a non-interative NPC wander around in a given area. | ||
+ | * [http://grimore.org/opensim/npc/puppeteer NPC Puppeteer] is a system that will allow you to playwright NPCs without too much knowledge of either LSL or OSSL. | ||
+ | |||
+ | == OSW NPCs Controller== | ||
+ | |||
+ | OpenSimWorld provides an NPC controller that supports interactive NPCs. The system allows you to give commands to | ||
+ | NPCs through the local chat and to build complex behaviors through easy-to-write notecards. It also allows you to specify a "map" | ||
+ | of your region so that the NPCs can walk through it. | ||
+ | |||
+ | [https://github.com/opensimworld/active-npcs More information] | ||
+ | |||
+ | = Questions/Comments = | ||
Please leave your questions and comments on [[Talk:OSSLNPC|this article's talk page]]. | Please leave your questions and comments on [[Talk:OSSLNPC|this article's talk page]]. | ||
− | [[Category: | + | [[Category:OSSL Functions]] |
[[Category:Scripting]] | [[Category:Scripting]] |
Latest revision as of 02:06, 16 September 2021
Contents |
[edit] Introduction
Since OpenSimulator 0.7.2, a number of functions are provided for for creating and manipulating server-side NPCs (Non Player Characters).
NPCs are controlled via a script which must be in the same region as the NPC. This could be housed in an attachment that is attached to the avatar.
Server-side NPCs cannot leave the region in which they were born.
Server-side NPC appearance is saved and loaded by serializing the appearance data structure to a notecard present in the same prim as the script. The required textures will be preserved when an OAR is saved and loaded.
The current appearance data format (as seen in notecards created by appearing saving) is the same used for the OpenSimulator wire format and so is not designed to be edited directly. With great care it is possible, see Appearance Formats for more details but it's not recommended unless you really, really need to do it.
[edit] Enabling
To use these functions, in the OpenSim.ini file you will need the following config
- Enabled = true set in the [NPC] section.
- Enable one script engine
in config-include/osslEnable.ini
- AllowOSFunctions = true
- check the value of osslNPC to set the rights of most NPC functions.
you may also need to check rights of osAgentSaveAppearance()
Since 0.9.2.0 the maximum number of NPCs per scene is limited to the value of MaxNumberNPCsPerScene entry on [NPC] section.
See Configuring Simulator Parameters#Getting information about parameters if you need to double check that these parameters have been set correctly.
[edit] Notes
- When using your avatar to model appearance before saving, you will need to wait a few seconds before invoking any save appearance command. This is because appearance saving currently operates on a timer in order to manage multiple appearance updates from the viewer.
- When using your avatar to model appearance, if you want to reposition attachments on the NPC you will need to
- Reposition them on yourself
- Detach them to inventory
- Reattach them
- Save the appearance
This is because saving appearance data does not trigger saving of current attachment positions.
[edit] Sensing
llAgentSensor() can be used to set up a sensor that will detected NPCs instead/of as well as other region entities.
In OpenSimulator 0.7.2, LSL sensors will detect NPCs as ordinary agents.
In OpenSimulator 0.7.3-rc1 onwards, unless otherwise specified, NPCs will only be detected using the OpenSimulator-specific NPC flag, e.g.
//Author: mewtwo0641 list keys = []; key npc; string toucher; key toucherkey; vector toucherPos; integer npc_on = FALSE; default { touch_start(integer x) { toucherkey = llDetectedKey(0); toucherPos = llDetectedPos(0); vector npcPos = llGetPos() + <1,1,1>; if(npc_on == FALSE) { npc = osNpcCreate("Fred", "Flintstone", npcPos, toucherkey); npc_on = TRUE; llSensor("", "", AGENT | NPC, 96.0, PI); //Will always return NPC key regardless of npc create option return; } if(npc_on == TRUE) { osNpcRemove(npc); llResetScript(); } } sensor(integer num) { keys = []; integer i = 0; for(i; i < num; i++) { keys += llDetectedKey(i); } llOwnerSay(llDumpList2String(keys, "\n")); } no_sensor() { keys = []; } }
You can get NPCs to be detected as AGENTs again using the OS_NPC_SENSE_AS_AGENT option below in osNpcCreate().
The justification is that detecting NPCs as AGENTs by default may make some scripts unusable (e.g. radars that know nothing about NPCs). However, conversely other scripts may not behave as expected if NPCs aren't sensed as agents (e.g. doors that open automatically for avatars would not open for NPCs).
llGetDetectedType() will set the NPC flag if an NPC was detected. If the NPC was created with the OS_NPC_SENSE_AS_AGENT option then the AGENT flag will also be set.
[edit] Data Formats
For more information about the format used to store appearance information in notecards, see Appearance Formats.
[edit] Functions
[edit] Create and Remove
[edit] osNpcCreate
osNpcCreate(string firstname, string lastname, vector position, string cloneFrom):key
cloneFrom may be:
- Name of the notecard containing a serialized avatar appearance, or
- Asset UUID of the Notecard, or
- UUID of an avatar logged into the same region. However, please note that this appearance will not be persisted unless osNpcSaveAppearance() is called.
If the NPC is successfully created, then its UUID is returned, which is required for all subsequent functions. Example is available at osNpcCreate
(OpenSimulator 0.7.3-rc1 onwards only). This creates an NPC that is 'owned' by the creating script and sensed using the OpenSimulator-only NPC flag for sensors.
[edit] osNpcCreate
OpenSimulator 0.7.3-rc1 onwards.
osNpcCreate(string firstname, string lastname, vector position, string cloneFrom, integer options):key
cloneFrom may be:
- Name of the notecard containing a serialized avatar appearance, or
- Asset UUID of the Notecard, or
- UUID of an avatar logged into the same region. However, please note that this appearance will not be persisted unless osNpcSaveAppearance() is called.
integer is a set of flags that may be 0 or one or more of
- OS_NPC_NOT_OWNED - create an unowned NPC.
- OS_NPC_SENSE_AS_AGENT - create an NPC that is sensed as an AGENT with LSL sensors
- OS_NPC_OBJECT_GROUP - create an NPC with the group of the object with the script, if that object owner is member of that group.
If the NPC is successfully created, then its UUID is returned, which is required for all subsequent functions. Example is available at osNpcCreate
[edit] osNpcRemove
osNpcRemove(key npc):void
Remove the given avatar from the region. Example at osNpcRemove.
[edit] Get and Set
[edit] osIsNpc
OpenSimulator 0.7.3-rc1 onwards.
osIsNpc(key npc):integer
Returns TRUE if the given key is an NPC, false otherwise.
[edit] osNpcGetRot
Gets the rotation of the avatar. Only the rotation around the Z plane in Euler rotation (horizontal rotation) has any meaning.
osNpcGetRot(key npc):rotation
[edit] osNpcSetRot
osNpcSetRot(key npc, rotation rot):void
Set the rotation of the avatar. Only setting the rotation in the Z plane in Euler rotation will have any meaningful effect (turning the avatar to point in one direction or another). Setting X or Y Euler values will result in the avatar rotating in an undefined manner.
[edit] osNpcGetPos
osNpcGetPos(key npc):vector
Return the current position of the NPC.
[edit] osNpcGetOwner
OpenSimulator 0.7.3-rc1 onwards.
osNpcGetOwner(key npc):key
Return the owner of the given NPC (i.e. the owner of the script that created it). If the NPC is unowned or the input key does not belong to an NPC then returns NULL_KEY.
[edit] Movement
[edit] osNpcMoveTo
osNpcMoveTo(key npc, vector position):void
An older function that performs an osNpcMoveToTarget() by flying and landing at the target.
[edit] osNpcMoveToTarget
osNpcMoveToTarget(key npc, vector target, int options):void
Move the avatar to a given target over time. How the avatar will get there depends on the following options.
- 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.
- 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
- 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.
- OS_NPC_RUNNING - if given, NPC avatar moves at running/fast flying speed, otherwise moves at walking/slow flying speed.
OS_NPC_FLY and OS_NPC_NO_FLY are options that cannot be combined - the avatar will end up doing one or the other. If you want the avatar to fly and land at the target, then OS_NPC_LAND_AT_TARGET must be combined with OS_NPC_FLY. For instance,
osNpcMoveToTarget(npc, llGetPos() + <9,9,5>, OS_NPC_FLY|OS_NPC_LAND_AT_TARGET);
[edit] osNpcStopMoveToTarget
osNpcStopMoveToTarget(key npc):void
Stop a current move to a target.
[edit] Sitting and standing
[edit] osNpcSit
OpenSimulator 0.7.3-rc1 onwards.
osNpcSit(key npc, key target, int options):void
Sit an NPC on a prim target.
[edit] osNpcStand
OpenSimulator 0.7.3-rc1 onwards.
osNpcStand(key npc):void
Make an npc stand up.
[edit] Communication
[edit] osNpcSay
osNpcSay(key npc, string message):void osNpcSay(key npc, int channel, string message):void
Get the NPC to say the given message.
If channel is specified then the chat is said on the given channel. The channel parameter is only available in OpenSimulator 0.7.4 development code at this time.
[edit] osNpcSayTo
osNpcSayTo(key npc, key target, integer channel, string message):void
Get the NPC to say the given message to the given channel to the specified target.
This function is based on llRegionSayTo, it therefore has no range limitation inside the region.
Added in OpenSimulator 0.9.1.0 Dev
[edit] osNpcShout
Only in OpenSimulator 0.7.4 development code at this time.
osNpcShout(key npc, int channel, string message):void
Get the NPC to shout the given message on the given channel.
[edit] osNpcWhisper
Only in OpenSimulator 0.7.4 development code at this time.
osNpcWhisper(key npc, int channel, string message):void
Get the NPC to whisper the given message on the given channel.
[edit] Animations
[edit] osNpcPlayAnimation
OpenSimulator 0.7.3-rc1 onwards.
osNpcPlayAnimation(key npc, string animation):void
Get an NPC to play an animation. The animation can either be a key or the name of an animation in the same inventory as the script.
[edit] osNpcStopAnimation
In OpenSimulator 0.7.3-rc1 there is a bug which makes this play the animation instead. This will be corrected for the final release but in the mean time please use osAvatarStopAnimation() instead.
osNpcStopAnimation(key npc, string animation):void
Get an NPC to stop playing an animation. The animation can either be a key or the name of an animation in the same inventory as the script.
[edit] Appearance
NPC appearance is manipulated by saving and loading appearance data to notecards from the same inventory as the invoking script.
[edit] osOwnerSaveAppearance
osOwnerSaveAppearance(string notecard):key
Save the owner's current appearance to a notecard in the prim's inventory. This includes body part data, clothing items and attachments. If a notecard with the same name already exists then it is replaced. The owner must be present in the region when this function is invoked. The baked textures for the owner (necessary to recreate appearance on the NPC) are saved permanently.
[edit] osAgentSaveAppearance
osAgentSaveAppearance(key agentId, string notecard):key
Save an arbitrary avatar's appearance to a notecard in the prim's inventory. This includes body part data, clothing items and attachments. If a notecard with the same name already exists then it is replaced. The avatar must be present in the region when this function is invoked. The baked textures for the avatar (necessary to recreate appearance on the NPC) are saved permanently.
[edit] osNpcSaveAppearance
osNpcSaveAppearance(key npc, string notecard):key
Save the NPC's current appearance to a notecard in the prim's inventory. This includes body part data, clothing items and attachments. If a notecard with the same name already exists then it is replaced. The avatar must be present in the region when this function is invoked. The baked textures for the avatar (necessary to recreate appearance) are saved permanently.
[edit] osNpcLoadAppearance
osNpcLoadAppearance(key npc, string notecard):void
Load appearance from a notecard. This notecard must contain appearance data created with one of the save appearance functions.
[edit] Touch
[edit] osNpcTouch
OpenSimulator 0.7.4 development code at this time.
osNpcTouch(key npc, key objectKey, integer linkNum)
Allows the NPC to touch objects.
[edit] Examples
[edit] NPC Automator
This is a rough example script for most of the current NPC functionality. One of its major current deficiencies is that it doesn't track more than one created avatar at a time. Please feel free to improve it.
key npc; integer listenChannel = 10; default { // NPC manipulator adapted by justincc 0.0.3 released 20121025 state_entry() { llListen(listenChannel,"",NULL_KEY,""); llSetText("Listening on " + listenChannel, <0, 255, 0>, 1); llOwnerSay("Say /" + (string)listenChannel + " help for commands"); } listen(integer channel, string name, key id, string msg) { if (msg != "") { list commands = llParseString2List(msg, [ " " ], []); string msg0 = llList2String(commands, 0); string msg1 = llList2String(commands, 1); string msg2 = llList2String(commands, 2); string msg3 = llList2String(commands, 3); if (msg0 == "create") { if (msg1 != "") { string notecardName = msg1; npc = osNpcCreate("Jane", "Doe", llGetPos() + <5, 5, 0>, notecardName); llOwnerSay("Created npc from notecard " + notecardName); } else { llOwnerSay("Usage: create <notecard-name>"); } } else if (msg0 =="createm" && msg1 != "") { osOwnerSaveAppearance("appearance"); vector pos = llGetPos(); integer i; for (i = 0; i < (integer)msg1; i++) { osNpcCreate("John", "Doe", pos + <8, 0, 0>, "appearance"); llSleep(1); } } else if (msg0 == "remove" && npc != NULL_KEY) { osNpcSay(npc, "You will pay for this with your liiiiiivvveeessss!!!....."); osNpcRemove(npc); } else if (msg0 == "say" && npc != NULL_KEY) { osNpcSay(npc, "I am your worst Nightmare!!!!"); } else if (msg0 == "move") { if (msg1 != "" && msg2 != "" && npc != NULL_KEY) { vector delta = <(integer)msg1, (integer)msg2, 0>; if (msg3 != "") { delta.z = (integer)msg3; } osNpcMoveTo(npc, osNpcGetPos(npc) + delta); } else { llOwnerSay("Usage: move <x> <y> [<z>]"); } } else if (msg0 == "moveto") { if (msg1 != "" && msg2 != "" && npc != NULL_KEY) { vector pos = <(integer)msg1, (integer)msg2, 0>; if (msg3 != "") { pos.z = (integer)msg3; } osNpcMoveTo(npc, pos); } else { llOwnerSay("Usage: move <x> <y> [<z>]"); } } else if (msg0 == "movetarget" && npc != NULL_KEY) { osNpcMoveToTarget(npc, llGetPos() + <9,9,5>, OS_NPC_FLY|OS_NPC_LAND_AT_TARGET); } else if (msg0 == "movetargetnoland" && npc != NULL_KEY) { osNpcMoveToTarget(npc, llGetPos() + <9,9,5>, OS_NPC_FLY); } else if (msg0 == "movetargetwalk" && npc != NULL_KEY) { osNpcMoveToTarget(npc, llGetPos() + <9,9,0>, OS_NPC_NO_FLY); } else if (msg0 == "rot" && npc != NULL_KEY) { vector xyz_angles = <0,0,90>; // This is to define a 1 degree change vector angles_in_radians = xyz_angles * DEG_TO_RAD; // Change to Radians rotation rot_xyzq = llEuler2Rot(angles_in_radians); // Change to a Rotation rotation rot = osNpcGetRot(npc); osNpcSetRot(npc, rot * rot_xyzq); } else if (msg0 == "rotabs" && msg1 != "") { vector xyz_angles = <0, 0, (integer)msg1>; vector angles_in_radians = xyz_angles * DEG_TO_RAD; // Change to Radians rotation rot_xyzq = llEuler2Rot(angles_in_radians); // Change to a Rotation osNpcSetRot(npc, rot_xyzq); } else if (msg0 == "animate" && npc != NULL_KEY) { osAvatarPlayAnimation(npc, "stabbed+die_2"); llSleep(3); osAvatarStopAnimation(npc, "stabbed+die_2"); } else if (msg0 == "save" && msg1 != "" && npc != NULL_KEY) { osNpcSaveAppearance(npc, msg1); llOwnerSay("Saved appearance " + msg1 + " to " + npc); } else if (msg0 == "load" && msg1 != "" && npc != NULL_KEY) { osNpcLoadAppearance(npc, msg1); llOwnerSay("Loaded appearance " + msg1 + " to " + npc); } else if (msg0 == "clone") { if (msg1 != "") { osOwnerSaveAppearance(msg1); llOwnerSay("Cloned your appearance to " + msg1); } else { llOwnerSay("Usage: clone <notecard-name-to-save>"); } } else if (msg0 == "stop" && npc != NULL_KEY) { osNpcStopMoveToTarget(npc); } else if (msg0 == "sit" && msg1 != "" && npc != NULL_KEY) { osNpcSit(npc, msg1, OS_NPC_SIT_NOW); } else if (msg0 == "stand" && npc != NULL_KEY) { osNpcStand(npc); } else if (msg0 == "help") { llOwnerSay("Commands are:"); llOwnerSay("create <notecard-name> - Create NPC from a stored notecard"); llOwnerSay("createm"); llOwnerSay("remove - Remove current NPC"); llOwnerSay("clone <notecard-name> - Clone own appearance to a notecard"); llOwnerSay("load <notecard-name> - Load appearance on notecard to current npc"); llOwnerSay("save <notecard-name> - Save appearance of current NPC to notecard"); llOwnerSay("animate"); llOwnerSay("move"); llOwnerSay("moveto <x> <y> <z> - move to absolute position"); llOwnerSay("movetarget"); llOwnerSay("movetargetnoland"); llOwnerSay("movetargetwalk"); llOwnerSay("rot"); llOwnerSay("say"); llOwnerSay("sit <target-uuid>"); llOwnerSay("stop"); llOwnerSay("stand"); } else { llOwnerSay("I don't understand [" + msg + "]"); } } } }
[edit] Wandering and Puppeteer
Wizardry and Steamworks provides free-to-use NPC extendable set of scripts, published under the GPLv3 license, that implements most of the NPC functions and allows you to conveniently create NPCs. There are several several scripts available at the time of writing:
- Wandering NPCs is a script that just makes a non-interative NPC wander around in a given area.
- NPC Puppeteer is a system that will allow you to playwright NPCs without too much knowledge of either LSL or OSSL.
[edit] OSW NPCs Controller
OpenSimWorld provides an NPC controller that supports interactive NPCs. The system allows you to give commands to NPCs through the local chat and to build complex behaviors through easy-to-write notecards. It also allows you to specify a "map" of your region so that the NPCs can walk through it.
[edit] Questions/Comments
Please leave your questions and comments on this article's talk page.