NS-347 raycaster support

main
rhetorica 2025-12-13 21:30:37 -08:00
parent 964a7a4305
commit 463a398a7a
2 changed files with 523 additions and 6 deletions

View File

@ -89,17 +89,21 @@
// #define FORCE_INSERT // #define FORCE_INSERT
// attempts to insert the battery when door closing fails // attempts to insert the battery when door closing fails
// #define HOOK_POWER // #define HOOK_POWER
// causes transmission of "on", "off", "power", and "rate" link messages // causes transmission of link messages for "on", "off", "power", and "rate" light bus signals
// #define HOOK_COLOR // #define HOOK_COLOR
// causes transmission of "color", "color-2", "color-3", and "color-4" link messages // causes transmission of link messages for "color", "color-2", "color-3", and "color-4" light bus signals
// #define HOOK_FAN // #define HOOK_FAN
// causes transmission of "fan" link messages // causes transmission of link messages for "fan" light bus signals
// #define HOOK_MENU // #define HOOK_MENU
// causes transmission of "menu-open" and "menu-close" link messages // causes transmission of link messages for "menu-open" and "menu-close" light bus signals
// #define HOOK_CONDITION // #define HOOK_CONDITION
// causes transmission of "broken", "fixed", "working", and "done" link messages // causes transmission of link messages for "broken", "fixed", "working", and "done" light bus signals
// #define HOOK_PROJECTOR // #define HOOK_PROJECTOR
// causes forwarding of "projector" link messages // causes forwarding of link messages for "projector" light bus signals
// #define HOOK_COMMAND
// causes forwarding of link messages for "command" light bus signals (not normally handled by controller firmware)
// #define HOOK_ADD_CONFIRM
// causes forwarding of link messages for "add-confirm" light bus signals
// #define INJECT_STARTUP // #define INJECT_STARTUP
// causes state_entry() and on_rez() to call a function named extra_startup() // causes state_entry() and on_rez() to call a function named extra_startup()
// #define FULL_COLOR_BOOT // #define FULL_COLOR_BOOT
@ -966,6 +970,10 @@ default {
linked(LINK_THIS, 0, "menu-close", ""); linked(LINK_THIS, 0, "menu-close", "");
#endif #endif
#ifdef HOOK_ADD_CONFIRM
linked(LINK_THIS, 0, m, id);
#endif
jump socket_aperture; jump socket_aperture;
} else if(m == "socket-aperture-q") { } else if(m == "socket-aperture-q") {
jump socket_aperture; jump socket_aperture;
@ -1277,6 +1285,10 @@ default {
lubricant = (float)gets(argv, 1) / nlv; lubricant = (float)gets(argv, 1) / nlv;
} else if(cmd == "temperature") { } else if(cmd == "temperature") {
temperature = (float)gets(argv, 1); temperature = (float)gets(argv, 1);
#ifdef HOOK_COMMAND
} else if(cmd == "command") {
linked(LINK_THIS, 0, m, id);
#endif
} else if(cmd == "conf") { } else if(cmd == "conf") {
/*list lines = split(delstring(m, 0, 4), "\n"); // remove 'conf ' /*list lines = split(delstring(m, 0, 4), "\n"); // remove 'conf '
integer i = count(lines); integer i = count(lines);

View File

@ -0,0 +1,505 @@
/* =========================================================================
*
* Nanite Systems Advanced Research Encapsulation System
*
* Copyright (c) 20222024 Nanite Systems Corporation
*
* =========================================================================
*
* VAR/H Metropolis Hardware Driver
*
* This program is covered under the terms of the ARES Software Copyright
* License, Section 3 (ASCL-iii). It may be redistributed or used as the
* basis of commercial, closed-source products so long as steps are taken
* to ensure proper attribution as defined in the text of the license.
*
* To see the full text of the ASCL, type 'help license' on any standard
* ARES distribution, or visit http://nanite-systems.com/ASCL for the
* current version.
*
* DISCLAIMER
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS
* IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
* DAMAGES HOWEVER CAUSED ON ANY THEORY OF LIABILITY ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* =========================================================================
*
*/
#include <utils.lsl>
#include <objects.lsl>
#define SND_BUTTON "e4174b28-6011-4b99-040b-b4ae58064f05"
#define TX_SCREEN_DEFAULT "27409425-2804-5be0-5b83-9ccb3fa1888b"
#define TX_SCREEN_INSTRUCTIONS "befc210f-0198-ffba-6948-f201431300be"
#define SND_OPEN "6ce74082-6781-fec8-76fc-a7b80f4993d1"
#define SND_CLOSE "08a7ec6b-ab2d-687f-b639-a2b222f74c56"
#define SND_A_OUT "5dad1f4d-c65b-fd8f-50dd-5d5b384ce060"
#define SND_A_IN "484c0af3-f58a-6102-7e84-93d95f9da585"
list screens = [
TX_SCREEN_DEFAULT,
TX_SCREEN_INSTRUCTIONS
];
#define B_POWER 36
#define B_B 27
#define B_A 21
#define B_PREV 24
#define B_NEXT 17
#define B_ANTENNAE 13
#define LID_BASIS 22
#define LID_1 25
#define LID_2 32
#define LID_3 43
#define SCREEN 2
list buttons = [B_POWER, B_B, B_A, B_PREV, B_NEXT, B_ANTENNAE];
list b_names = ["power", "b", "a", "prev", "next", "antennae"];
list commands = [
"power",
"command $USER $USER exec echo B pressed",
"command $USER $USER exec echo A pressed",
"command $USER $USER exec ray prev",
"command $USER $USER exec ray next",
"command $USER $USER exec ray antennae"
];
integer power_on;
key system;
key avatar;
integer CL;
integer door_open;
key hatch_operator;
integer timer_close_hatch;
float next_timer;
#define cmd_format(command) replace(replace(command, "$ME", llGetKey()), "$USER", toucher)
#define LID_DEFAULT_ROT llEuler2Rot(<0, 270, 180> * DEG_TO_RAD)
operate_door() {
vector door_scale = getv(getp(LID_3, [PRIM_SIZE]), 0);
vector door_pos = ZV;
vector LID_1_HINGE = door_pos + <0, door_scale.y * -0.5, 0> * llEuler2Rot(<240 * DEG_TO_RAD, 0, 0>);
vector LID_2_HINGE = door_pos + <0, door_scale.y * -0.5, 0> * llEuler2Rot(<120 * DEG_TO_RAD, 0, 0>);
vector LID_3_HINGE = door_pos + <0, door_scale.y * -0.5, 0>;
#define THETA 60
#define STEP_SIZE 4.0
#define SLEEP_SIZE 0.022
float origin_theta = (float)door_open * THETA;
float dest_theta = (float)(1 - door_open) * THETA;
float step = (door_open * 2 - 1) * -STEP_SIZE; // maps [0,1] to [1,-1]
// echo("from " + (string)origin_theta + " to " + (string)dest_theta + " by " + (string)step);
if(!door_open)
llLinkPlaySound(LID_BASIS, SND_OPEN, 1.0, SOUND_PLAY);
else
llLinkPlaySound(LID_BASIS, SND_CLOSE, 1.0, SOUND_PLAY);
float t = origin_theta;
while(llFabs(dest_theta - t) > llFabs(step) * 0.5 && (t <= THETA && t >= 0)) {
setp(0, [
PRIM_LINK_TARGET, LID_1,
PRIM_ROT_LOCAL, LID_DEFAULT_ROT * (llEuler2Rot(<t * DEG_TO_RAD, 0, 0> * llEuler2Rot(<0, 0, 240 * DEG_TO_RAD>))),
PRIM_POS_LOCAL, door_pos,
PRIM_LINK_TARGET, LID_2,
PRIM_ROT_LOCAL, LID_DEFAULT_ROT * (llEuler2Rot(<t * DEG_TO_RAD, 0, 0> * llEuler2Rot(<0, 0, 120 * DEG_TO_RAD>))),
PRIM_POS_LOCAL, door_pos,
PRIM_LINK_TARGET, LID_3,
PRIM_ROT_LOCAL, LID_DEFAULT_ROT * (llEuler2Rot(<t * DEG_TO_RAD, 0, 0>)),
PRIM_POS_LOCAL, door_pos
]);
llSleep(SLEEP_SIZE);
t += step;
}
setp(0, [
PRIM_LINK_TARGET, LID_1,
PRIM_ROT_LOCAL, LID_DEFAULT_ROT * (llEuler2Rot(<dest_theta * DEG_TO_RAD, 0, 0> * llEuler2Rot(<0, 0, 240 * DEG_TO_RAD>))),
PRIM_POS_LOCAL, door_pos,
PRIM_LINK_TARGET, LID_2,
PRIM_ROT_LOCAL, LID_DEFAULT_ROT * (llEuler2Rot(<dest_theta * DEG_TO_RAD, 0, 0> * llEuler2Rot(<0, 0, 120 * DEG_TO_RAD>))),
PRIM_POS_LOCAL, door_pos,
PRIM_LINK_TARGET, LID_3,
PRIM_ROT_LOCAL, LID_DEFAULT_ROT * (llEuler2Rot(<dest_theta * DEG_TO_RAD, 0, 0>)),
PRIM_POS_LOCAL, door_pos
]);
door_open = !door_open;
}
#define ANTENNAE_BASIS 18
#define A_SCALE_MODEL 37
#define A_RIGHT 38
#define A_RIGHT_BALL 4
#define A_RIGHT_BASE 34
#define A_LEFT 47
#define A_LEFT_BALL 45
#define A_LEFT_BASE 44
integer antennae_extended = 1;
integer antennae_should_extend = 1;
antennae(integer extended) {
// echo("Antennae now " + (string)extended);
if(extended != antennae_extended) {
antennae_extended = extended;
vector ball_scale = getv(getp(A_SCALE_MODEL, [PRIM_SIZE]), 0);
vector antenna_scale = <8, 1, 1> * ball_scale.x;
float antenna_length = antenna_scale.x;
vector antenna_L_pos = getv(getp(A_LEFT_BASE, [PRIM_POS_LOCAL]), 0);
vector antenna_R_pos = getv(getp(A_RIGHT_BASE, [PRIM_POS_LOCAL]), 0);
rotation antenna_L_rot = getr(getp(A_LEFT_BASE, [PRIM_ROT_LOCAL]), 0);
rotation antenna_R_rot = getr(getp(A_RIGHT_BASE, [PRIM_ROT_LOCAL]), 0);
float t;
#define X_MIN 0.01625
#define X_MAX 0.4
#define A_STEP_SIZE 0.006
#define A_SLEEP_SIZE 0.022
float rtd = llGetRegionTimeDilation();
if(rtd < 0.2)
rtd = 0.2;
float dest_x = (float)antennae_extended * (X_MAX - X_MIN) + X_MIN;
float origin_x = (float)(1.0 - antennae_extended) * (X_MAX - X_MIN) + X_MIN;
float step = (antennae_extended * 2 - 1) * A_STEP_SIZE / rtd; // maps [0,1] to [-1,1]
llResetTime();
if(extended)
llLinkPlaySound(ANTENNAE_BASIS, SND_A_OUT, 1.0, SOUND_PLAY);
else
llLinkPlaySound(ANTENNAE_BASIS, SND_A_IN, 1.0, SOUND_PLAY);
float last_time = llGetTime();
float x = origin_x;
while(llFabs(dest_x - x) > llFabs(step) * 0.5 && (x >= X_MIN && x <= X_MAX)) {
setp(0, [
PRIM_LINK_TARGET, A_RIGHT,
PRIM_POS_LOCAL,
antenna_R_pos - <antenna_length * x, 0, 0> * (antenna_R_rot),
PRIM_SIZE,
<8 * ((x) / (X_MAX)), 1, 1> * ball_scale.x,
PRIM_LINK_TARGET, A_RIGHT_BALL,
PRIM_POS_LOCAL,
antenna_R_pos - <antenna_length * x, 0, 0> * (antenna_R_rot),
PRIM_LINK_TARGET, A_LEFT,
PRIM_POS_LOCAL,
antenna_L_pos - <antenna_length * x, 0, 0> * (antenna_L_rot),
PRIM_SIZE,
<8 * ((x) / (X_MAX)), 1, 1> * ball_scale.x,
PRIM_LINK_TARGET, A_LEFT_BALL,
PRIM_POS_LOCAL,
antenna_L_pos - <antenna_length * x, 0, 0> * (antenna_L_rot)
]);
llSleep(A_SLEEP_SIZE * rtd);
float now = llGetTime();
x += step * (now - last_time) / (A_SLEEP_SIZE * rtd);
last_time = now;
}
x = dest_x;
setp(0, [
PRIM_LINK_TARGET, A_RIGHT,
PRIM_POS_LOCAL,
antenna_R_pos - <antenna_length * x, 0, 0> * (antenna_R_rot),
PRIM_SIZE,
<8 * ((x) / (X_MAX)), 1, 1> * ball_scale.x,
PRIM_LINK_TARGET, A_RIGHT_BALL,
PRIM_POS_LOCAL,
antenna_R_pos - <antenna_length * x, 0, 0> * (antenna_R_rot),
PRIM_LINK_TARGET, A_LEFT,
PRIM_POS_LOCAL,
antenna_L_pos - <antenna_length * x, 0, 0> * (antenna_L_rot),
PRIM_SIZE,
<8 * ((x) / (X_MAX)), 1, 1> * ball_scale.x,
PRIM_LINK_TARGET, A_LEFT_BALL,
PRIM_POS_LOCAL,
antenna_L_pos - <antenna_length * x, 0, 0> * (antenna_L_rot)
]);
// echo((string)llGetTime());
}
}
shutdown() {
setp(SCREEN, [
PRIM_COLOR, ALL_SIDES, ZV, 0,
PRIM_GLOW, ALL_SIDES, 0
]);
// antennae are always retracted when powered down:
antennae(FALSE);
power_on = 0;
if(!timer_close_hatch) // should always be the case
llSetTimerEvent(0);
}
boot() {
setp(SCREEN, [
PRIM_COLOR, ALL_SIDES, ONES, 1,
PRIM_GLOW, ALL_SIDES, 0.125
]);
// restore user preference:
if(antennae_should_extend)
antennae(TRUE);
power_on = 1;
if(door_open)
operate_door();
llSetTimerEvent(auto_cycle);
}
integer auto_cycle = 0;
integer image_index = 0;
default {
state_entry() {
CL = 105 - (integer)("0x" + substr(avatar = llGetOwner(), 29, 35));
system = "";
tell(avatar, CL, "ping");
/* echo(llGetLinkName(LID_1));
echo(llGetLinkName(LID_2));
echo(llGetLinkName(LID_3)); */
}
timer() {
if(timer_close_hatch) {
operate_door();
timer_close_hatch = 0;
}
if(auto_cycle && power_on) {
if(++image_index >= count(screens)) {
image_index = 0;
}
setp(SCREEN, [
PRIM_TEXTURE, ALL_SIDES, gets(screens, image_index), <2, 1, 0>, ZV, PI_BY_TWO
]);
}
if(!timer_close_hatch && !auto_cycle) {
llSetTimerEvent(0);
} else if(auto_cycle) {
llSetTimerEvent(auto_cycle);
}
}
on_rez(integer n) {
CL = 105 - (integer)("0x" + substr(avatar = llGetOwner(), 29, 35));
system = "";
}
touch_start(integer n) {
while(n--) {
integer pi = llDetectedLinkNumber(n);
string part = llGetLinkName(pi);
key toucher = llDetectedKey(n);
integer pj = index(buttons, pi);
if(~pj) {
llTriggerSound(SND_BUTTON, 1);
string c = gets(commands, pj);
if(system) {
if(c == "power") {
if(power_on)
c = "command $USER $USER power off";
else
c = "command $USER $USER power on";
}
tell(system, CL, cmd_format(c));
} else {
tell(avatar, CL, "ping");
tell(toucher, 0, "Not yet connected to ARES. Please try again.");
}
} else if((part == "lid") && !power_on) {
if(door_open) {
timer_close_hatch = 1;
hatch_operator = toucher;
tell(llGetOwner(), CL, "hatch-blocked-q");
if(next_timer < llGetTime() + 0.5) {
next_timer = llGetTime() + 0.5;
llSetTimerEvent(0.5);
}
} else {
operate_door();
}
} else if(part != "socket" && !power_on) {
tell(system, CL, "command " + (string)toucher + " " + (string)toucher + " power on");
} else if(power_on) {
tell(system, CL, "menu request " + (string)toucher);
}
}
}
link_message(integer src, integer n, string m, key id) {
// echo((string)n + ": " + m + " (" + (string)id + ")");
list argv = splitnulls(m, " ");
string cmd = gets(argv, 0);
if(n == 0) {
if(m == "add-confirm" || ((m == "on" || m == "off"))) {
if(m == "add-confirm" || system == "") {
// register special commands here:
system = id;
tell(id, CL, "add-command ray");
}
if(m != "add-confirm") {
integer new_power_on = (m == "on");
if(new_power_on != power_on) {
if(power_on)
shutdown();
else
boot();
}
}
} else if(cmd == "command") {
// echo("Got command: " + m);
key user = gets(argv, 1);
string cmd = gets(argv, 2);
string action = gets(argv, 3);
if(action == "") {
tell(user, 0,
"Syntax: @ray <action>"
+ "\n\nWhere <action> is one of: "
+ "\n next: Cycle to next image"
+ "\n prev: Cycle to previous image"
+ "\n auto <interval>: Automatically cycle images every <interval> seconds (0 = off)"
+ "\n reset: Restore default images"
+ "\n add <uuid> [<uuid>...]: Add a new image"
+ "\n remove: Remove current image"
+ "\n screens: List current screen images"
+ "\n command <button> <string>: Set command for button"
+ "\n Supported buttons: power, b, a, prev, next, antennae"
+ "\n commands: List current commands for all buttons"
+ "\n antennae [on|off]: Toggle or set antennae state"
);
} else if(action == "auto") {
string new_auto = gets(argv, 4);
if(new_auto != "") {
auto_cycle = (integer)new_auto;
if(power_on)
llSetTimerEvent(auto_cycle);
} else {
tell(user, 0, "Auto cycle interval: " + (string)auto_cycle);
}
} else if(action == "next") {
if(++image_index == count(screens)) {
image_index = 0;
}
setp(SCREEN, [
PRIM_TEXTURE, ALL_SIDES, gets(screens, image_index), <2, 1, 0>, ZV, PI_BY_TWO
]);
} else if(action == "prev") {
if(--image_index < 0) {
image_index = count(screens) - 1;
}
// echo("Screen now " + (string)image_index);
setp(SCREEN, [
PRIM_TEXTURE, ALL_SIDES, gets(screens, image_index), <2, 1, 0>, ZV, PI_BY_TWO
]);
} else if(action == "reset") {
screens = [
TX_SCREEN_DEFAULT,
TX_SCREEN_INSTRUCTIONS
];
} else if(action == "add") {
list new_screens = delrange(argv, 0, 3);
if(count(new_screens)) {
screens = llListInsertList(screens, new_screens, image_index + 1);
image_index += 1;
setp(SCREEN, [
PRIM_TEXTURE, ALL_SIDES, gets(screens, image_index), <2, 1, 0>, ZV, PI_BY_TWO
]);
tell(user, 0, "Added " + (string)count(new_screens) + " screen(s).");
} else {
tell(user, 0, "No new screens specified. Please add by UUID.");
}
} else if(action == "remove") {
screens = delitem(screens, image_index);
tell(user, 0, "Removed 1 screen.");
if(count(screens) == image_index) {
if(image_index == 0) {
tell(user, 0, "All images removed. Restoring default set.");
screens = [
TX_SCREEN_DEFAULT,
TX_SCREEN_INSTRUCTIONS
];
} else {
image_index = 0;
}
setp(SCREEN, [
PRIM_TEXTURE, ALL_SIDES, gets(screens, image_index), <2, 1, 0>, ZV, PI_BY_TWO
]);
}
} else if(action == "screens") {
tell(user, 0, "Current screens: " + concat(screens, ", "));
} else if(action == "command") {
tell(user, 0, "Unimplemented. FIX BEFORE SHIPPING AAAAAHHHHH");
string b_name = gets(argv, 4);
integer b = index(b_names, b_name);
if(~b) {
string command = concat(delrange(argv, 0, 4), " ");
if(gets(argv, 5) == "raw") {
command = concat(delrange(argv, 0, 5), " ");
} else if(command != "power" && gets(argv, 5) != "command") {
command = "command $USER $USER exec " + command;
}
commands = alter(commands, [command], b, b);
tell(user, 0, "Action for button " + b_name + " updated.");
} else {
tell(user, 0, "Invalid button name: " + b_name + ". Must be one of: " + concat(b_names, ", ") + ".");
}
} else if(action == "commands") {
tell(user, 0, "Current commands:\n" + format_table(zip(b_names, commands), 2, ": "));
} else if(action == "antennae") {
integer new = !antennae_extended;
string subaction = gets(argv, 4);
if(subaction == "on")
new = 1;
else if(subaction == "off")
new = 0;
// user preference:
antennae_should_extend = new;
antennae(new);
} else {
tell(user, 0, "Unknown action: " + action + ". See '@ray' for instructions.");
}
} else if(cmd == "power" || cmd == "rate") {
// ignore it
} /*else {
echo((string)n + ": " + m + " (" + (string)id + ")");
}*/
}
}
}