Cheeky Geek with Qie Niangao, Resident Geek

Cheeky Geek with Qie Niangao 

Another Experience-experience: "Don't (mouse-)look now..."

Experiences have been around for a while now, and it's even been a few months since the most recent Experience-related feature, forced-sit control. Besides its ability to act as a "Bird Spike for Avatars" (as I described in the November 2016 column), its more intended usage is to direct Experience participants to take a particular seat immediately.

When an avatar sits -- forced or not -- its viewer can be set to Mouselook -- but only when they're first seated, after which they can press the Escape key to get back to normal cam control mode. (This is all independent of what an implementation of RLV may enforce in a third-party viewer.) Parts of some SL adventures may be more fun in Mouselook, so Experience designers may want to enforce Mouselook for those places, preventing distant cam views. That seems a natural fit: A Mouselook-forcing object that momentarily force-sits avatars any time they try to escape Mouselook.

So first, the scripts that do the deed, starting with one that goes inside an object that zips to the avatar's location, forces them to sit on it thereby putting them in Mouselook, and then disappears:

________________
/*
 2017 Qie Niangao released into Public Domain.

 This must be compiled to use an Experience enabled on the parcel.

 Goes in an object rezzed by "Rez one for each AV of interest"

 Negotiates the target agent,
 gets Experience perms from that agent
    (for real participants might be more verbose)
 then force-sits them just long enough to force mouselook

 BUT !!!
 IF USED WITH FLYING AVATARS THE FORCED SIT CAUSES THEM
 TO STOP FLYING IMMEDIATELY WHEN UNSEATED. See:
 https://jira.secondlife.com/browse/BUG-40984
 (A buoyancy kludge-around wouldn't actually restore flight mode)
 (so the rezzer script just skips already-seated AVs)

 ALSO
 IF USED WITH SEATED AVATARS THEY'LL BE UNSEATED
 UNLESS WHAT THEY WERE SITTING ON IS ALSO EXPERIENCE-SCRIPTED,
 THERE'S NO WAY TO AUTOMATICALLY RE-SEAT THEM AS BEFORE
 (so the rezzer script just skips already-seated AVs)

 Not sure why, but when trying to match the target AV's position and rotation,
 don't achieve intended effect with a rotated seat prim and zero_rot sit target
 but works fine with a zero_rot seat and rotated sit target.

*/

integer rezzedByObject = FALSE;

default
{
    state_entry()
    {
        llForceMouselook(TRUE);
        llSetLinkPrimitiveParamsFast(LINK_THIS, [PRIM_SCRIPTED_SIT_ONLY, TRUE]);
        llSetClickAction(CLICK_ACTION_NONE);
        llSetCameraAtOffset(ZERO_VECTOR);   // Clear these, just to be safe
        llSetCameraEyeOffset(ZERO_VECTOR);
        llSetStatus(STATUS_PHANTOM, TRUE);
    }
    on_rez(integer rezChan)
    {
        if (rezChan)
        {
            key rezzer = llList2Key(llGetObjectDetails(llGetKey(), [OBJECT_REZZER_KEY]), 0);
            llListen(rezChan, "", rezzer, "");
            llRegionSayTo(rezzer, rezChan, "1"); // tell rezzer I'm ready and listening
            llSetLinkPrimitiveParamsFast(LINK_THIS, [PRIM_TEMP_ON_REZ, TRUE]);
            rezzedByObject = TRUE;
        }
    }
    listen(integer chan, string name, key id, string text)
    {
        llRequestExperiencePermissions((key)text, "");
    }
    experience_permissions(key agent)
    {
        list targetDetails = llGetObjectDetails(agent, [OBJECT_POS, OBJECT_ROT]);
        llSetRegionPos(llList2Vector(targetDetails, 0));
        // This the accepted approximation for best-fit of sit target to av's current pos+rot
        // from GetSitTarget in http://wiki.secondlife.com/wiki/LlSitTarget
        // (simplified by identical positions of av and prim)
        rotation tr = llList2Rot(targetDetails, 1);
        vector tp = llGetAgentSize(agent);
        float fAdjust = ((((0.008906 * tp.z) + -0.049831) * tp.z) + 0.088967) * tp.z;
        llSitTarget(llRot2Up(tr) * fAdjust - <0.0, 0.0, 0.4>, tr);
        llSleep(0.1);   // else observers see part of the llSetRegionPos translation
        llSitOnLink(agent, LINK_THIS);
    }
    changed(integer change)
    {
        if (CHANGED_LINK & change)
        {
            key av = llAvatarOnSitTarget();
            if (av)
            {
                llStopAnimation("sit");
                llSleep(0.2);   // maybe smoother dismount?
                llUnSit(av);    // Mouselook doesn't "take" if unseated before this event is posted
                if (rezzedByObject)
                    llDie();
            }
        }
    }
}
________________
Note that this script needs to be compiled with whatever Experience will be enabled on the land and by the participants.
              
A copy of the object that holds that script goes into another, rezzer object that holds the following script:
________________
/*
 2017 Qie Niangao released into Public Domain.

 Rez one copy of the inventory object for each avatar currently "of interest"

 In this case, we're interested in everybody not in Mouselook on this land

*/

