Jump to content
testgenord1

llSetKeyframedMotion forward and back

Recommended Posts

Hi!

I've got a script that makes a prim move on top of a different prim when clicked on,
and then automatically move back to its initial position.

I would like the prim to remain on top of that prim
and only move back when clicked on a second time.

The movement has to be split into two parts (the back-movement part seems to go into the timer below),
and then a touch_start event has to be built into it,
but I haven't been able to achieve this.

Maybe you have some ideas?

Here is the part of the script containing the movement:

state move
{
    state_entry()
    {
        rotation spin = NormRot(llEuler2Rot(<0.0, 0.0, 180.0> * DEG_TO_RAD));

        llSetTimerEvent(0.1);

        llSetKeyframedMotion([
            <0.0, 0.0, 1.0> * llGetRot(), ZERO_ROTATION, 0.5,
            pos - llGetPos() + ZERO_VECTOR * llGetRot(), ZERO_ROTATION, 1.0,
            ZERO_VECTOR, spin, 0.5,
            ZERO_VECTOR, ZERO_ROTATION, 1.0,
            llGetPos() - pos + ZERO_VECTOR * llGetRot(), ZERO_ROTATION, 1.0,
            -<0.0, 0.0, 1.0> * llGetRot(), ZERO_ROTATION, 0.5,
            ZERO_VECTOR, spin, 0.5
        ], [KFM_MODE, KFM_FORWARD]);       
    }

    timer()
    {
        llSetTimerEvent(1.0);

        llShout(canal, word + "," + (string)llGetPos() + "," + (string)llGetRot());

        if (count > 5)
        {
            llShout(canal, trad);
            llResetScript();
        }

        llSetText("lang: " + lang + "\nword: " + word + "\ntrad: " + trad, <1.0, 1.0, 1.0>, 1.0);
        ++count;
    }
}

Thank you very much in advance.

I'm adding the complete 2 scripts for context:
1. The script of the prim doing the movement:

// KFM Vocabulary Memory Game Reciever v0.2 by djphil (CC-BY-NC-SA 4.0)

integer canal = -123654789;
integer tempo = 5;
integer echo;
integer count;
integer power;
string lang;
string word;
string trad;
string prim;
vector pos;
rotation rot;

rotation NormRot(rotation Q)
{
    float MagQ = llSqrt(Q.x*Q.x + Q.y*Q.y +Q.z*Q.z + Q.s*Q.s);
    return <Q.x/MagQ, Q.y/MagQ, Q.z/MagQ, Q.s/MagQ>;
}

default
{
    state_entry()
    {
        list buffer = llCSV2List(llGetObjectName());

        if (llGetListLength(buffer) == 2)
        {
            lang = llStringTrim(llList2String(buffer, 0), STRING_TRIM);
            prim = llStringTrim(llList2String(buffer, 1), STRING_TRIM);
        }

        else
        {
            llOwnerSay("Error: Bad prim name detected");
            return;
        }

        buffer = llCSV2List(llGetObjectDesc());

        if (llGetListLength(buffer) == 2)
        {
            word = llStringTrim(llList2String(buffer, 0), STRING_TRIM);
            trad = llStringTrim(llList2String(buffer, 1), STRING_TRIM);
        }
       
        else
        {
            llOwnerSay("Error: Bad prim desc detected");
            return;
        }

        llSetText("lang: " + lang + "\nword: " + word + "\nstate: ready", <1.0, 1.0, 1.0>, 1.0);

        llSetLinkPrimitiveParamsFast(LINK_THIS, [
            PRIM_PHYSICS_SHAPE_TYPE,
            PRIM_PHYSICS_SHAPE_CONVEX
        ]);
    }

    touch_start(integer number)
    {
        llListenRemove(echo);
        count = 0;

        if (power =! power)
        {
            echo = llListen(canal, prim + "," + lang, NULL_KEY, "");
            llSetTimerEvent(0.1);
        }

        else
        {
            llSetTimerEvent(0.0);
        }

        llSetText("lang: " + lang + "\nword: " + word + "\nstate: ready", <1.0, 1.0, 1.0>, 1.0);
    }
   
    listen(integer channel, string name, key id, string message)
    {
        list buffer = llCSV2List(name);

        if (llGetListLength(buffer) == 2)
        {
            name = llList2String(buffer, 0);

            if (name == prim)
            {
                buffer = llCSV2List(message);
               
                if (llGetListLength(buffer) == 3)
                {
                    message = llList2String(buffer, 0);
                    pos = llList2Vector(buffer, 1);
                    rot = llList2Rot(buffer, 1);
   
                    if (message == trad)
                    {
                        llListenRemove(echo);
                        power =! power;
                        llShout(canal, message);
                        state move;
                    }
                }   
            }
        }
    }

    timer()
    {
        llSetTimerEvent(1.0);

        llShout(canal, word + "," + (string)llGetPos() + "," + (string)llGetRot());

        if (count > tempo)
        {
            llResetScript();
        }

        llSetText("lang: " + lang + "\nword: " + word + "\ntimeleft: " + (tempo - count), <1.0, 1.0, 1.0>, 1.0);
        ++count;
    }
}

