In an outdoor photo, you can see the
weather. In a film, you can FEEL the weather. That's because the
dynamic medium represents how the weather CHANGES environmental cues:
Clouds move in and the sky darkens, lighting changes, the sea gets
heavy.
What if we could give our visitors that
feeling in an intentional, _directed_ series of environmental
settings? Immerse them in a little program of weather dynamics?
Below is a sample Experience script
that demonstrates features of the new Environment Enhancement Project
(EEP), to provide just that kind of weather-changing experience,
individually for each visitor. As configured, it applies a sequence
of four Library environmental "sky" assets to make the
weather seem progressively more threatening over about a minute and a
half after arrival. (In real use, you might choose longer transition
intervals to stretch this out more realistically. And of course you
can use other EEP environments in any sequence - perhaps a cheery
brightening of the sky as clouds lift.)
/*
Released into Public Domain, 2020,
Qie Niangao for Bay City Post
Give visitors an enhanced
experience with a SERIES of Environments
So clouds can build, sky can
darken, seas roil more over time - or vice versa
(sample Environments in
ENV_STAGES below are super primitive, just to demo the concept)
This basic script uses simple
passage of time to trigger next stage
Make sure parcel (at least) has the
script's Experience enabled
*/
//== USER SPECIFIED CONSTANTS
=======================
integer AGENT_SCOPE =
AGENT_LIST_PARCEL; // or AGENT_LIST_PARCEL_OWNER or
AGENT_LIST_REGION
// but if they're on land without
the Experience, there will be errors
list ENV_STAGES = // sequence of
(SAMPLE) Environment UUIDs and durations (in seconds)
[
"d15d3fb0-7e14-4af8-203e-33b85346f3f1", 20 // "Neutral"
Library sky
,
"cd880f86-53c7-1a91-532c-2797f517a35f", 20 // "Daytime
shadows" Library sky
,
"07899451-b3d8-9f5c-563b-14b89eda481d", 30 // "Dusty"
Library sky
,
"2a000d12-e692-da2a-9e08-0cbda70bc0bf", 30 //
"PaperSnow" Library sky
]; // These use unnaturally
short durations for demo & testing)
float AVATAR_SCAN_INTERVAL = 10.0; //
in seconds.
// Not a real sensor, but still
some list manipulation, so keep it as long as tolerable
// A new arrival may wait UP TO
this interval, so just half this time on average
integer MAX_IGNORED_AGENTS = 20; //
after which, forget the oldest (prevent memory leak)
integer MAX_STARTING_AGENTS = 3; //
most we'll wait to reply to permissions request (prevent leak)
//== SCRIPT-MANAGED VARIABLES
=======================
list tAvStages; // avatar UUIDs in
scope, index of their Env.stage, preceded by time to move through
stage
list ignoredAgents; // don't spam these
with more exp perm requests
key parcelID;
list agentsStarting;
debugOut(string outStr) {
// llOwnerSay(outStr);
}
default
{
state_entry()
{
if ([] ==
llGetExperienceDetails(NULL_KEY))
{
llWhisper(DEBUG_CHANNEL,
"Script \""+llGetScriptName()+"\" is not
associated with an experience. Exiting.");
return;
}
// trivial, impossible to match
sensor, just set a repeating no_sensor interval:
llSensorRepeat("NO SUCH
NAME", llGetKey(), AGENT, 0.01, 0.01, AVATAR_SCAN_INTERVAL);
parcelID =
llList2Key(llGetParcelDetails(llGetPos(), [PARCEL_DETAILS_ID]), 0);
}
on_rez(integer start_param)
{
llResetScript();
}
no_sensor()
{
list newAgents =
llGetAgentList(AGENT_SCOPE, []);
// any already-staged agents
need removing?
integer oldAgentIdx =
llGetListLength(tAvStages) - 2;
while (0 <= oldAgentIdx)
{
key oldAgent =
llList2Key(tAvStages, oldAgentIdx);
if (-1 ==
llListFindList(newAgents, [oldAgent]))
tAvStages =
llDeleteSubList(tAvStages, oldAgentIdx-1, oldAgentIdx+1);
oldAgentIdx -= 3;
}
// any newly-arrived agents
need request to add?
integer newAgentIdx =
llGetListLength(newAgents);
while (0 <= --newAgentIdx)
{
key newAgent =
llList2Key(newAgents, newAgentIdx);
integer scopeIdx =
llListFindList(tAvStages, [newAgent]);
if (-1 == scopeIdx) //
agent not yet in list of agents with managed environments
if ((-1 ==
llListFindList(ignoredAgents, [newAgent])) // agent didn't deny us
in past
&& (-1 ==
llListFindList(agentsStarting, [newAgent]))) // and not already
being invited
{
if
(!llAgentInExperience(newAgent)) // agent new to this Experience:
greet & invite
llRegionSayTo(newAgent, 0, "Greetings
"+llGetDisplayName(newAgent)
+" and
welcome! You're being invited to an Experience, \""
+
llList2String(llGetExperienceDetails(NULL_KEY), 0) // script's
Experience name
+"\"
that demonstrates dynamic personal Environment settings."
+" We hope
you'll agree to participate."
+" (If you
don't grant permissions, this script will try not to ask again for a
while.)");
agentsStarting +=
newAgent;
if
(MAX_STARTING_AGENTS < llGetListLength(agentsStarting))
agentsStarting
= llList2List(agentsStarting, 1, -1);
llRequestExperiencePermissions(newAgent, "");
}
}
}
experience_permissions_denied(key
agentId, integer reason)
{
integer agentStartingIdx =
llListFindList(agentsStarting, [agentId]);
if (-1 != agentStartingIdx)
{
if (XP_ERROR_NOT_PERMITTED
== reason)
{
if (-1 ==
llListFindList(ignoredAgents, [agentId]))
{
ignoredAgents +=
agentId; // don't spam after first request
if
(llGetListLength(ignoredAgents) >= MAX_IGNORED_AGENTS)
ignoredAgents =
llList2List(ignoredAgents, 1, MAX_IGNORED_AGENTS);
}
}
agentsStarting =
llDeleteSubList(agentsStarting, agentStartingIdx, agentStartingIdx);
}
else
if (XP_ERROR_NOT_PERMITTED_LAND
!= reason)
// ignore _LAND perm error
because it's raised every time agent moves to non-XP parcel
llWhisper(DEBUG_CHANNEL,
"Experience permissions denied, reason #"+(string)reason
+":
"+llGetExperienceErrorMessage(reason));
}
experience_permissions(key agentId)
{
integer agentStartingIdx =
llListFindList(agentsStarting, [agentId]);
if (-1 != agentStartingIdx)
{
tAvStages =
[llGetUnixTime(), agentId, 0] + tAvStages;
llSetTimerEvent(0.1);
agentsStarting =
llDeleteSubList(agentsStarting, agentStartingIdx, agentStartingIdx);
return;
}
// else handle timer-triggered
requests (process the tAvStages queue)
integer now = llGetUnixTime();
if (now >=
llList2Integer(tAvStages, 0))
{
key agent =
llList2Key(tAvStages, 1);
integer envIdx =
llList2Integer(tAvStages, 2);
key env =
llList2Key(ENV_STAGES, envIdx);
integer transition =
llList2Integer(ENV_STAGES, envIdx + 1);
debugOut("process env
"+(string)env+" for agent "+llKey2Name(agent)
+"\n\t with
tAvStages = "+llDumpList2String(tAvStages, " | "));
integer envErr =
llReplaceAgentEnvironment(agent, (float)transition, env);
if (0 > envErr)
llWhisper(DEBUG_CHANNEL, "Error in llReplaceAgentEnvironment :
"+(string)envErr);
envIdx += 2; // advance
agent to wait for next stage
integer nextTime = now +
transition;
if (envIdx >=
llGetListLength(ENV_STAGES))
nextTime = 2147483647;
// Final env stage should last for rest of stay (MAXINT)
tAvStages =
llListReplaceList(tAvStages, [nextTime, agent, envIdx], 0, 2);
}
tAvStages =
llListSort(tAvStages, 3, TRUE); // lazy
float timeTo =
(float)(llList2Integer(tAvStages, 0) - now);
if (0.0 >= timeTo) timeTo =
0.1;
llSetTimerEvent(timeTo);
}
timer()
{
llSetTimerEvent(0); // set
again in exp_perms event
if (llGetListLength(tAvStages))
// any Avs left?
llRequestExperiencePermissions(llList2Key(tAvStages, 1), "");
}
}
Note that Experience permissions are
required by llReplaceAgentEnvironment, the EEP function that makes
this possible, so the script will need to be compiled to an
Experience that's enabled on the land where it runs. And visitors
have to grant the permissions requested when the script invites them
to participate.
At the moment, EEP is only visible in
the Linden viewer, but soon it'll be supported in popular third party
viewers too. Although the script sequences the effect individually
for each viewer, it manipulates what's called the "Shared
Environment" -- so it can be overridden by users who specify
their own environment or time-of-day.
Passage of time needn't be the only
thing to trigger a change of EEP environment. Should one particular
room or area have different lighting? Such a script would track where
visitors roam and update their environments accordingly. Or maybe
trigger environments by level in an in-world game, or in response to
some scripted role-play interaction.
Another possible extension: _Sound_ is
powerfully evocative of weather: wind, thunder, bird song, waves,
etc: llPlaySound() in a HUD is heard only by the wearer;
llAttachToAvatarTemp() could transparently attach a sound-emitting
HUD to experience participants.
Reporter Qie Niangao
200504