float SAMPLING_INTERVAL = 10.0; // longest a newly "of interest" agent can go undetected
integer OWNER_ONLY = TRUE;   // ONLY FOR TESTING. If TRUE, applies to owner only
integer LAND_SPECIFICITY =
    AGENT_LIST_PARCEL   // just this parcel
//    AGENT_LIST_PARCEL_OWNER   // owned by my owner (must deed object to match group-owned land)
//    AGENT_LIST_REGION // whole sim
    ;

integer of_interest(key agent)
{
    if (!( llGetAgentInfo(agent) &
        ( AGENT_MOUSELOOK  // not already in mouselook
        | AGENT_IN_AIR  // save the fliers (see above)
        | AGENT_SITTING // not already sitting on something (optional)
        )))
        if (-1 == llListFindList(rezzingForAgents, [agent]))    // Not yet rezzing for this agent
            if (!OWNER_ONLY || (llGetOwner() == agent))
                return(TRUE);
    return(FALSE);
}



string invObjectName;   // Could set a string here if there's more than one Object in Inventory
list rezzingForAgents;
integer rezChan;

default
{
    state_entry()
    {
        rezChan = -1000000000 - (integer)llFrand(1000000000.0);
        llListen(rezChan, "", NULL_KEY, "");   // Leave it open. If nobody's around, what's to lag?
        if ("" == invObjectName)
            invObjectName = llGetInventoryName(INVENTORY_OBJECT, 0);
        llSetTimerEvent(SAMPLING_INTERVAL);
    }
    changed (integer change)
    {
        if (CHANGED_INVENTORY & change)
            llResetScript();
    }
    timer()
    {
        list newAgents = llGetAgentList(LAND_SPECIFICITY, []);
        integer agentIdx = llGetListLength(newAgents);
        integer now = llGetUnixTime();
        while (0 <= --agentIdx)
        {
            key agent = llList2Key(newAgents, agentIdx);
            if (of_interest(agent))
            {
                rezzingForAgents += [now, (string)agent, NULL_KEY];
                llRezAtRoot(invObjectName, llGetPos(), ZERO_VECTOR, ZERO_ROTATION, rezChan);
            }
        }
        // tidy-up stranded record (shouldn't happen normally, but maybe temp-rez GC at sim restart)
        if (rezzingForAgents)   // non-zero length
            if (now > (llList2Integer(rezzingForAgents, 0) + SAMPLING_INTERVAL))
                rezzingForAgents = llDeleteSubList(rezzingForAgents, 0, 2);
    }
    object_rez(key obj)
    {
        // Associate this object with the first still-pending agent
        integer firstNull = llListFindList(rezzingForAgents, [NULL_KEY]);
        rezzingForAgents = llListReplaceList(rezzingForAgents, [obj], firstNull, firstNull);
    }
    listen(integer channel, string name, key obj, string text)
    {
        integer thisRezzing = llListFindList(rezzingForAgents, [obj]);
        if (-1 != thisRezzing)  // heard from one we rezzed
            llRegionSayTo(obj, rezChan,
                llList2String(rezzingForAgents, thisRezzing-1)); // send associated agent's key
    }
}
________________

Note that to actually impose this on anybody else, you'll need to switch the OWNER_ONLY variable to FALSE. Also, this approach can't prevent momentary glimpses of normal cam control, where the maximum length of those glimpses is set by the SAMPLING_INTERVAL variable.

Now, is any of this practical? Perhaps not, as the script comments reveal, and that may be of more general interest than the scripts themselves.

First, you can't sit on two places at once, and there's no way to return an already-seated avatar to that seat after being force-sat onto one of these. (Well, unless that prior seating location is also scripted to force-sit avatars.) So the script skips over anybody already seated.

Second, there's no llFly() function (yet?), so the script skips avatars that are already in the air. Otherwise, while it would indeed force them into Mouselook, it would also shoot them out of the sky.

Third, the forced sitting and unsitting (controlled by the first script above) isn't perfectly, seamlessly smooth, despite measures to minimize the disruption. Among those is the use of an arcane best-fit approximation of where to put a sit-target to match the avatar's current location (part of many furniture-animation setup scripts). Also, a very brief delay was added after the seat moves into position because if the avatar is seated immediately, an observer will see the events out-of-order, with the avatar momentarily seated at the rezzing location then zipping back to where they were before being unseated. In contrast, the seated agent's own viewer shows the events in order -- although it's also popping into Mouselook at the time.

For all the limitations, the scripts do show one perhaps unintended way to use Experience-based forced-sit, and more generally a reusable way to rez an object for each avatar of interest.

No comments:

Post a Comment

CALENDAR



Having an Event?

Please contact Kinn (kinnaird.fiachra@yahoo.com) to have your Bay City event added to the Calendar!

Park Plaza Hotel

Park Plaza Hotel
Please visit our Sponsor!

Luxury Living in Bay City


Park Plaza residents enjoy beautiful views, fine dining, and a roof top patio with hot tub; all this near route 66, and within walking distance to Hairy Hippo Fun Land and aquarium. Rates start at 55L a week for studios, and 185L a week for full size apartments. Free furnishings available. Contact Roc Plutonium for more information!

Follow by Email

Follow the Bay City Alliance on Twitter