state move
{
    state_entry()
    {
        rotation spin = NormRot(llEuler2Rot(<0.0, 0.0, 180.0> * DEG_TO_RAD));

        llSetTimerEvent(0.1);

        llSetKeyframedMotion([
            <0.0, 0.0, 1.0> * llGetRot(), ZERO_ROTATION, 0.5,
            pos - llGetPos() + ZERO_VECTOR * llGetRot(), ZERO_ROTATION, 1.0,
            ZERO_VECTOR, spin, 0.5,
            ZERO_VECTOR, ZERO_ROTATION, 1.0,
            llGetPos() - pos + ZERO_VECTOR * llGetRot(), ZERO_ROTATION, 1.0,
            -<0.0, 0.0, 1.0> * llGetRot(), ZERO_ROTATION, 0.5,
            ZERO_VECTOR, spin, 0.5
        ], [KFM_MODE, KFM_FORWARD]);       
    }

    timer()
    {
        llSetTimerEvent(1.0);

        llShout(canal, word + "," + (string)llGetPos() + "," + (string)llGetRot());

        if (count > 5)
        {
            llShout(canal, trad);
            llResetScript();
        }

        llSetText("lang: " + lang + "\nword: " + word + "\ntrad: " + trad, <1.0, 1.0, 1.0>, 1.0);
        ++count;
    }
}

2. ... and the script of the other prim:

// KFM Vocabulary Memory Game Emitter v0.2 by djphil (CC-BY-NC-SA 4.0)

integer canal = -123654789;
integer tempo = 5;
integer echo;
integer count;
integer power;
string lang;
string word;
string trad;
string prim;

default
{
    state_entry()
    {
        list buffer = llCSV2List(llGetObjectName());

        if (llGetListLength(buffer) == 2)
        {
            lang = llStringTrim(llList2String(buffer, 0), STRING_TRIM);
            prim = llStringTrim(llList2String(buffer, 1), STRING_TRIM);
        }

        else
        {
            llOwnerSay("Error: Bad prim name detected");
            return;
        }

        buffer = llCSV2List(llGetObjectDesc());

        if (llGetListLength(buffer) == 2)
        {
            word = llStringTrim(llList2String(buffer, 0), STRING_TRIM);
            trad = llStringTrim(llList2String(buffer, 1), STRING_TRIM);
        }
       
        else
        {
            llOwnerSay("Error: Bad prim desc detected");
            return;
        }

        llSetText("lang: " + lang + "\nword: " + word + "\nstate: ready", <1.0, 1.0, 1.0>, 1.0);
    }
   
    touch_start(integer number)
    {
        llListenRemove(echo);
        count = 0;

        if (power =! power)
        {
            echo = llListen(canal, prim + "," + lang, NULL_KEY, "");
            llSetTimerEvent(0.1);
        }

        else
        {
            llSetTimerEvent(0.0);
        }

        llSetText("lang: " + lang + "\nword: " + word + "\nstate: ready", <1.0, 1.0, 1.0>, 1.0);
    }

    listen(integer channel, string name, key id, string message)
    {
        list buffer = llCSV2List(name);

        if (llGetListLength(buffer) == 2)
        {
            name = llList2String(buffer, 0);

            if (name == prim)
            {
                if (message == word)
                {
                    llSetTimerEvent(0.0);
                    state move;
                }
            }
        }
    }

    timer()
    {
        llSetTimerEvent(1.0);
        llShout(canal, word + "," +(string)llGetPos() + "," + (string)llGetRot());

        if (count > tempo)
        {
            llResetScript();
        }

        llSetText("lang: " + lang + "\nword: " + word + "\ntimeleft: " + (tempo - count), <1.0, 1.0, 1.0>, 1.0);
        ++count;
    }
}

