396 lines
14 KiB
Plaintext
396 lines
14 KiB
Plaintext
// :SHOW:1
|
|
// :CATEGORY:NPC
|
|
// :NAME:Hypergrid Story One
|
|
// :AUTHOR:Ferd Frederix
|
|
// :KEYWORDS:Game, Collider
|
|
// :CREATED:2015-11-24 20:36:34
|
|
// :EDITED:2015-11-24 19:36:34
|
|
// :ID:1089
|
|
// :NUM:1856
|
|
// :REV:3.0
|
|
// :WORLD:Opensim
|
|
// :DESCRIPTION:
|
|
// Sample sequencer script for NPC animator. It sequences multiple NPCs in order thru a scenarios
|
|
// each 'things' entry is the NPC number, a (float) time to take between sending commands ( 0 is not allowed, but a small number is()
|
|
///and a @command that is sent to the NPC.
|
|
|
|
// :CODE:
|
|
|
|
// Rev: 2 fixes the Linux bug for collisions.
|
|
//
|
|
// Rev: 3 Neo Cortex: modified to become a standalone sequencer for Scene one
|
|
//
|
|
// Rev: 4 Aine Caoimhe: added support for custom appearance change slave scripts for finale sequence with 2 new commands:
|
|
// @dumpkeys must be called when all NPCs have been rezzed (ie when npcList has been populated with their keys)
|
|
// this relays the key data to the slave scripts which are numbered from 01 to 12
|
|
// @slaveap=target|appearance_notecard_name where:
|
|
// target 1 = namaka (for final one)
|
|
// target 2 = mirror namakas (for final one)
|
|
// target 3 = all namaka (for all the other ones for her)
|
|
// target -1 = dylan (for the final one)
|
|
// target -2 = mirror dylans (for the final one)
|
|
// target -3 = all dylans (for all the ones for him)
|
|
// in both of the above commands you can use any non-zero npc number but it's ignored so I'd just use 1 each time
|
|
// it will respect a time delay value if you set one
|
|
// example:
|
|
// after all NPCs have been rezzed you need to call (but you only need to do it once):
|
|
// things += [1,0,"@dumpkeys"];
|
|
// then to have all of the dylans change to appearance notecard "dylan scene 2" with no delay after it you would do:
|
|
// things += [1,0,"@slaveap=-3|dylan scene 2"];
|
|
|
|
string myStoryNotecard = "!Story";
|
|
|
|
integer debug = FALSE;
|
|
integer LSLEditor = FALSE; // set to to TRUE to working in LSLEditor, FALSE for in-world.
|
|
integer iTitleText = TRUE; // set to TRUE to see debug info in text above the controller
|
|
|
|
integer SENSE = FALSE; // sensor for an avatar
|
|
float RATE = 5; // every 5 seconds
|
|
float RANGE = 2; // a very short range as this goes into a ring around the avatar
|
|
integer COLLIDE = TRUE; // if they collide, trigger the sequence
|
|
|
|
integer isRunning = FALSE; // used to stop collision from firing twice
|
|
|
|
list npcList = [ (key) "00000000-0000-0000-0000-000000000000", "empty npc0", // this one is ignored
|
|
(key) "00000000-0000-0000-0000-000000000000", "empty npc1",
|
|
(key) "00000000-0000-0000-0000-000000000000", "empty npc2",
|
|
(key) "00000000-0000-0000-0000-000000000000", "empty npc3",
|
|
(key) "00000000-0000-0000-0000-000000000000", "empty npc4",
|
|
(key) "00000000-0000-0000-0000-000000000000", "empty npc5",
|
|
(key) "00000000-0000-0000-0000-000000000000", "empty npc6",
|
|
(key) "00000000-0000-0000-0000-000000000000", "empty npc7",
|
|
(key) "00000000-0000-0000-0000-000000000000", "empty npc8",
|
|
(key) "00000000-0000-0000-0000-000000000000", "empty npc9",
|
|
(key) "00000000-0000-0000-0000-000000000000", "empty npc10",
|
|
(key) "00000000-0000-0000-0000-000000000000", "empty npc11",
|
|
(key) "00000000-0000-0000-0000-000000000000", "empty npc12"];
|
|
|
|
list myObjects = [];
|
|
list myObjectKeys = [];
|
|
integer myObjectsInitialized = FALSE;
|
|
|
|
|
|
string npcAction = "";
|
|
string npcParams = "";
|
|
key npcKey = "";
|
|
integer NPCOptions = OS_NPC_CREATOR_OWNED; // only the owner of this box can control this NPC.
|
|
|
|
key oldCollider; //remember who collided last
|
|
key newCollider;
|
|
|
|
// CODE follows
|
|
|
|
// For rezzing in
|
|
RezIn()
|
|
{
|
|
things = [1,5,"@stop"];
|
|
things += [2,5,"@stop"];
|
|
things += [3,10,"@stop"];
|
|
Speak();
|
|
}
|
|
|
|
DoIt()
|
|
{
|
|
things = myStory;
|
|
isRunning = TRUE;
|
|
llVolumeDetect(FALSE);
|
|
llSleep(0.1);
|
|
}
|
|
|
|
|
|
list things ;
|
|
list myStory;
|
|
|
|
// DEBUG(string) will chat a string or display it as hovertext if debug == TRUE
|
|
DEBUG(string str) {
|
|
if (debug && ! LSLEditor)
|
|
llOwnerSay( str); // Send the owner debug info
|
|
if (debug && LSLEditor)
|
|
llSay(0, str); // Send to the Console in LSLEDitor
|
|
if (iTitleText) {
|
|
llSetText(str,<1.0,1.0,1.0>,1.0); // show hovertext
|
|
|
|
}
|
|
}
|
|
|
|
list ListStridedUpdate(list dest, list src, integer start, integer end, integer stride) {
|
|
return llListReplaceList(dest, src, start * stride, ((end + 1) * stride) - 1 );
|
|
}
|
|
|
|
spawn(integer who,string msg) {
|
|
DEBUG("spawning " + msg);
|
|
list data = llParseString2List(msg, ["|"], []);
|
|
//DEBUG((string) data);
|
|
list npcName = llParseString2List(llList2String(data,0), [" "], []);
|
|
npcKey = osNpcCreate(llList2String(npcName, 0), llList2String(npcName, 1), llList2Vector(data,1), llList2String(data, 2), NPCOptions);
|
|
npcList=ListStridedUpdate(npcList,[npcKey,npcName],who,who,2);
|
|
//DEBUG("npcList " + (string) npcList);
|
|
}
|
|
|
|
delete(integer who) {
|
|
DEBUG(llList2String(npcList,who*2+1) + " is removed");
|
|
osNpcRemove (llList2Key(npcList,who*2));
|
|
npcList=ListStridedUpdate(npcList,[(key) "00000000-0000-0000-0000-000000000000","removed NPC"],who,who,2);
|
|
//llSay(0, "npcList " + (string) npcList);
|
|
}
|
|
|
|
say(integer who,string msg) {
|
|
DEBUG(llList2String(npcList,who*2+1) + " says " + msg);
|
|
osNpcSay(llList2Key(npcList,who*2),0, msg);
|
|
}
|
|
|
|
sit(integer who,string msg) {
|
|
DEBUG(llList2String(npcList,who*2+1) + " sits on " + (string) msg);
|
|
// look up object named "msg" in myObjects, get key from myObjectsKeys, make "who" NPC sit on it
|
|
osNpcSit(llList2Key(npcList,who*2), llList2Key(myObjectKeys,llListFindList(myObjects,[msg])), OS_NPC_SIT_NOW);
|
|
}
|
|
|
|
touchit(integer who,string msg) {
|
|
DEBUG(llList2String(npcList,who*2+1) + " touches " + (string) msg);
|
|
// look up object named "msg" in myObjects, get key from myObjectsKeys, make "who" NPC touch it
|
|
osNpcTouch(llList2Key(npcList,who*2),llList2Key(myObjectKeys,llListFindList(myObjects,[msg])), LINK_THIS);
|
|
}
|
|
|
|
stand(integer who) {
|
|
DEBUG(llList2String(npcList,who*2+1) + " stands");
|
|
osNpcStand(llList2Key(npcList,who*2));
|
|
list anToStop=llGetAnimationList(llList2Key(npcList,who*2));
|
|
integer stop=llGetListLength(anToStop);
|
|
while (--stop>-1) { osAvatarStopAnimation(llList2Key(npcList,who*2),llList2Key(anToStop,stop)); }
|
|
osNpcPlayAnimation(llList2Key(npcList,who*2),"Stand");
|
|
}
|
|
|
|
walk(integer who,vector pos) {
|
|
DEBUG(llList2String(npcList,who*2+1) + " walks to " + (string) pos);
|
|
osNpcMoveToTarget(llList2Key(npcList,who*2),pos,OS_NPC_NO_FLY);
|
|
osNpcPlayAnimation(llList2Key(npcList,who*2),"Walk");
|
|
}
|
|
|
|
animate(integer who, string ani) {
|
|
DEBUG(llList2String(npcList,who*2+1) + " plays " + (string) ani);
|
|
osNpcPlayAnimation(llList2Key(npcList,who*2),ani);
|
|
}
|
|
|
|
appearance(integer who, string app) {
|
|
DEBUG(llList2String(npcList,who*2+1) + " changes appearance to " + (string) app);
|
|
osNpcLoadAppearance(llList2Key(npcList,who*2),app);
|
|
}
|
|
|
|
rotate(integer who,float rot) {
|
|
DEBUG(llList2String(npcList,who*2+1) + " rotates " + (string) rot);
|
|
osNpcSetRot(llList2Key(npcList,who*2),llEuler2Rot(<0,0,rot> * DEG_TO_RAD));
|
|
}
|
|
|
|
// <<<< Added by Aine
|
|
dumpKeys() {
|
|
llMessageLinked(LINK_THIS,0,"NPC_UUID_LIST",llDumpList2String(llList2ListStrided(npcList,0,-1,2),"|"));
|
|
}
|
|
slaveap(string com) {
|
|
list parse=llParseString2List(com,["|"],[]);
|
|
llMessageLinked(LINK_THIS,llList2Integer(parse,0),"NPC_CHANGE_APPEARANCE",llList2String(parse,1));
|
|
}
|
|
// <<<< End added by Aine
|
|
Speak() {
|
|
|
|
integer npc = llList2Integer(things,0);
|
|
float time = llList2Float(things,1);
|
|
string msg = llList2String(things,2);
|
|
DEBUG("npc:" + (string) npc + " time:" + (string) time + " Msg:" + msg);
|
|
if (npc) {
|
|
things = llDeleteSubList(things,0,2);
|
|
// llMessageLinked(npc,0, msg,""); // <<<<< Aine: I don't see any need for having this here still since this script is handling it
|
|
list data = llParseString2List(msg, ["="], []);
|
|
npcAction = llToLower(llStringTrim(llList2String(data, 0), STRING_TRIM));
|
|
DEBUG("Action:" + npcAction);
|
|
npcParams = llStringTrim(llList2String(data, 1), STRING_TRIM);
|
|
DEBUG("Params:" + npcParams);
|
|
if (npcAction == "@say") {
|
|
say(npc,npcParams);
|
|
} else if (npcAction == "@spawn") {
|
|
spawn(npc,npcParams);
|
|
} else if (npcAction == "@delete") {
|
|
delete(npc);
|
|
} else if (npcAction == "@sit") {
|
|
sit(npc,npcParams);
|
|
} else if (npcAction == "@touch") {
|
|
touchit(npc,npcParams);
|
|
} else if (npcAction == "@stand") {
|
|
stand(npc);
|
|
} else if (npcAction == "@walk") {
|
|
walk(npc,npcParams);
|
|
} else if (npcAction == "@animate") {
|
|
animate(npc,npcParams);
|
|
} else if (npcAction == "@appearance") {
|
|
appearance(npc,npcParams);
|
|
} else if (npcAction == "@rotate") {
|
|
rotate(npc,npcParams);
|
|
// >>>> Added by Aine
|
|
} else if (npcAction == "@dumpkeys") {
|
|
dumpKeys();
|
|
} else if (npcAction == "@slaveap") {
|
|
slaveap(npcParams);
|
|
} // >>>> end of add by Aine
|
|
if (time > 0) {
|
|
llSetTimerEvent(time);
|
|
} else {
|
|
llOwnerSay("Whooops, time = 0!");
|
|
DEBUG("npc:" + (string) npc + " time:" + (string) time + " Msg:" + msg);
|
|
llSetTimerEvent(0);
|
|
}
|
|
} else {
|
|
DEBUG("Done");
|
|
isRunning = FALSE;
|
|
Reset();
|
|
llSetTimerEvent(0);
|
|
if (SENSE)
|
|
llSensorRepeat("","",AGENT,RANGE,PI,RATE);
|
|
}
|
|
}
|
|
|
|
Reset()
|
|
{
|
|
llSetStatus(STATUS_PHANTOM, FALSE); // Rev 2
|
|
llVolumeDetect(FALSE);
|
|
llSleep(0.1);
|
|
llVolumeDetect(TRUE);
|
|
}
|
|
|
|
default
|
|
{
|
|
state_entry()
|
|
{
|
|
DEBUG("entering state default");
|
|
|
|
if (! myObjectsInitialized){
|
|
state initial;
|
|
}
|
|
|
|
llSetText("",<1,1,1>,1.0);
|
|
Reset();
|
|
llSetTimerEvent(0);
|
|
if (SENSE)
|
|
llSensorRepeat("","",AGENT,RANGE,PI,RATE);
|
|
}
|
|
|
|
sensor(integer n) {
|
|
DEBUG("Bumped");
|
|
if (! osIsNpc(llDetectedKey(0))) {
|
|
DEBUG("Sensed avatar");
|
|
llSensorRemove();
|
|
RezIn();
|
|
}
|
|
}
|
|
|
|
timer()
|
|
{
|
|
Speak();
|
|
}
|
|
|
|
collision_start(integer n) {
|
|
newCollider = llKey2Name(llDetectedKey(0));
|
|
DEBUG("Collided with " + llKey2Name(llDetectedKey(0)));
|
|
|
|
if (! osIsNpc(llDetectedKey(0)))
|
|
{
|
|
if ( newCollider != oldCollider) {
|
|
DoIt();
|
|
oldCollider = llKey2Name(llDetectedKey(0));
|
|
Speak();
|
|
}
|
|
}
|
|
}
|
|
|
|
collision_end(integer num_detected) {
|
|
DEBUG(llDetectedName(0) + " has stopped colliding with me!");
|
|
if (! isRunning && (newCollider == oldCollider) ) {
|
|
oldCollider = NULL_KEY;
|
|
}
|
|
}
|
|
|
|
touch_start(integer n) {
|
|
DEBUG("Touched by " + llKey2Name(llDetectedKey(0)));
|
|
if (isRunning) {
|
|
//insert restart code here
|
|
}
|
|
if (! osIsNpc(llDetectedKey(0))) {
|
|
DoIt();
|
|
Speak();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
on_rez(integer p)
|
|
{
|
|
llResetScript();
|
|
}
|
|
|
|
changed(integer what)
|
|
{
|
|
if (what & CHANGED_REGION_START|CHANGED_INVENTORY)
|
|
{
|
|
llResetScript();
|
|
}
|
|
}
|
|
}
|
|
|
|
state initial
|
|
{
|
|
state_entry()
|
|
{
|
|
DEBUG("entering state initial");
|
|
myStory = [];
|
|
list myStoryLines = llParseString2List(osGetNotecard(myStoryNotecard), ["\n"], []);
|
|
DEBUG("text = " + (string) myStoryLines);
|
|
DEBUG("lines = " + (string) llGetListLength(myStoryLines));
|
|
integer i;
|
|
for (i=0; i<llGetListLength(myStoryLines); i++) {
|
|
string myStoryLine = llList2String(myStoryLines,i);
|
|
integer npc = (integer) llGetSubString(myStoryLine,0,llSubStringIndex(myStoryLine,","));
|
|
string rest = llGetSubString(myStoryLine,llSubStringIndex(myStoryLine,",")+1,-1);
|
|
float time = llGetSubString(rest,0,llSubStringIndex(rest,","));
|
|
string msg = llGetSubString(rest,llSubStringIndex(rest,",")+2,-2);
|
|
//now parse for sit and touch events to find the names of the objects needed
|
|
list data = llParseString2List(msg, ["="], []);
|
|
npcAction = llToLower(llStringTrim(llList2String(data, 0), STRING_TRIM));
|
|
DEBUG("Action:" + npcAction);
|
|
npcParams = llStringTrim(llList2String(data, 1), STRING_TRIM);
|
|
DEBUG("Params:" + npcParams);
|
|
if (npcAction == "@sit") {
|
|
myObjects += npcParams;
|
|
} else if (npcAction == "@touch") {
|
|
myObjects += npcParams;
|
|
}
|
|
DEBUG("npc: " + (string) npc + " time: " + (string) time + " msg: " + msg);
|
|
myStory += [npc, time, msg];
|
|
}
|
|
DEBUG("Story read, initializing objects: " + llDumpList2String(myObjects," "));
|
|
|
|
if (llGetListLength(myObjects) == 0) {
|
|
DEBUG("myObjects is empty upon entering state initial");
|
|
myObjectsInitialized = TRUE;
|
|
state default;
|
|
}
|
|
llSensor(llList2String(myObjects,0),"",ACTIVE|PASSIVE,96,PI);
|
|
DEBUG("initially looking for: " + llList2String(myObjects,0));
|
|
}
|
|
|
|
//sensor is used to scan surrounding for named prims
|
|
sensor(integer num) {
|
|
myObjectKeys = myObjectKeys + llDetectedKey(0);
|
|
DEBUG("myObjectKeys: " + llDumpList2String(myObjectKeys," "));
|
|
|
|
myObjects = llDeleteSubList(myObjects,0,0);
|
|
if (llGetListLength(myObjects) == 0) {
|
|
myObjectsInitialized = TRUE;
|
|
state default;
|
|
} else {
|
|
llSensor(llList2String(myObjects,0),"",ACTIVE|PASSIVE,96,PI);
|
|
DEBUG("now looking for: " + llList2String(myObjects,0));
|
|
}
|
|
}
|
|
no_sensor(){
|
|
DEBUG ("no target prim located");
|
|
}
|
|
}
|