// Davan Camus, 2006 June // Control Unit for Cavorite Flotation Devices integer COMMAND_CHANNEL = 17; integer gListenerNumber = 0; integer gTicks = 0; integer gDebug = 0; float TIMER_RATE = .3; // easy on the servers integer gHeldTicks = 0; // for control accel integer gHeldKeys = 0; vector gHere; // updated on every timer tick vector gVel; // velocity in global coords rotation gRot; // our current direction key gOwner; // updated on timer tick integer gOwnerInfo; // bitfields from llGetAgentInfo // User Mode Settings integer gUWalkingGravity = 100; // gravity we apply when walking integer gUJumpingGravity = 20; // gravity we apply when jumping float gUInertia = 0; // applies drag opposing your vel integer gUSafetyAnchor = 1; // activate safety anchor if falling! (if we're in controlled-flight mode) integer gUBasicMode = 1; // if set, then what you get is "fly" on any up-key, and movement and brake no modes. integer gUBasicSpeed = 3; integer gJumpingTicks = 0; // set a count when you start a jump, to return to walking gravity. // d([this, that, the other]); prints debug stuff // (I find it easier to type the [list,notation] than all // that tedious casting-to-string and adding stuff d(list debugList) { if(!gDebug) return; else { string s = list2String(debugList); llOwnerSay(s); } } // Take a list, type-and-cast appropriately, and concat it // all into a string. useful for debug displays. string list2String(list x) { string s = ""; integer len = llGetListLength(x); integer i; for(i = 0; i < len; i++) { integer t = llGetListEntryType(x,i); if(t == TYPE_STRING) s += llList2String(x,i); else if(t == TYPE_INTEGER) s += (string)llList2Integer(x,i); else if(t == TYPE_FLOAT) s += (string)llList2Float(x,i); else if(t == TYPE_FLOAT) s += (string)llList2Float(x,i); else if(t == TYPE_VECTOR) s += (string)llList2Vector(x,i); else s += "(item " + (string)i + ")"; } return s; } integer gAnimationPermission = 0; integer gControlPermission = 0; // corresponds to enable and disable commands. integer gOldAttachPoint = 0; perms(integer onoff) { if(onoff == 0) { llReleaseControls(); gControlPermission = 0; return; } integer attachPoint = llGetAttached(); // are we worn? if((attachPoint != gOldAttachPoint) || !gControlPermission) { // attach point changed, release all... gOldAttachPoint = attachPoint; gAnimationPermission = 0; gControlPermission = 0; llReleaseControls(); // and try now. if(gOldAttachPoint) // !0 means worn { llSleep(1); llRequestPermissions(gOwner,PERMISSION_TAKE_CONTROLS | PERMISSION_TRIGGER_ANIMATION); // control. } } } init() { MOTION_KEYS = CONTROL_FWD | CONTROL_BACK | CONTROL_UP | CONTROL_DOWN | CONTROL_LEFT | CONTROL_RIGHT; gOwner = llGetOwner(); // reset the chat listener llListenRemove(gListenerNumber); gListenerNumber = llListen(COMMAND_CHANNEL,"",gOwner,""); // only you d(["init: listening on ",COMMAND_CHANNEL]); gTicks = 0; llSetTimerEvent(TIMER_RATE); // easy on the servers link("debug 0"); //link("brake"); // go through shield-fadeout perms(0); perms(1); gState = 0; // when permissions come back, we'll set ground mode. } doHelp(key avKey) { // give the first notecard... string helpCard = llGetInventoryName(INVENTORY_NOTECARD,0); llGiveInventory(avKey,helpCard); } doEnable(integer enable) { if(enable) { setState(S_GROUNDING); perms(1); setState(S_GROUND); } else { setState(S_GROUNDING); perms(0); setState(S_GROUND); link("pingSteady 0 0 0"); } } doCommand(key avKey,string message) { if(message == "") { doDialog(); return; } // If the command starts with a dot, then bring up the dialog after. integer dialogAfter = 0; if(llGetSubString(message,0,0) == ".") { dialogAfter = 1; message = llGetSubString(message,1,-1); } list messageL = llParseString2List(message,[" "],[]); string cmd = llToLower(llList2String(messageL,0)); integer arg1 = llList2Integer(messageL,1); string arg1S = llToLower(llList2String(messageL,1)); if(arg1S == "" || arg1S == "on") arg1 = 1; integer handledMessage = 1; // innocent til guilty if(cmd == "debug") { gDebug = arg1; d(["memory: ",llGetFreeMemory()]); //link(message); // pass debug message down to linked fellas } else if(cmd == "control") { setState(S_GROUNDING); perms(arg1); setState(S_GROUND); } else if(cmd == "enable") { doEnable(1); } else if(cmd == "disable") { doEnable(0); } else if(cmd == "init") init(); else if(cmd == "help...") doHelp(avKey); else if (cmd == "brake") brake(1); else if(cmd == "float") { setState(S_LAUNCHING); doControlsImpulse(0,0); // to settle } else if(cmd == "fall") { setState(S_GROUNDING); doControlsImpulse(0,0); // to settle } else if(cmd == "link") { // send link message direct (for debug) or parse as commands string linkMessage = llGetSubString(message,5,-1); link(linkMessage); return; } else if(cmd == "gravity") setJumpingGravity(arg1); else if(cmd == "safety") gUSafetyAnchor = arg1; else if(cmd == "basic") { gUBasicMode = 1; setState(S_GROUNDING); } else if(cmd == "speed") { gUBasicSpeed = arg1; } else if(cmd == "advanced") { gUBasicMode = 0; setState(S_GROUNDING); } else handledMessage = 0; // show yellow for good or orange for bad command if(handledMessage) link("pingShort 1 1 0 .3 .1"); else link("pingShort 1 .4 0 .2 .1"); if(dialogAfter) doDialog(); } string bs(integer b) { if(b) return "On"; return "Off"; } doDialog() { key av = llGetOwner(); string s = "Personal Cavorite Flotation Device 0.98" + "\nMechanism by Davan Camus" + "\nDesign by Horg Neurocam" + "\n"; if(!gControlPermission) s += "\nStatus: Disabled"; else { s += "\nFloating Mode: "; if(gUBasicMode) s += "Basic" + "\nSpeed: " + (string)gUBasicSpeed; else s += "Advanced"; s += "\nEtherStop(tm) Safety Anchor: " + bs(gUSafetyAnchor); if(!gUBasicMode) s+= "\nJumping Gravity: " + (string)gUJumpingGravity + "%"; } list options; if(!gControlPermission) options = ["Help...","Enable"]; else { options = [ "Help...","Disable"]; if(!gUBasicMode) options += ["Safety On","Safety Off","Brake", "Gravity 15","Gravity 25","Gravity 50"]; else options += ["Speed 1","Speed 2","Speed 3"]; options += ["Float","Fall"]; if(gUBasicMode) options += ["Advanced"]; else options += ["Basic"]; } llDialog(av,s,options,COMMAND_CHANNEL); } link(string linkMessage) { if(linkMessage != "") { d(["link message sending: ",linkMessage]); llMessageLinked(LINK_SET,COMMAND_CHANNEL,linkMessage,""); } } brake(integer parachute) { d(["brake ",parachute]); // use llGetVel, not gVel, so we can brake several times iteratively llApplyImpulse(-1 * llGetVel() * llGetMass(),0); // brake! // and always with braking brakeFX(parachute); } // this is the Safety Ether Anchor visual effect brakeFX(integer parachute) { float velM = llVecMag(gVel); if(velM < .5) return; // no expensive particles on such a mild brake. // if not moving fast, but braking, do a safety circle if(parachute) { llParticleSystem( [ PSYS_SRC_TEXTURE,"radio_circle", PSYS_SRC_BURST_RADIUS,0.0, PSYS_PART_MAX_AGE,.4, // fast blink! PSYS_SRC_BURST_RATE,5.0, PSYS_SRC_BURST_PART_COUNT,2, PSYS_PART_START_SCALE,<3,3,3>, PSYS_PART_END_SCALE,<3.8,3.8,3.8>, PSYS_PART_FLAGS, PSYS_PART_INTERP_COLOR_MASK | PSYS_PART_EMISSIVE_MASK, PSYS_SRC_PATTERN, PSYS_SRC_PATTERN_EXPLODE, PSYS_PART_START_COLOR, <.6,1,.6>, PSYS_PART_START_ALPHA,0.9, PSYS_PART_END_ALPHA,0.0, PSYS_SRC_PATTERN,PSYS_SRC_PATTERN_ANGLE_CONE, PSYS_SRC_ANGLE_BEGIN,0.0, PSYS_SRC_ANGLE_END,PI, PSYS_SRC_BURST_SPEED_MIN,0., PSYS_SRC_BURST_SPEED_MAX,0., PSYS_SRC_MAX_AGE,1.0 // 1-second total burst ]); llSleep(.4); return; } float startAlpha = velM / 50.0; // top speed 50-ish makes dim alpha llParticleSystem( [ PSYS_SRC_BURST_RADIUS,2.0, PSYS_PART_MAX_AGE,.6, PSYS_SRC_BURST_RATE,.1, PSYS_SRC_BURST_PART_COUNT,400, PSYS_PART_FLAGS, PSYS_PART_FOLLOW_VELOCITY_MASK | PSYS_PART_INTERP_COLOR_MASK | PSYS_PART_EMISSIVE_MASK, PSYS_SRC_PATTERN, PSYS_SRC_PATTERN_EXPLODE, PSYS_PART_START_SCALE, <1,.1,0>, PSYS_PART_START_COLOR, <0.4,0.9,0.4>, PSYS_PART_END_COLOR, <0.0,1.0,0.0>, PSYS_PART_START_ALPHA,startAlpha, PSYS_PART_END_ALPHA,0.0, PSYS_SRC_PATTERN,PSYS_SRC_PATTERN_ANGLE_CONE, PSYS_SRC_ANGLE_BEGIN,0.0, PSYS_SRC_ANGLE_END,PI, PSYS_SRC_BURST_SPEED_MIN,0.01, PSYS_SRC_BURST_SPEED_MAX,0.01, PSYS_SRC_MAX_AGE,1.4 // 1-second total burst ]); llSleep(.1); } integer gFlyMode = 0; enterFlyMode() { if(gFlyMode) return; // for bad coders gFlyMode = 1; setGOA("hover"); if(gControlPermission) { // llTakeControls( // CONTROL_UP // | CONTROL_DOWN // | CONTROL_FWD // | CONTROL_BACK, //// TRUE,FALSE // if we do NOT pass through, we dont get mystery f/b impulses // TRUE,TRUE // if we pass through, then Flying still works... // ); setGravity(0); // hover // brake(0); // link("pingSteady 0 1 0"); } } // ground mode -- you can walk around and stuff! // differences (really) are: buoyancy, and pass-through of fwd-bwd arrows. enterGroundMode() { gFlyMode = 0; setGOA(""); if(gControlPermission) { // llTakeControls( // CONTROL_UP // | CONTROL_DOWN, // TRUE,TRUE); // pass through, so jumping works in buoyant mode // brake(0); setGravity(gUWalkingGravity); // fall! link("pingSteady 0 0 .2"); } } integer gGravity = 100; // 0 to 100 // set the objects actual gravity (buoyancy) setGravity(integer gravity) { if(gravity != gGravity) { gGravity = gravity; float buoyancy = 1.0 - ((float)gGravity) / 100.0; if(buoyancy < 0.0) buoyancy = 0.0; else if(buoyancy > 2.0) buoyancy = 2.0; llSetBuoyancy(buoyancy); } } // set the user-setting for gravity in Ground mode setWalkingGravity(integer g) { gUWalkingGravity = g; if(gState & (S_GROUNDING | S_GROUND)) setGravity(g); } // set the user-setting for gravity in Ground mode setJumpingGravity(integer g) { gUJumpingGravity = g; } // state variable for managing buttony transitions integer gState = 0; integer gStateTicks = 0; float gStateTime; setState(integer newState) { if(newState != gState) { gStateTicks = 0; gStateTime = llGetTime(); // to know how long we've been in a state d(["state from ",gState," to ",newState]); gState = newState; vector pingColor = <0,0,0>; if(gState == S_READY_FLY) pingColor = <0,1,0>; else if(gState == S_MOVING) pingColor = <0,.7,1>; else if(gState == S_BRAKING) pingColor = <.4,.9,.4>; else if(gState == S_LAUNCHING) { pingColor = <.4,1,.4>; enterFlyMode(); } else if(gState == S_BASIC_FLY) { pingColor = <.6,1,.1>; enterFlyMode(); } else if(gState == S_BASIC_FLY_READY) // flying, but waiting for action { pingColor = <.6,.6,.1>; enterFlyMode(); } else if(gState == S_GROUNDING) { pingColor = <0,0,.3>; enterGroundMode(); } else if(gState == S_GROUND) { pingColor = <0,0,.2>; } else if(gState == S_JUMPING) { if(gUJumpingGravity < 80) pingColor = <.9,.4,0>; setGravity(gUJumpingGravity); } else // anything else (braking?) pingColor = <0,.4,0>; link("pingSteady " + (string)pingColor.x + " " + (string)pingColor.y + " " + (string)pingColor.z); link("state " + (string)gState); } } integer S_READY_FLY = 0x0001; // state "ready" responds to keys for impulse, braking integer S_BRAKING = 0x0002; // after braking, keys do nothing until you release both, just keep braking. after some seconds, turn off cavor. integer S_V_BRAKING = 0x0004; // keep braking til all keys released, or enter S_BRAKING integer S_H_BRAKING = 0x0008; // keep braking til all keys released integer S_TURN_OFF = 0x0010; integer S_MOVING = 0x0020; // a lot like "ready", but nice to catch the transitions... integer S_GROUNDING = 0x0040; // fly mode disabled, waiting for keys to be released so we dont just fly again! integer S_GROUND = 0x0080; // walking on the ground! but will float if for pg-up key (if enabled) integer S_LAUNCHING = 0x0100; // in process of starting to fly integer S_JUMPING = 0x0200; integer S_BASIC_FLY = 0x0400; integer S_BASIC_FLY_READY = 0x0800; integer MOTION_KEYS = 0x0033; // any combination of up down fwd back left right. float gVelThreshold = .8; // how fast you must be going for the counterbrake to activate float gFallThreshold = 25.; // falling rate threshold float gTeleThreshold = 50.; // if we move this or more, assume it was a teleporter: no brake! doControlsImpulse(integer held, integer change) { gHeldKeys = held; if(gDebug && change) { string a = llGetAnimation(gOwner); // aw why not. d([ "state: ",gState, ", key: ",held, ", fly: ",gFlyMode, ", G: ",gGravity ]); } // whole different world for basic controls // short circuit here... if(gUBasicMode) { doBasicControls(held); return; } // fast return for no-keys-pressed. This is to minimize lag // when you're not doing anything at all! (though you may // be coasting at a good clip, of course) if((held & MOTION_KEYS) == 0) { if(gState == S_GROUNDING) gState = S_GROUND; else if(!(gState & (S_GROUND | S_JUMPING))) // most other states go to "ready" with no keys. setState(S_READY_FLY); return; } integer cu = held & CONTROL_UP; integer cd = held & CONTROL_DOWN; if(gState & (S_GROUND | S_JUMPING)) // enter fly mode by pressing up&down,or just up if you're falling { if(cu) { //if(cd || (gVel.z < -5.0) || ((gState == S_JUMPING) && (llGetTime() - gStateTime > 2)) ) // or "up" after jumping 2 seconds. if(cd || (gVel.z < -5.0) || ((gState == S_JUMPING) ) ) // or "up" after jumping 2 seconds. setState(S_LAUNCHING); else { setState(S_JUMPING); // we are jumping! set lowG mode. normal av does the jump. float seconds; if(gUJumpingGravity <= 10) seconds = 22; else if(gUJumpingGravity <= 20) seconds = 13; else if(gUJumpingGravity <= 40) seconds = 10; else if(gUJumpingGravity <= 60) seconds = 7; else seconds = 3; gJumpingTicks = (integer)(seconds / TIMER_RATE); } } else return; } integer cf = held & CONTROL_FWD; integer cb = held & CONTROL_BACK; vector impulse = ZERO_VECTOR; // will be scaled by getMass at the end... gVel = llGetVel(); if(gState == S_READY_FLY || gState == S_MOVING) { if((cu && cd) || (cf && cb)) setState(S_BRAKING); // full braking... else { // normal motion, treat each key for direction... if(cf || cb) // forward and back combos { gRot = llGetRot(); vector gVelLocal = gVel / gRot; float xVel = gVelLocal.x; // forward/backward velocity, relative to av if(xVel > -gVelThreshold && xVel < gVelThreshold) xVel = 0.0; if( (cf && (xVel < 0.0)) // fwd while going backwards? brake! || (cb && (xVel > 0)) ) // bwd while going forwards? brake! setState(S_H_BRAKING); else { if(gState == S_READY_FLY) impulse = <.4,0,0>; else impulse = <1,0,0>; setState(S_MOVING); impulse = impulse * gRot; if(cb) impulse = -impulse; } } // normal motion, treat each key for direction... if(cu || cd) // up and down combos { float zVel = gVel.z; if(zVel > -gVelThreshold && zVel < gVelThreshold) zVel = 0.0; if( (cu && (zVel < 0.0)) // up while going down? brake! || (cd && (zVel > 0)) ) // down while going up? brake! setState(S_V_BRAKING); else { // if(cu && !gFlyMode) // enterFlyMode(); float zImp; if(gState == S_READY_FLY) zImp =.4; else zImp = 1.; setState(S_MOVING); if(cd) zImp = -zImp; // extra MMF if avatar is in avFly mode... flying tends to anchor a little if(cu && gAgentFlying) zImp *= 4; impulse.z += zImp; } } } } // fall through and do these, so the S_READY_FLY portions can set brake state for // immediate action upon. if(gState == S_H_BRAKING) { if((cu && cd) || (cf && cb)) setState(S_BRAKING); // full braking can kick in any time, transitioning from partial braking... brakeFX(0); vector hBrakeImp = gVel; hBrakeImp.z = 0; impulse -= hBrakeImp; } else if(gState == S_V_BRAKING) { if((cu && cd) || (cf && cb)) setState(S_BRAKING); // full braking can kick in any time, transitioning from partial braking... brakeFX(0); vector vBrakeImp = gVel; vBrakeImp.x = 0; vBrakeImp.y = 0; impulse -= vBrakeImp; } else if(gState == S_LAUNCHING) // any keys down during launching... keep braking impulse -= gVel; else if(gState == S_BRAKING) { brakeFX(0); impulse -= gVel; // d(["braking: ",gBrakeCount++]); // TODO: if we hold this long enough: exit fly mode. float brakingTime = llGetTime() - gStateTime; if(brakingTime > 2) { setState(S_GROUNDING); } else if(brakingTime > 1) link("pingSteady 0 0 1"); // warning to drop indicator } else if(gState == S_GROUNDING) // must wait for all keys to rise now... { if(!(held & 0x33)) setState(S_GROUND); } // and see what we should do, from impulse & buoyancy set (or not) above. if(impulse != ZERO_VECTOR) { impulse *= llGetMass(); d(["normal impulse: ",impulse]); llApplyImpulse(impulse,0); } } float gBasicMovingTime = 0; doBasicControls(integer held) { integer cu = held & CONTROL_UP; if(gState != S_BASIC_FLY) { if(cu) setState(S_BASIC_FLY); else setState(S_GROUND); // only two states in basic mode! } if(gState == S_BASIC_FLY) { if(!(held & MOTION_KEYS)) { // quick case for flying but no keys: brake! brake(0); gBasicMovingTime = llGetTime(); return; } integer cd = held & CONTROL_DOWN; integer cf = held & CONTROL_FWD; integer cb = held & CONTROL_BACK; integer cl = held & CONTROL_LEFT; integer cr = held & CONTROL_RIGHT; // either pair grounds at once. if((cu && cd) || (cf && cb)) { setState(S_GROUNDING); return; } vector impulse = <0,0,0>; if(cu) impulse += <0,0,1>; if(cd) impulse += <0,0,-1>; if(cf) impulse += <1,0,0>; if(cb) impulse += <-1,0,0>; if(cl) impulse += <0,1,0>; if(cr) impulse += <0,-1,0>; // scale accelerating impulse float t = llGetTime() - gBasicMovingTime; if(t > gUBasicSpeed) t = gUBasicSpeed; impulse *= t; gRot = llGetRot(); impulse *= gRot; impulse *= llGetMass(); d(["basic impulse: ",impulse]); llApplyImpulse(impulse,0); } } integer gBrakeCount = 0; // poll for typing, and activate antenna if so typingStuff() { string message = ""; integer info = llGetAgentInfo(gOwner); integer isTyping = info & AGENT_TYPING; if(isTyping) message = "pingShort 1 0 0 " + (string)(TIMER_RATE - 0.2)+ " 0.2"; link(message); } // poll for traditional-flying mode integer gAgentFlying = 0; integer gAgentSitting = 0; avFlyStuff() { integer isFlying = gOwnerInfo & AGENT_FLYING; integer isSitting = gOwnerInfo & AGENT_SITTING; // release controls automatically if you sit. take em when done. if(isSitting && !gAgentSitting) { doEnable(0); } else if(gAgentSitting && !isSitting) { doEnable(1); } gAgentSitting = isSitting; if(isFlying && !gAgentFlying) { setState(S_BASIC_FLY); doControlsImpulse(0,0); // to settle } else if(gAgentFlying && !isFlying) { setState(S_GROUNDING); doControlsImpulse(0,0); // to settle } gAgentFlying = isFlying; // adjust gravity based on flying -- if(gState == S_BASIC_FLY) { // if you are buoyant and AGENT_FLYING you tend to drift to 150, gr if(gAgentFlying && gHere.z < 150) setGravity(100); else setGravity(0); } } vector gRecentPosition = <0,0,0>; // set a certain animation, but only if we have perms, and its not the current string gOA = ""; setGOA(string oa) { if(gAnimationPermission)// && gOA != oa) { if(gOA != oa) { if(gOA != "") llStopAnimation(gOA); } gOA = oa; if(gOA != "") llStartAnimation(gOA); } } // Do some misc logic and maybe override animation. animationStuff() { if(!gAnimationPermission) return; // dont try if not allowed. // string a = llGetAnimation(gOwner); // if(a == "Falling Down") // aka "the flounder" // { if(gFlyMode) { string oa = "hover"; if(gVel.z > 2) oa = "hover_up"; else if(gVel.z < -2) oa = "hover_down"; llStopAnimation("falldown"); // { // list a = llGetAnimationList(gOwner); // integer n = llGetListLength(a); // while(n > 0) // { // n--; // key a1 = llList2Key(a,n); // if(a1 != NULL_KEY) // { // llStopAnimation(a1); // } // } // } // transition between our overrides... setGOA(oa); // } // else // { // // really ARE falling! // setGOA("falldown"); // } } } // falling stuff -- if not in control mode, quick! stop the fall, and // take over. applySafetyAnchor() { if(!gFlyMode && !gAgentSitting) // we can fall if we want to { if(gVel.z < -gFallThreshold && gVel.z > -gTeleThreshold) // tune me { d(["safety anchor activated, z = ",gVel.z]); brake(1); if(gUBasicMode) setState(S_BASIC_FLY); else setState(S_LAUNCHING); doControlsImpulse(0,0); // after a catch, give 1 pass to remedy the states, assuming no keys. llSleep(.3); brake(1); } } } default { on_rez(integer n) { } state_entry() { init(); } touch_start(integer total_number) { key avKey = llDetectedKey(0); // touch to redo perms if(avKey == gOwner) doDialog(); else doHelp(avKey); } listen(integer channel,string name,key id,string message) { d(["Heard \"",message,"\" on channel ",channel," from ",name]); doCommand(id,message); } attach(key avKey) { init(); perms(1); } // just got control! run_time_permissions(integer perm) { if(perm & PERMISSION_TAKE_CONTROLS) { d(["run_time_permissions: ",perm]); gControlPermission = 1; // take control exactly once, for all occasions. llTakeControls(MOTION_KEYS, TRUE,TRUE // if we pass through, then Flying still works... ); // if(gFlyMode) // setState(S_LAUNCHING); // else setState(S_GROUNDING); setState(S_GROUND); // doControlsImpulse(0,0); // to settle } if(perm & PERMISSION_TRIGGER_ANIMATION) gAnimationPermission = 1; } control(key id, integer held, integer change) { doControlsImpulse(held,change); } timer() { // rock-steady in basic mode if(gUBasicMode && !gHeldKeys && (gState == S_BASIC_FLY)) brake(0); if(gJumpingTicks > 0) { gJumpingTicks--; if(gJumpingTicks <= 0 && !gFlyMode) { setState(S_GROUND); setGravity(gUWalkingGravity); } } gVel = llGetVel(); gHere = llGetPos(); gOwnerInfo = llGetAgentInfo(gOwner); gTicks++; // d(["gTicks: ",gTicks]); typingStuff(); avFlyStuff(); animationStuff(); if(gUSafetyAnchor && gControlPermission) // dont anchor when "disabled" applySafetyAnchor(); } } // end of file