state move
{
    state_entry()
    {
        llListenRemove(echo);
        echo = llListen(canal, prim + "," + lang, NULL_KEY, "");
        llSetText("lang: " + lang + "\nword: " + word + "\ntrad: " + trad, <1.0, 1.0, 1.0>, 1.0);
    }

    listen(integer channel, string name, key id, string message)
    {
        list buffer = llCSV2List(name);

        if (llGetListLength(buffer) == 2)
        {
            name = llList2String(buffer, 0);
           
            if (name == prim)
            {
                if (message == word)
                {
                    llResetScript();
                }
            }
        }
    }
}

 

Edited by testgenord1

Share this post


Link to post
Share on other sites

Thank you for your reply.
The scripts do work.
I forgot to mention that you have to give a particular name and description to both prims.

For example:
prim 1:
name: English,German
description: Vocabulary,Vokabular

prim 2:
name: German,English
description: Vokabular,Vocabulary

Share this post


Link to post
Share on other sites
5 minutes ago, testgenord1 said:

Thank you for your reply.
The scripts do work.
I forgot to mention that you have to give a particular name and description to both prims.

For example:
prim 1:
name: English,German
description: Vocabulary,Vokabular

prim 2:
name: German,English
description: Vokabular,Vocabulary

Does not even complie. I tried. The same error is in both scripts. So you can not have compiled it inworld.

Share this post


Link to post
Share on other sites
21 minutes ago, testgenord1 said:

The scripts do work.

Both of the last two scripts in your first post have a syntax error on lines 120 and 95, where you're trying to add "(tempo - count)" to a string, which would require a typecast. Minor issue, but still an issue for anybody who would want to test your scripts.

As for the first snippet of code, you don't need to re-set llSetTimerEvent within the timer event. A timer will always repeat forever until you set the timer to 0, which stops it. (But this doesn't break anything.)

6 hours ago, testgenord1 said:

I would like the prim to remain on top of that prim and only move back when clicked on a second time.

For this, you can implement a simple "toggle" on touch. But since you use separate states in your script and didn't include the code where you change to a new state, I can't offer any specific advice for your script. But a toggle (basically a switch between 0 and 1) could be implemented like this:

//  FALSE or 0 = down,  TRUE or 1 = up
integer up = FALSE;

default
{
    touch_start(integer n)
    {
        up = !up; // This changes 0 to 1 (or 1 to 0), and replaces the old value.

        if(up == TRUE)
        {
            llOwnerSay("KFM move up");
        }
        else
        {
            llOwnerSay("KFM move down");
        }
    }
}
Edited by Wulfie Reanimator
Better example.

Share this post


Link to post
Share on other sites

Yeah, I'm not sure where the posted version of the script came from, but it took some (too much) debugging to get it working at all.  In addition to the syntax error, there's an assignment that won't work because llList2Vector doesn't cast a string to a vector, so instead we need:

pos = (vector)llList2String(buffer, 1);

With that out of the way, Wulfie identifies the main complexity here: the way it gets out of the move state is with llResetScript() which will lose all state, making it impossible to know that the next click means "go back" and even if it just set state default, state_entry would do a bunch of stuff we don't want. So the simplest way to proceed is to replace the move state's timer() event handler with a touch_end() handler.*

