mirror of
https://github.com/RuthAndRoth/Ruth2.git
synced 2026-08-14 00:57:58 +00:00
* Define the actual values for the BoM texture constants used * Add a HUD CONFIG notecard with the UUIDs used in the Halcyon test uploads Signed-off-by: Serie Sumei <seriesumei@avimail.org>
1110 lines
40 KiB
Plaintext
1110 lines
40 KiB
Plaintext
// ru2_hud_control.lsl - Roth2 v3 HUD Controller
|
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
// Copyright 2017 Shin Ingen
|
|
// Copyright 2020 Serie Sumei
|
|
|
|
// v2.0 12Apr2020 <seriesumei@avimail.org> - Based on ru2_hud_control.lsl v3.2
|
|
// v2.1 12Apr2020 <seriesumei@avimail.org> - New simpler alpha HUD
|
|
// v2.2 13May2020 <seriesumei@avimail.org> - Rework skin panel
|
|
// v2.3 21Jun2020 <seriesumei@avimail.org> - Updates to compile on OpenSimulator 0.8
|
|
// v2.4 22Aug2020 <seriesumei@avimail.org> - Update for Ruth2 v4
|
|
// v2.5 26Sep2020 <seriesumei@avimail.org> - Set the IMG_USE_BAKED_* constants
|
|
|
|
// This is a heavily modified version of Shin's RC3 HUD scripts for alpha
|
|
// and skin selections.
|
|
|
|
// Older OpenSimulator builds may not have the PRIM_ALPHA_MODE_*
|
|
// constants defined. If you get a compiler error that these are not defined
|
|
// uncomment the following lines:
|
|
// integer PRIM_ALPHA_MODE_NONE = 0;
|
|
// integer PRIM_ALPHA_MODE_BLEND = 1;
|
|
// integer PRIM_ALPHA_MODE_MASK = 2;
|
|
// integer PRIM_ALPHA_MODE_EMISSIVE = 3;
|
|
|
|
// The app ID is used on calculating the actual channel number used for communication
|
|
// and must match in both the HUD and receivers.
|
|
integer APP_ID = 20181024;
|
|
|
|
vector alphaOnColor = <0.000, 0.000, 0.000>;
|
|
vector buttonOnColor = <0.400, 0.700, 0.400>;
|
|
vector faceOnColor = <0.800, 1.000, 0.800>;
|
|
vector offColor = <1.000, 1.000, 1.000>;
|
|
|
|
// Which API version do we implement?
|
|
integer API_VERSION = 2;
|
|
|
|
// Alpha settings for 'on' nails: [top, under, tip]
|
|
list nail_alpha = [0.20, 1.00, 0.80];
|
|
|
|
// Configured values read from notecard
|
|
list nail_colors;
|
|
string nail_texture;
|
|
|
|
// Save last selected color
|
|
vector fingernail_color = ZERO_VECTOR;
|
|
vector toenail_color = ZERO_VECTOR;
|
|
|
|
// Halcyon and older OpenSimulator builds may not have the Bakes on Mesh
|
|
// constants defined. If you get a compiler error that these are not defined
|
|
// uncomment the following lines:
|
|
// string IMG_USE_BAKED_AUX1 = "9742065b-19b5-297c-858a-29711d539043";
|
|
// string IMG_USE_BAKED_AUX2 = "03642e83-2bd1-4eb9-34b4-4c47ed586d2d";
|
|
// string IMG_USE_BAKED_AUX3 = "edd51b77-fc10-ce7a-4b3d-011dfc349e4f";
|
|
|
|
// Nail BoM channels
|
|
string FINGERNAILS_BOM = IMG_USE_BAKED_AUX1;
|
|
string TOENAILS_BOM = IMG_USE_BAKED_AUX2;
|
|
|
|
// Keep a mapping of link number to prim name
|
|
list link_map = [];
|
|
|
|
integer num_links = 0;
|
|
|
|
// HUD Positioning offsets
|
|
float bottom_offset = 1.36;
|
|
float left_offset = -0.22;
|
|
float right_offset = 0.22;
|
|
float top_offset = 0.46;
|
|
integer last_attach = 0;
|
|
|
|
vector MIN_BAR = <0.0, 0.0, 0.0>;
|
|
vector OPTION_HUD = <PI_BY_TWO, 0.0, 0.0>;
|
|
vector SKIN_HUD = <PI, 0.0, 0.0>;
|
|
vector ALPHA_HUD = <-PI_BY_TWO, 0.0, 0.0>;
|
|
vector alpha_rot;
|
|
vector last_rot;
|
|
|
|
integer VERBOSE = FALSE;
|
|
|
|
// Memory limit
|
|
integer MEM_LIMIT = 65000;
|
|
|
|
// Ruth link messages
|
|
integer LINK_RUTH_HUD = 40;
|
|
integer LINK_RUTH_APP = 42;
|
|
|
|
// The name of the XTEA script
|
|
string XTEA_NAME = "r2_xtea";
|
|
|
|
// Set to encrypt 'message' and re-send on channel 'id'
|
|
integer XTEAENCRYPT = 13475896;
|
|
|
|
// Set in the reply to a received XTEAENCRYPT if the passed channel is 0 or ""
|
|
integer XTEAENCRYPTED = 8303877;
|
|
|
|
// Set to decrypt 'message' and reply vi llMessageLinked()
|
|
integer XTEADECRYPT = 4690862;
|
|
|
|
// Set in the reply to a received XTEADECRYPT
|
|
integer XTEADECRYPTED = 3450924;
|
|
|
|
integer haz_xtea = FALSE;
|
|
|
|
integer r2channel;
|
|
integer visible_fingernails = 0;
|
|
|
|
// *****
|
|
// New alpha part management
|
|
|
|
// Enumerate the body region types as of Bakes on Mesh
|
|
// We added our fingernail and toenail types at the end
|
|
// The index of this list is the value of <body-region> in the element maps
|
|
|
|
list regions = [
|
|
"head",
|
|
"upper",
|
|
"lower",
|
|
"eyes",
|
|
"skirt",
|
|
"hair",
|
|
"leftarm",
|
|
"leftleg",
|
|
"aux1",
|
|
"aux2",
|
|
"aux3",
|
|
"fingernails",
|
|
"toenails"
|
|
];
|
|
|
|
// A JSON buffer to save the alpha values of elements:
|
|
// key - element name
|
|
// value - 16-bit alpha values, 1 bit per face: 0 == visible, 1 == invisible
|
|
// Note this representation is opposite of the usage in the rest of this script where
|
|
// alpha values are integer representations of the actual face alpha float
|
|
string current_alpha = "{}";
|
|
|
|
// Alpha HUD button map
|
|
// These are also used as section names in mapping alpha sections in the mesh
|
|
list alpha_buttons = [
|
|
// alpha0
|
|
"",
|
|
"head",
|
|
"neck",
|
|
"arms",
|
|
"hands",
|
|
"fingernails",
|
|
"",
|
|
"hide",
|
|
// alpha1
|
|
"",
|
|
"torso",
|
|
"pelvis",
|
|
"legs",
|
|
"feet",
|
|
"toenails",
|
|
"",
|
|
"show"
|
|
];
|
|
|
|
|
|
// *****
|
|
|
|
// ***
|
|
// Hand pose
|
|
string gcPrevRtAnim = ""; //the previously selected animation on the right side
|
|
string gcPrevLfAnim = ""; //the previously selected animation on the left side
|
|
string gcWhichSide = ""; //Whether the currently selected animation is a right or left hand
|
|
|
|
integer hp_index = 0;
|
|
integer do_hp = FALSE;
|
|
// ***
|
|
|
|
// ***
|
|
// Foot pose
|
|
integer fp_offset = 30; // Add to the fp1 index (face) to get the actual anim in inventory
|
|
string PrevFootAnim = "";
|
|
string AnkleLockAnim = "30_anklelock"; // The index value must match the fp_offset above
|
|
integer AnkleLockEnabled = FALSE;
|
|
integer AnkleLockLink = 0;
|
|
integer AnkleLockFace = 4;
|
|
integer fp_index = 0;
|
|
integer do_fp = FALSE;
|
|
// ***
|
|
|
|
// ***
|
|
// Skin / Bakes on Mesh
|
|
integer current_skin = -1;
|
|
integer current_eye = -1;
|
|
integer alpha_mode;
|
|
integer mask_cutoff = 128;
|
|
|
|
// Map skin selections to button faces
|
|
// Stride 2: <sk0+N>, <face>
|
|
list skin_button_faces = [
|
|
// 0, 0, unused
|
|
// 0, 2, BoM
|
|
0, 4,
|
|
0, 6,
|
|
1, 0,
|
|
1, 2,
|
|
1, 4,
|
|
// 1, 6, unused
|
|
// 2, 0, unused
|
|
2, 2,
|
|
2, 4,
|
|
2, 6,
|
|
3, 0,
|
|
3, 2,
|
|
3, 4
|
|
// 3, 6, unused
|
|
];
|
|
|
|
// Map eye selections to button faces
|
|
// Stride 2: <eye0+N>, <face>
|
|
list eye_button_faces = [
|
|
// 0, 0, unused
|
|
// 0, 2, BoM
|
|
0, 4,
|
|
0, 6,
|
|
1, 0,
|
|
1, 2,
|
|
1, 4
|
|
// 1, 6, unused
|
|
];
|
|
|
|
// ***
|
|
|
|
log(string msg) {
|
|
if (VERBOSE == 1) {
|
|
llOwnerSay(msg);
|
|
}
|
|
}
|
|
|
|
// See if the notecard is present in object inventory
|
|
integer can_haz_notecard(string name) {
|
|
integer count = llGetInventoryNumber(INVENTORY_NOTECARD);
|
|
while (count--) {
|
|
if (llGetInventoryName(INVENTORY_NOTECARD, count) == name) {
|
|
log("Found notecard: " + name);
|
|
return TRUE;
|
|
}
|
|
}
|
|
llOwnerSay("Notecard " + name + " not found");
|
|
return FALSE;
|
|
}
|
|
|
|
// See if the XTEA script is present in object inventory
|
|
integer can_haz_script(string name) {
|
|
integer count = llGetInventoryNumber(INVENTORY_SCRIPT);
|
|
while (count--) {
|
|
if (llGetInventoryName(INVENTORY_SCRIPT, count) == name) {
|
|
log("Found script: " + name);
|
|
return TRUE;
|
|
}
|
|
}
|
|
log("Script " + name + " not found");
|
|
return FALSE;
|
|
}
|
|
|
|
send(string msg) {
|
|
if (haz_xtea) {
|
|
llMessageLinked(LINK_THIS, XTEAENCRYPT, msg, (string)r2channel);
|
|
} else {
|
|
llSay(r2channel, msg);
|
|
}
|
|
if (VERBOSE == 1) {
|
|
llOwnerSay("S: " + msg);
|
|
}
|
|
}
|
|
|
|
send_csv(list msg) {
|
|
send(llList2CSV(msg));
|
|
}
|
|
|
|
// Calculate a channel number based on APP_ID and owner UUID
|
|
integer keyapp2chan(integer id) {
|
|
return 0x80000000 | ((integer)("0x" + (string)llGetOwner()) ^ id);
|
|
}
|
|
|
|
vector get_size() {
|
|
return llList2Vector(llGetPrimitiveParams([PRIM_SIZE]), 0);
|
|
}
|
|
|
|
adjust_pos() {
|
|
integer current_attach = llGetAttached();
|
|
|
|
// See if attachpoint has changed
|
|
if ((current_attach > 0 && current_attach != last_attach) ||
|
|
(last_attach == 0)) {
|
|
vector size = get_size();
|
|
|
|
// Nasty if else block
|
|
if (current_attach == ATTACH_HUD_TOP_LEFT) {
|
|
llSetPos(<0.0, left_offset - size.y / 2, top_offset - size.z / 2>);
|
|
}
|
|
else if (current_attach == ATTACH_HUD_TOP_CENTER) {
|
|
llSetPos(<0.0, 0.0, top_offset - size.z / 2>);
|
|
}
|
|
else if (current_attach == ATTACH_HUD_TOP_RIGHT) {
|
|
llSetPos(<0.0, right_offset + size.y / 2, top_offset - size.z / 2>);
|
|
}
|
|
else if (current_attach == ATTACH_HUD_BOTTOM_LEFT) {
|
|
llSetPos(<0.0, left_offset - size.y / 2, bottom_offset + size.z / 2>);
|
|
}
|
|
else if (current_attach == ATTACH_HUD_BOTTOM) {
|
|
llSetPos(<0.0, 0.0, bottom_offset + size.z / 2>);
|
|
}
|
|
else if (current_attach == ATTACH_HUD_BOTTOM_RIGHT) {
|
|
llSetPos(<0.0, right_offset + size.y / 2, bottom_offset + size.z / 2>);
|
|
}
|
|
else if (current_attach == ATTACH_HUD_CENTER_1) {
|
|
}
|
|
else if (current_attach == ATTACH_HUD_CENTER_2) {
|
|
}
|
|
last_attach = current_attach;
|
|
}
|
|
}
|
|
|
|
|
|
// *****
|
|
// get/set alpha values in JSON buffer
|
|
|
|
// j - JSON value storage
|
|
// name - link_name
|
|
// face - integer face, -1 returns the unmasked 16 bit value
|
|
// Internal JSON values are ones complement, ie a 1 bit means the face is not visible
|
|
integer json_get_alpha(string j, string name, integer face) {
|
|
integer cur_val = (integer)llJsonGetValue(j, [name]);
|
|
if (face < 0) {
|
|
// All faces, return aggregate value masked to 16 bits
|
|
cur_val = ~cur_val & 0xffff;
|
|
} else {
|
|
cur_val = (cur_val & (1 << face)) == 0;
|
|
}
|
|
return cur_val;
|
|
}
|
|
|
|
// j - JSON value storage
|
|
// name - link_name
|
|
// face - integer face, -1 sets all 16 bits in the value
|
|
// value - alpha boolean, 0 = invisible, 1 = visible
|
|
// Internal JSON values are ones complement, ie a 1 bit means the face is not visible
|
|
string json_set_alpha(string j, string name, integer face, integer value) {
|
|
value = !value; // logical NOT for internal storage
|
|
integer cur_val = (integer)llJsonGetValue(j, [name]);
|
|
integer mask;
|
|
integer cmd;
|
|
if (face < 0) {
|
|
// All faces
|
|
mask = 0x0000;
|
|
// One's complement
|
|
cmd = -value;
|
|
} else {
|
|
mask = ~(1 << face);
|
|
cmd = (value << face);
|
|
}
|
|
// Mask final value to 16 bits
|
|
cur_val = ((cur_val & mask) | cmd) & 0xffff;
|
|
return llJsonSetValue(j, [name], (string)(cur_val));
|
|
}
|
|
// *****
|
|
|
|
|
|
// Set the alpha val of all links matching name
|
|
set_alpha(string name, integer face, float alpha) {
|
|
log("set_alpha(): name="+name+" face="+(string)face+" alpha="+(string)alpha);
|
|
send_csv(["ALPHA", name, face, alpha]);
|
|
current_alpha = json_set_alpha(current_alpha, name, face, (integer)alpha);
|
|
integer link;
|
|
for (; link < num_links; ++link) {
|
|
// Set color for all matching link names
|
|
if (llList2String(link_map, link) == name) {
|
|
// Reset links that appear in the list of body parts
|
|
if (alpha == 0) {
|
|
llSetLinkPrimitiveParamsFast(link, [PRIM_COLOR, face, alphaOnColor, 1.0]);
|
|
} else {
|
|
llSetLinkPrimitiveParamsFast(link, [PRIM_COLOR, face, offColor, 1.0]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// alpha = -1 toggles the current saved value
|
|
set_alpha_section(string section_name, integer alpha) {
|
|
integer i;
|
|
integer len = llGetListLength(alpha_buttons);
|
|
for (i = 0; i <= len; ++i) {
|
|
if (llList2String(alpha_buttons, i) == section_name) {
|
|
if (alpha < 0) {
|
|
// Toggle the current value
|
|
log("json: " + current_alpha);
|
|
alpha = !json_get_alpha(current_alpha, section_name, 0);
|
|
log("val: " + (string)alpha);
|
|
}
|
|
if (alpha > 0 && (section_name == "fingernails")) {
|
|
fingernails_on(fingernail_color);
|
|
}
|
|
else if (alpha > 0 && (section_name == "toenails")) {
|
|
toenails_on(toenail_color);
|
|
}
|
|
else {
|
|
send_csv(["ALPHA", section_name, -1, alpha]);
|
|
}
|
|
current_alpha = json_set_alpha(current_alpha, section_name, 0, (integer)alpha);
|
|
}
|
|
}
|
|
}
|
|
|
|
reset_alpha(float alpha) {
|
|
// Reset body and HUD doll
|
|
integer len = llGetListLength(alpha_buttons);
|
|
integer section;
|
|
for (section = 0; section < len; ++section) {
|
|
string section_name = llList2String(alpha_buttons, section);
|
|
if (alpha > 0 && (section_name == "fingernails")) {
|
|
fingernails_on(fingernail_color);
|
|
}
|
|
else if (alpha > 0 && (section_name == "toenails")) {
|
|
toenails_on(toenail_color);
|
|
}
|
|
else {
|
|
send_csv(["ALPHA", section_name, -1, alpha]);
|
|
}
|
|
current_alpha = json_set_alpha(current_alpha, section_name, 0, (integer)alpha);
|
|
}
|
|
|
|
// Reset HUD buttons
|
|
integer link;
|
|
integer j;
|
|
for (link = 0; link <= 3; ++link) {
|
|
for (j=0; j < 8; j+=2) {
|
|
set_outline_link_face_state("alpha", link, j, 1.0, (alpha < 0.01));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Literal API for TEXTURE v2 command
|
|
texture_v2(string name, string tex, integer face, vector color) {
|
|
string cmd = llList2CSV(["TEXTURE", name, tex, face, color]);
|
|
log(cmd);
|
|
send(cmd);
|
|
}
|
|
|
|
fingernails_on(vector color) {
|
|
send_csv(["ALPHAMODE", "fingernailunder", 1, PRIM_ALPHA_MODE_MASK, mask_cutoff]);
|
|
send_csv(["ALPHA", "fingernailtop", 0, llList2Float(nail_alpha, 0)]);
|
|
send_csv(["ALPHA", "fingernailunder", 1, llList2Float(nail_alpha, 1)]);
|
|
send_csv(["ALPHA", "fingernailtip", 2, llList2Float(nail_alpha, 2)]);
|
|
send_csv(["TEXTURE", "fingernails", nail_texture, 0, <237,227,222>]);
|
|
send_csv(["TEXTURE", "fingernails", nail_texture, 2, <237,227,222>]);
|
|
send_csv(["TEXTURE", "fingernails", nail_texture, 1, color]);
|
|
}
|
|
|
|
toenails_on(vector color) {
|
|
send_csv(["ALPHAMODE", "toenailunder", 1, PRIM_ALPHA_MODE_MASK, mask_cutoff]);
|
|
send_csv(["ALPHA", "toenailtop", 0, llList2Float(nail_alpha, 0)]);
|
|
send_csv(["ALPHA", "toenailunder", 1, llList2Float(nail_alpha, 1)]);
|
|
send_csv(["ALPHA", "toenailtip", 2, llList2Float(nail_alpha, 2)]);
|
|
send_csv(["TEXTURE", "toenails", nail_texture, 0, <237,227,222>]);
|
|
send_csv(["TEXTURE", "toenails", nail_texture, 2, <237,227,222>]);
|
|
send_csv(["TEXTURE", "toenails", nail_texture, 1, color]);
|
|
}
|
|
|
|
integer is_ankle_lock_running() {
|
|
return (
|
|
llListFindList(
|
|
llGetAnimationList(llGetOwner()),
|
|
[llGetInventoryKey(AnkleLockAnim)]
|
|
) >= 0
|
|
);
|
|
}
|
|
|
|
set_ankle_color(integer link) {
|
|
if (AnkleLockEnabled) {
|
|
llSetLinkPrimitiveParamsFast(link, [PRIM_COLOR, AnkleLockFace, <0.0, 1.0, 0.0>, 1.0]);
|
|
} else {
|
|
llSetLinkPrimitiveParamsFast(link, [PRIM_COLOR, AnkleLockFace, <1.0, 1.0, 1.0>, 1.0]);
|
|
}
|
|
}
|
|
|
|
// Look up link/face in map
|
|
integer lookup_button(list face_map, integer xlink, integer face) {
|
|
integer i;
|
|
for (i = 0; i < llGetListLength(face_map); i += 2) {
|
|
if (llList2Integer(face_map, i) == xlink &&
|
|
llList2Integer(face_map, i+1) == face) {
|
|
return i / 2;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
// Set state of an outline button given a link and face
|
|
set_outline_link_face_state(string prefix, integer xlink, integer face, float alpha, integer enabled) {
|
|
integer link = llListFindList(link_map, [prefix + (string)xlink]);
|
|
if (link >= 0) {
|
|
if (enabled) {
|
|
llSetLinkPrimitiveParamsFast(link, [
|
|
PRIM_COLOR, face, faceOnColor, alpha,
|
|
PRIM_COLOR, face+1, buttonOnColor, alpha,
|
|
PRIM_TEXTURE, face+1, TEXTURE_BLANK, <1.0, 1.0, 0.0>, <0.0, 0.0, 0.0>, 0.0
|
|
]);
|
|
} else {
|
|
llSetLinkPrimitiveParamsFast(link, [
|
|
PRIM_COLOR, face, <1.0, 1.0, 1.0>, alpha,
|
|
PRIM_COLOR, face+1, offColor, alpha,
|
|
PRIM_TEXTURE, face+1, TEXTURE_TRANSPARENT, <1.0, 1.0, 0.0>, <0.0, 0.0, 0.0>, 0.0
|
|
]);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Sets the texture and outline of an outline button using a map of button
|
|
// numbers to link/face
|
|
set_outline_button_state(list face_map, string prefix, integer index, integer enabled) {
|
|
integer xlink = llList2Integer(face_map, (index * 2));
|
|
integer face = llList2Integer(face_map, (index * 2) + 1);
|
|
set_outline_link_face_state(prefix, xlink, face, 1.0, enabled);
|
|
}
|
|
|
|
// Sets the texture and outline of an outline button using a map of button
|
|
// numbers to link/face
|
|
set_outline_button_tex(list face_map, string prefix, integer index, string texture) {
|
|
integer xlink = llList2Integer(face_map, (index * 2));
|
|
integer face = llList2Integer(face_map, (index * 2) + 1);
|
|
integer link = llListFindList(link_map, [prefix + (string)xlink]);
|
|
if (link >= 0) {
|
|
llSetLinkPrimitiveParamsFast(link, [
|
|
PRIM_COLOR, face, <1.0, 1.0, 1.0>, 1.0,
|
|
PRIM_TEXTURE, face, texture, <1.0, 1.0, 0.0>, <0.0, 0.0, 0.0>, 0.0,
|
|
PRIM_COLOR, face+1, offColor, 1.0,
|
|
PRIM_TEXTURE, face+1, TEXTURE_TRANSPARENT, <1.0, 1.0, 0.0>, <0.0, 0.0, 0.0>, 0.0
|
|
]);
|
|
}
|
|
}
|
|
|
|
init() {
|
|
// Initialize attach state
|
|
last_attach = llGetAttached();
|
|
log("state_entry() attached=" + (string)last_attach);
|
|
|
|
alpha_mode = PRIM_ALPHA_MODE_MASK;
|
|
|
|
r2channel = keyapp2chan(APP_ID);
|
|
llListen(r2channel+1, "", "", "");
|
|
llMessageLinked(LINK_THIS, LINK_RUTH_APP, llList2CSV(["appid", APP_ID]), "");
|
|
send_csv(["STATUS", API_VERSION]);
|
|
|
|
// Create map of all links to prim names
|
|
integer i;
|
|
num_links = llGetNumberOfPrims() + 1;
|
|
for (; i < num_links; ++i) {
|
|
list p = llGetLinkPrimitiveParams(i, [PRIM_NAME]);
|
|
string name = llList2String(p, 0);
|
|
link_map += [name];
|
|
if (name == "fp0") {
|
|
AnkleLockLink = i;
|
|
}
|
|
}
|
|
|
|
alpha_rot = ALPHA_HUD;
|
|
last_rot = OPTION_HUD;
|
|
|
|
log("Free memory " + (string)llGetFreeMemory() + " Limit: " + (string)MEM_LIMIT);
|
|
|
|
haz_xtea = can_haz_script(XTEA_NAME);
|
|
|
|
AnkleLockEnabled = is_ankle_lock_running();
|
|
set_ankle_color(AnkleLockLink);
|
|
}
|
|
|
|
default {
|
|
state_entry() {
|
|
init();
|
|
}
|
|
|
|
listen(integer channel, string name, key id, string message) {
|
|
if (llGetOwnerKey(id) == llGetOwner()) {
|
|
if (channel == r2channel+1) {
|
|
log("R: " + message);
|
|
list cmdargs = llCSV2List(message);
|
|
string command = llToUpper(llList2String(cmdargs, 0));
|
|
|
|
if (command == "STATUS") {
|
|
log(
|
|
"STATUS: " +
|
|
"API v" + llList2String(cmdargs, 1) + ", " +
|
|
"Type " + llList2String(cmdargs, 2) + ", " +
|
|
"Attached " + llList2String(cmdargs, 3)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
touch_start(integer total_number) {
|
|
integer link = llDetectedLinkNumber(0);
|
|
integer face = llDetectedTouchFace(0);
|
|
vector pos = llDetectedTouchST(0);
|
|
string name = llGetLinkName(link);
|
|
string message;
|
|
|
|
log("link=" + (string)link + " face=" + (string)face + " name=" + name);
|
|
|
|
if (name == "rotatebar") {
|
|
if(face == 1||face == 3||face == 5||face == 7) {
|
|
rotation localRot = llList2Rot(llGetLinkPrimitiveParams(LINK_ROOT, [PRIM_ROT_LOCAL]), 0);
|
|
llSetLinkPrimitiveParamsFast(LINK_ROOT, [PRIM_ROT_LOCAL, llEuler2Rot(<0.0, -PI_BY_TWO, 0.0>)*localRot]);
|
|
} else {
|
|
rotation localRot = llList2Rot(llGetLinkPrimitiveParams(LINK_ROOT, [PRIM_ROT_LOCAL]), 0);
|
|
llSetLinkPrimitiveParamsFast(LINK_ROOT, [PRIM_ROT_LOCAL, llEuler2Rot(<0.0, PI_BY_TWO, 0.0>)*localRot]);
|
|
}
|
|
// Save current alpha rotation
|
|
alpha_rot = llRot2Euler(llList2Rot(llGetLinkPrimitiveParams(LINK_ROOT, [PRIM_ROT_LOCAL]), 0));
|
|
}
|
|
else if (name == "minbar" || name == "alphabar" || name == "optionbar" || name == "skinbar") {
|
|
integer bx = (integer)(pos.x * 10);
|
|
integer by = (integer)(pos.y * 10);
|
|
log("x,y="+(string)bx+","+(string)by);
|
|
|
|
if (bx == 0 || bx == 1 || bx == 8 || name == "minbar") {
|
|
// min
|
|
vector next_rot = MIN_BAR;
|
|
|
|
if (last_rot == MIN_BAR) {
|
|
// Save current rotation for later
|
|
last_rot = llRot2Euler(llList2Rot(llGetLinkPrimitiveParams(LINK_ROOT,[PRIM_ROT_LOCAL]),0));
|
|
} else {
|
|
// Restore last rotation
|
|
next_rot = last_rot;
|
|
last_rot = MIN_BAR;
|
|
}
|
|
llSetLinkPrimitiveParamsFast(LINK_ROOT,[PRIM_ROT_LOCAL,llEuler2Rot(next_rot)]);
|
|
}
|
|
else if (bx == 2 || bx == 3) {
|
|
// alpha
|
|
llSetLinkPrimitiveParamsFast(LINK_ROOT,[PRIM_ROT_LOCAL,llEuler2Rot(alpha_rot)]);
|
|
last_rot = MIN_BAR;
|
|
}
|
|
else if (bx == 4 || bx == 5) {
|
|
// skin
|
|
llSetLinkPrimitiveParamsFast(LINK_ROOT,[PRIM_ROT_LOCAL,llEuler2Rot(SKIN_HUD)]);
|
|
last_rot = MIN_BAR;
|
|
}
|
|
else if (bx == 6 || bx == 7) {
|
|
// options
|
|
llSetLinkPrimitiveParamsFast(LINK_ROOT,[PRIM_ROT_LOCAL,llEuler2Rot(OPTION_HUD)]);
|
|
last_rot = MIN_BAR;
|
|
}
|
|
else if (bx == 9) {
|
|
log("DETACH!");
|
|
llRequestPermissions(llDetectedKey(0), PERMISSION_ATTACH);
|
|
}
|
|
}
|
|
else if (name == "alphadoll") {
|
|
// Handle the doll before the buttons
|
|
integer bx = (integer)(pos.x * 10);
|
|
integer by = (integer)(pos.y * 10);
|
|
if ((pos.x > 0.475 && pos.x < 0.525) && (pos.y > 0.775 && pos.y < 0.8)) {
|
|
// neck
|
|
set_alpha_section("neck", -1);
|
|
}
|
|
else if (by == 6 || by == 7) {
|
|
if (pos.x > 0.44 && pos.x < 0.56) {
|
|
// torso (except neck)
|
|
set_alpha_section("torso", -1);
|
|
}
|
|
else if (pos.x > 0.37 && pos.x < 0.63) {
|
|
// arms
|
|
set_alpha_section("arms", -1);
|
|
}
|
|
}
|
|
else if (bx == 4 || bx == 5) {
|
|
if (by == 8) {
|
|
// head
|
|
set_alpha_section("head", -1);
|
|
}
|
|
else if (by == 4 || by == 5) {
|
|
// pelvis
|
|
set_alpha_section("pelvis", -1);
|
|
}
|
|
else if (by == 2 || by == 3) {
|
|
// legs
|
|
set_alpha_section("legs", -1);
|
|
}
|
|
else if (by == 1) {
|
|
// feet
|
|
set_alpha_section("feet", -1);
|
|
}
|
|
}
|
|
else if ((bx == 3 || bx == 6) && (by == 4 || by == 5)) {
|
|
// hands
|
|
set_alpha_section("hands", -1);
|
|
}
|
|
}
|
|
else if (llGetSubString(name, 0, 4) == "alpha") {
|
|
integer b = ((integer)llGetSubString(name, 5, -1));
|
|
if (b == 1 && face == 6) {
|
|
// Hide all
|
|
reset_alpha(0.0);
|
|
}
|
|
else if (b == 3 && face == 6) {
|
|
// Show all
|
|
reset_alpha(1.0);
|
|
}
|
|
else {
|
|
set_alpha_section(llList2String(alpha_buttons, (b * 4) + (face >> 1)), -1);
|
|
|
|
// Set button color
|
|
vector face_color = llList2Vector(llGetLinkPrimitiveParams(link, [PRIM_NAME, PRIM_COLOR, face]), 1);
|
|
set_outline_link_face_state("alpha", b, face, 1.0, (face_color == offColor));
|
|
}
|
|
}
|
|
else if (llGetSubString(name, 0, 4) == "amode") {
|
|
// Alpha Mode
|
|
if (face == 2) {
|
|
// Alpha Masking
|
|
alpha_mode = PRIM_ALPHA_MODE_MASK;
|
|
set_outline_link_face_state("amode", 0, 2, 1.0, TRUE);
|
|
set_outline_link_face_state("amode", 0, 4, 1.0, FALSE);
|
|
}
|
|
else if (face == 4) {
|
|
// Alpha Blending
|
|
alpha_mode = PRIM_ALPHA_MODE_BLEND;
|
|
set_outline_link_face_state("amode", 0, 2, 1.0, FALSE);
|
|
set_outline_link_face_state("amode", 0, 4, 1.0, TRUE);
|
|
}
|
|
string cmd = llList2CSV(["ALPHAMODE", "all", -1, alpha_mode, mask_cutoff]);
|
|
log(cmd);
|
|
send(cmd);
|
|
}
|
|
else if (llGetSubString(name, 0, 1) == "sk") {
|
|
// Skin appliers
|
|
integer b = (integer)llGetSubString(name, 2, -1);
|
|
// BoM button is hard coded to xlink 0, face 2
|
|
if (b == 0 && face == 2) {
|
|
// Skin Bakes on Mesh
|
|
if (current_skin >= 0) {
|
|
set_outline_button_state(skin_button_faces, "sk", current_skin, FALSE);
|
|
}
|
|
current_skin = -1;
|
|
set_outline_link_face_state("sk", b, face, 1.0, TRUE);
|
|
llMessageLinked(LINK_THIS, LINK_RUTH_APP, llList2CSV(["skin", "bom"]), "");
|
|
} else {
|
|
integer index = lookup_button(skin_button_faces, b, face);
|
|
if (index >= 0) {
|
|
if (current_skin >= 0) {
|
|
set_outline_button_state(skin_button_faces, "sk", current_skin, FALSE);
|
|
} else {
|
|
// BoM button is hard coded to xlink 0, face 2
|
|
set_outline_link_face_state("sk", 0, 2, 1.0, FALSE);
|
|
}
|
|
current_skin = index;
|
|
set_outline_button_state(skin_button_faces, "sk", index, TRUE);
|
|
llMessageLinked(
|
|
LINK_THIS,
|
|
LINK_RUTH_APP,
|
|
llList2CSV(["skin", (string)(index+1)]),
|
|
""
|
|
);
|
|
}
|
|
}
|
|
}
|
|
else if (llGetSubString(name, 0, 2) == "eye") {
|
|
// Eye appliers
|
|
integer b = (integer)llGetSubString(name, 3, -1);
|
|
// BoM button is hard coded to xlink 0, face 2
|
|
if (b == 0 && face == 2) {
|
|
// Eyes Bakes on Mesh
|
|
if (current_eye >= 0) {
|
|
set_outline_button_state(eye_button_faces, "eye", current_eye, FALSE);
|
|
}
|
|
current_eye = -1;
|
|
set_outline_link_face_state("eye", b, face, 1.0, TRUE);
|
|
llMessageLinked(LINK_THIS, LINK_RUTH_APP, llList2CSV(["eyes", "bom"]), "");
|
|
} else {
|
|
integer index = lookup_button(eye_button_faces, b, face);
|
|
if (index >= 0) {
|
|
if (current_eye >= 0) {
|
|
set_outline_button_state(eye_button_faces, "eye", current_eye, FALSE);
|
|
} else {
|
|
// BoM button is hard coded to xlink 0, face 2
|
|
set_outline_link_face_state("eye", 0, 2, 1.0, FALSE);
|
|
}
|
|
current_eye = index;
|
|
set_outline_button_state(eye_button_faces, "eye", index, TRUE);
|
|
llMessageLinked(
|
|
LINK_THIS,
|
|
LINK_RUTH_APP,
|
|
llList2CSV(["eyes", (string)(index+1)]),
|
|
""
|
|
);
|
|
}
|
|
}
|
|
}
|
|
else if (llGetSubString(name, 0, 2) == "fnc") {
|
|
// Fingernail color
|
|
integer b = (integer)llGetSubString(name, 3, -1);
|
|
integer index = (b * 5) + face;
|
|
fingernail_color = (vector)llList2String(nail_colors, index);
|
|
if (index == 0) {
|
|
// BoM
|
|
texture_v2(
|
|
"fingernails",
|
|
FINGERNAILS_BOM,
|
|
ALL_SIDES,
|
|
fingernail_color
|
|
);
|
|
send_csv(["ALPHA", "fingernails", -1, 1.0]);
|
|
}
|
|
else if (index >= 1 && index <= 9) {
|
|
fingernails_on(fingernail_color);
|
|
}
|
|
}
|
|
else if (llGetSubString(name, 0, 2) == "fns") {
|
|
// Fingernail shape
|
|
list nail_types = [
|
|
"fingernailsshort",
|
|
"fingernailsmedium",
|
|
"fingernailslong",
|
|
"fingernailspointed",
|
|
"fingernailsnone",
|
|
"fingernailsoval"
|
|
];
|
|
integer b = (integer)llGetSubString(name, 2, -1);
|
|
if (face >= 0 && face <= 4) {
|
|
integer num = llGetListLength(nail_types);
|
|
integer i = 0;
|
|
visible_fingernails = face;
|
|
for (; i < num; ++i) {
|
|
if (i == face) {
|
|
send_csv(["ALPHA", llList2String(nail_types, i), ALL_SIDES, 1.0]);
|
|
} else {
|
|
send_csv(["ALPHA", llList2String(nail_types, i), ALL_SIDES, 0.0]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (llGetSubString(name, 0, 2) == "tnc") {
|
|
// Toenail color
|
|
integer b = (integer)llGetSubString(name, 3, -1);
|
|
integer index = (b * 5) + face;
|
|
toenail_color = (vector)llList2String(nail_colors, index);
|
|
if (index == 0) {
|
|
// BoM
|
|
texture_v2(
|
|
"toenails",
|
|
TOENAILS_BOM,
|
|
ALL_SIDES,
|
|
toenail_color
|
|
);
|
|
}
|
|
else if (index >= 1 && index <= 9) {
|
|
toenails_on(toenail_color);
|
|
}
|
|
}
|
|
else if (llGetSubString(name, 0, 1) == "hp") {
|
|
// Hand poses
|
|
integer b = ((integer)llGetSubString(name, 2, -1));
|
|
// There are 4 buttons per link but 2 faces per button
|
|
// All of the left buttons (hp0-hp2) come first then all
|
|
// of the right buttons (hp3-hp5) but the animations
|
|
// are L,R,L,R,... order.
|
|
|
|
// TODO: add stop button??
|
|
// Stop
|
|
//hp_index = 0;
|
|
|
|
// First get the delta for the R side buttons to overlap the left
|
|
integer delta = ((integer)(b / 3) * 3);
|
|
|
|
// Calculate the usual 8 face stride and add one for the right side
|
|
// and another one because the list is 1-based
|
|
hp_index = ((b - delta) * 8) + face + ((integer)(b / 3)) + 1;
|
|
|
|
integer i;
|
|
for (i=0; i<8; i+=2) {
|
|
set_outline_link_face_state("hp", 0+delta, i, 0.0, FALSE);
|
|
set_outline_link_face_state("hp", 1+delta, i, 0.0, FALSE);
|
|
set_outline_link_face_state("hp", 2+delta, i, 0.0, FALSE);
|
|
}
|
|
set_outline_link_face_state("hp", b, face, 0.4, TRUE);
|
|
|
|
do_hp = TRUE;
|
|
llRequestPermissions(llDetectedKey(0), PERMISSION_TRIGGER_ANIMATION);
|
|
}
|
|
else if (llGetSubString(name, 0, 1) == "fp") {
|
|
// Foot poses
|
|
if (name == "fp0") {
|
|
// Ankle Lock
|
|
AnkleLockEnabled = !AnkleLockEnabled;
|
|
log("ankle lock: " + (string)AnkleLockEnabled);
|
|
fp_index = face;
|
|
set_ankle_color(link);
|
|
do_fp = TRUE;
|
|
llRequestPermissions(llDetectedKey(0), PERMISSION_TRIGGER_ANIMATION);
|
|
} else {
|
|
fp_index = face + 1;
|
|
log("index: " + (string)face);
|
|
do_fp = TRUE;
|
|
llRequestPermissions(llDetectedKey(0), PERMISSION_TRIGGER_ANIMATION);
|
|
}
|
|
}
|
|
else {
|
|
// Do nothing here
|
|
}
|
|
}
|
|
|
|
link_message(integer sender_number, integer number, string message, key id) {
|
|
log("l_m: num: " + (string)number + " msg: " + message + " id: " + (string)id);
|
|
if (number == LINK_RUTH_HUD) {
|
|
// <command>,<arg1>,...
|
|
list cmdargs = llCSV2List(message);
|
|
string command = llToUpper(llList2String(cmdargs, 0));
|
|
if (command == "STATUS") {
|
|
log("Loaded notecard: " + llList2String(cmdargs, 1));
|
|
}
|
|
else if (command == "SKIN_THUMBNAILS") {
|
|
log("Loaded notecard: " + llList2String(cmdargs, 1));
|
|
integer len = llGetListLength(skin_button_faces);
|
|
integer i;
|
|
// Walk returned thumbnail list
|
|
for (i = 0; i < len; ++i) {
|
|
string tex = llList2String(cmdargs, i + 2);
|
|
if (tex == "") {
|
|
tex = TEXTURE_TRANSPARENT;
|
|
}
|
|
set_outline_button_tex(skin_button_faces, "sk", i, tex);
|
|
}
|
|
if (current_skin == -1) {
|
|
// Skin Bakes on Mesh
|
|
set_outline_link_face_state("sk", 0, 2, 1.0, TRUE);
|
|
} else {
|
|
set_outline_button_state(skin_button_faces, "sk", current_skin, TRUE);
|
|
}
|
|
}
|
|
else if (command == "EYE_THUMBNAILS") {
|
|
log("Loaded notecard: " + llList2String(cmdargs, 1));
|
|
integer len = llGetListLength(eye_button_faces);
|
|
integer i;
|
|
// Walk returned thumbnail list
|
|
for (i = 0; i < len; ++i) {
|
|
string tex = llList2String(cmdargs, i + 2);
|
|
if (tex == "") {
|
|
tex = TEXTURE_TRANSPARENT;
|
|
}
|
|
set_outline_button_tex(eye_button_faces, "eye", i, tex);
|
|
}
|
|
if (current_eye == -1) {
|
|
// Eyes Bakes on Mesh
|
|
set_outline_link_face_state("eye", 0, 2, 1.0, TRUE);
|
|
} else {
|
|
set_outline_button_state(eye_button_faces, "eye", current_eye, TRUE);
|
|
}
|
|
}
|
|
else if (command == "NAILS") {
|
|
nail_texture = llList2String(cmdargs, 1);
|
|
list links = [
|
|
llListFindList(link_map, ["fnc0"]),
|
|
llListFindList(link_map, ["fnc1"]),
|
|
llListFindList(link_map, ["tnc0"]),
|
|
llListFindList(link_map, ["tnc1"])
|
|
];
|
|
integer len = llGetListLength(cmdargs) - 2;
|
|
integer i;
|
|
nail_colors = [llList2String(cmdargs, 2)];
|
|
// Walk returned color list
|
|
for (i = 1; i < len; ++i) {
|
|
string tex = TEXTURE_BLANK;
|
|
string color = llList2String(cmdargs, i + 2);
|
|
if (color == "") {
|
|
tex = TEXTURE_TRANSPARENT;
|
|
color = "<1,1,1>";
|
|
}
|
|
integer link = i / 5;
|
|
integer face = i % 5;
|
|
llSetLinkPrimitiveParamsFast(llList2Integer(links, link), [
|
|
PRIM_COLOR, face, (vector)color, 1.0,
|
|
PRIM_TEXTURE, face, tex, <1.0, 1.0, 0.0>, <0.0, 0.0, 0.0>, 0.0
|
|
]);
|
|
llSetLinkPrimitiveParamsFast(llList2Integer(links, link+2), [
|
|
PRIM_COLOR, face, (vector)color, 1.0,
|
|
PRIM_TEXTURE, face, tex, <1.0, 1.0, 0.0>, <0.0, 0.0, 0.0>, 0.0
|
|
]);
|
|
nail_colors += color;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
run_time_permissions(integer perm) {
|
|
if (perm & PERMISSION_ATTACH) {
|
|
llDetachFromAvatar();
|
|
}
|
|
if (perm & PERMISSION_TRIGGER_ANIMATION) {
|
|
if (do_hp && hp_index == 0) {
|
|
// Stop all animations
|
|
list anims = llGetAnimationList(llGetPermissionsKey());
|
|
integer len = llGetListLength(anims);
|
|
integer i;
|
|
for (i = 0; i < len; ++i) {
|
|
llStopAnimation(llList2Key(anims, i));
|
|
}
|
|
// removing all anims can create problems - this sorts things out
|
|
llStartAnimation("stand");
|
|
// llOwnerSay("All finished: " + (string)len + llGetSubString(" animations",0,-1 - (len == 1))+" stopped.\n");
|
|
do_hp = FALSE;
|
|
}
|
|
else if (do_hp && hp_index > 0) {
|
|
// Locate and play a pose animation
|
|
integer nCounter = -1;
|
|
integer lFlag = FALSE;
|
|
integer nTotCount = llGetInventoryNumber(INVENTORY_ANIMATION);
|
|
integer nItemNo;
|
|
string anim = "";
|
|
do {
|
|
nCounter++;
|
|
anim = llGetInventoryName(INVENTORY_ANIMATION, nCounter);
|
|
nItemNo = (integer)anim;
|
|
if (nItemNo == hp_index) {
|
|
//When the Animation number matches the button number
|
|
if (anim != "") {
|
|
log("hp anim: " + anim);
|
|
|
|
if ((hp_index % 2) == 1) {
|
|
log(" left");
|
|
// Left side is odd
|
|
if (gcPrevLfAnim != "") {
|
|
llStopAnimation(gcPrevLfAnim);
|
|
}
|
|
gcPrevLfAnim = anim;
|
|
} else {
|
|
log(" right");
|
|
// Right side
|
|
if (gcPrevRtAnim != "") {
|
|
llStopAnimation(gcPrevRtAnim);
|
|
}
|
|
gcPrevRtAnim = anim;
|
|
}
|
|
llStartAnimation(anim);
|
|
lFlag = TRUE; //We found the animation
|
|
}
|
|
}
|
|
}
|
|
while (nCounter < nTotCount && !lFlag);
|
|
|
|
if (!lFlag) {
|
|
//Error messages - explanations of common problems a user might have if they assemble the HUD or add their own animations
|
|
if (nItemNo == 0) {
|
|
llOwnerSay("There's a problem. First check to make sure you've loaded all of the hand animations in the HUD inventory. There should be 24 of them. If that's not the problem, you may have used an incorrect name for one of the prims making up the HUD. Finally, double check to make sure that the backboard of the HUD is the last prim you linked (the root prim).\n");
|
|
}
|
|
else {
|
|
llOwnerSay("Animation # "+(string)nItemNo + " was not found. Check the animations in the inventory of the HUD. When numbering the animations, you may have left this number out.\n");
|
|
}
|
|
}
|
|
do_hp = FALSE;
|
|
}
|
|
if (do_fp) {
|
|
fp_index += fp_offset;
|
|
if (fp_index == fp_offset) {
|
|
// Handle ankle lock
|
|
if (AnkleLockEnabled) {
|
|
log(" start " + AnkleLockAnim);
|
|
llStartAnimation(AnkleLockAnim);
|
|
} else {
|
|
log(" stop " + AnkleLockAnim);
|
|
llStopAnimation(AnkleLockAnim);
|
|
}
|
|
} else {
|
|
// Handle foot poses
|
|
integer nCounter = -1;
|
|
integer nTotCount = llGetInventoryNumber(INVENTORY_ANIMATION);
|
|
string anim = "";
|
|
// Adjust for the foot pose animation index
|
|
do {
|
|
nCounter++;
|
|
anim = llGetInventoryName(INVENTORY_ANIMATION, nCounter);
|
|
if ((integer)anim == fp_index) {
|
|
log("fp anim: " + anim);
|
|
if (PrevFootAnim != "") {
|
|
log(" stopping: " + PrevFootAnim);
|
|
llStopAnimation(PrevFootAnim);
|
|
}
|
|
PrevFootAnim = anim;
|
|
llStartAnimation(anim);
|
|
nCounter = nTotCount; // Implicit break
|
|
}
|
|
}
|
|
while (nCounter < nTotCount);
|
|
}
|
|
do_fp = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
attach(key id) {
|
|
if (id == NULL_KEY) {
|
|
// Nothing to do on detach?
|
|
} else {
|
|
// Fix up our location
|
|
adjust_pos();
|
|
}
|
|
}
|
|
|
|
changed(integer change) {
|
|
if (change & (CHANGED_OWNER | CHANGED_INVENTORY)) {
|
|
init();
|
|
}
|
|
}
|
|
}
|