The next problem is that your KFM command list correctly uses llGetPos() which evaluates when the command is constructed, not during the execution of the command. So if you separate the "moving back" part of the command list from the "moving to" part, it'll use the moved-to position, having forgotten whence it began. You could create a global variable that stores that position. I decided to instead create a global variable to store the "moving to" command list, and then use KFM_REVERSE to restore the position. (That also saves the need to recalculate the spin variable or make it, too, global.) Like this:

state move
{
    state_entry()
    {
        llOwnerSay(llGetScriptName()+" in move state with pos = "+(string)pos);
        rotation spin = NormRot(llEuler2Rot(<0.0, 0.0, 180.0> * DEG_TO_RAD));
        KFMcommand = [
            <0.0, 0.0, 1.0> * llGetRot(), ZERO_ROTATION, 0.5,
            pos - llGetPos(), ZERO_ROTATION, 1.0,
            ZERO_VECTOR, spin, 0.5
            ];
        llSetKeyframedMotion(KFMcommand, [KFM_MODE, KFM_FORWARD]);
    }
    touch_end(integer num_detected)
    {
        llShout(canal, word + "," + (string)llGetPos() + "," + (string)llGetRot());
        llSetKeyframedMotion(KFMcommand, [KFM_MODE, KFM_REVERSE]);
        llShout(canal, trad);
        llSetText("lang: " + lang + "\nword: " + word + "\ntrad: " + trad, <1.0, 1.0, 1.0>, 1.0);
        llResetScript();
    }
}

Oh, also, I didn't spend time figuring out whether all the llShout()ing is necessary, nor the llSetText(), I just mindlessly migrated it in from the timer() event. And finally, the original KFM command added 

ZERO_VECTOR * llGetRot()

 to the positions. No idea what that was supposed to do.

_________
* That reminds me: You're using touch_start in a script with states; better to get in the practice of using touch_end for such scripts to avoid some really obscure misbehaviors.

  • Like 1

Share this post


Link to post
Share on other sites

Hi again.
Yes, this is where I got the script from.
Sorry about the messy script.
So, thank you all very much for taking your time and your patience.
Thanks to your help I got the scripts to work.

At the top I added the global variable

list KFMcommand;

I hope this was correct. At least it worked after that.

For other users possibly interested in this,
I'm adding my final version below.
There is probably still room for improvement, so feel free to comment if you like.
Here is the new version of the script of the moving part:
(The other one I basically left as it was.)

// KFM Vocabulary Memory Game Reciever v0.2 by djphil (CC-BY-NC-SA 4.0) and Qie Niangao
//https://community.secondlife.com/forums/topic/434279-llsetkeyframedmotion-forward-and-back/

list KFMcommand;//changed original script here.

integer canal = -123654789;
integer tempo = 5;
integer echo;
integer count;
integer power;
string lang;
string word;
string trad;
string prim;
vector pos;
rotation rot;

rotation NormRot(rotation Q)
{
    float MagQ = llSqrt(Q.x*Q.x + Q.y*Q.y +Q.z*Q.z + Q.s*Q.s);
    return <Q.x/MagQ, Q.y/MagQ, Q.z/MagQ, Q.s/MagQ>;
}

default
{
    state_entry()
    {
        list buffer = llCSV2List(llGetObjectName());

        if (llGetListLength(buffer) == 2)
        {
            lang = llStringTrim(llList2String(buffer, 0), STRING_TRIM);
            prim = llStringTrim(llList2String(buffer, 1), STRING_TRIM);
        }

        else
        {
            llOwnerSay("Error: Bad prim name detected");
            return;
        }

        buffer = llCSV2List(llGetObjectDesc());

        if (llGetListLength(buffer) == 2)
        {
            word = llStringTrim(llList2String(buffer, 0), STRING_TRIM);
            trad = llStringTrim(llList2String(buffer, 1), STRING_TRIM);
        }
       
        else
        {
            llOwnerSay("Error: Bad prim desc detected");
            return;
        }

        llSetText("state: ready", <1.0, 1.0, 1.0>, 1.0);

        llSetLinkPrimitiveParamsFast(LINK_THIS, [
            PRIM_PHYSICS_SHAPE_TYPE,
            PRIM_PHYSICS_SHAPE_CONVEX
        ]);
    }

    touch_start(integer number)
    {
        llListenRemove(echo);
        count = 0;

        if (power =! power)
        {
            echo = llListen(canal, prim + "," + lang, NULL_KEY, "");
            llSetTimerEvent(0.1);
        }

        else
        {
            llSetTimerEvent(0.0);
        }

        llSetText("state: ready", <1.0, 1.0, 1.0>, 1.0);
    }
   
    listen(integer channel, string name, key id, string message)
    {
        list buffer = llCSV2List(name);

        if (llGetListLength(buffer) == 2)
        {
            name = llList2String(buffer, 0);

            if (name == prim)
            {
                buffer = llCSV2List(message);
               
                if (llGetListLength(buffer) == 3)
                {
                    message = llList2String(buffer, 0);
                    pos = (vector)llList2String(buffer, 1);//changed original script here.
                    rot = llList2Rot(buffer, 1);
   
                    if (message == trad)
                    {
                        llListenRemove(echo);
                        power =! power;
                        llShout(canal, message);
                        state move;
                    }
                }   
            }
        }
    }

    timer()
    {
        llSetTimerEvent(1.0);

        llShout(canal, word + "," + (string)llGetPos() + "," + (string)llGetRot());

        if (count > tempo)
        {
            llResetScript();
        }

        llSetText("time left: " + (tempo - count), <1.0, 1.0, 1.0>, 1.0);
        ++count;
    }
}

state move//changed original script here.
{
    state_entry()
    {
        llOwnerSay(llGetScriptName()+" in move state with pos = "+(string)pos);
        rotation spin = NormRot(llEuler2Rot(<90.0, 0.0, 0.0> * DEG_TO_RAD));
        KFMcommand = [
            <0.0, 0.0, 1.0> * llGetRot(), ZERO_ROTATION, 0.5,
            pos - llGetPos(), ZERO_ROTATION, 1.0,
            ZERO_VECTOR, spin, 0.5
            ];
        llSetKeyframedMotion(KFMcommand, [KFM_MODE, KFM_FORWARD]);
        llSleep(5);
    }
    touch_end(integer num_detected)
    {
        llShout(canal, word + "," + (string)llGetPos() + "," + (string)llGetRot());
        llSetKeyframedMotion(KFMcommand, [KFM_MODE, KFM_REVERSE]);
        llShout(canal, trad);
        //llSetText("lang: " + lang + "\nword: " + word + "\ntrad: " + trad, <1.0, 1.0, 1.0>, 1.0);
        llResetScript();
}
}

 

Edited by testgenord1
reference of Qie Niangao

Share this post


Link to post
Share on other sites
17 minutes ago, testgenord1 said:

llSetText("time left: " + (tempo - count), <1.0, 1.0, 1.0>, 1.0);

So if this is working, I guess we can surmise that OpenSim is less strict than Second Life about typecasting -- which may also explain how the pos = llList2Vector(...) assignment wasn't a problem until I tried to get it to work in Second Life.

And yes, that's correct, the KFMcommand list needs to be declared at the top of the script like that.

[ETA: I should have removed the line:

 llOwnerSay(llGetScriptName()+" in move state with pos = "+(string)pos);

which was just my debugging code when pos wasn't getting assigned correctly because of that typecasting problem.]

Edited by Qie Niangao
  • Like 1

Share this post


Link to post
Share on other sites

Just to be respectfull of others you really should put a reference in the script to those that converted it for SL usage. Specially Qie Niangao.

This is incorrect. It throws up an error on compile.

llSetText("time left: " + (tempo - count), <1.0, 1.0, 1.0>, 1.0);
  
This is correct.
  
integer a = tempo - count;
llSetText("time left: " + (string)a , <1.0, 1.0, 1.0>, 1.0);

 

Edited by steph Arnott

Share this post


Link to post
Share on other sites
3 hours ago, Qie Niangao said:

So if this is working, I guess we can surmise that OpenSim is less strict than Second Life about typecasting

Not sure that is a good thing. Seems a very sledge hammer and pocket watch approach.

Edited by steph Arnott

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×