The recent discussion of checking when a player is looking at an object got me thinking about this problem, so I gave it a shot in Squirrel. My first attempt involved angular diameters and vision cones, but I couldn't get the trigonometry voodoo to work right, so I had to give up on that and use the brute-force raycasting approach.
Code:
class ZBLookTrap extends SqRootScript {
static CLASSNAME = "ZBLookTrap";
static LINKFLAVOR = "ControlDevice"; // thief
//static LINKFLAVOR = "SwitchLink"; // shock
// get the ball rolling
function OnBeginScript() {
if (IsDataSet("LookTimer")) {
KillTimer(GetData("LookTimer"));
}
SetData("LookTimer", SetOneShotTimer("LookCheck", 0.5));
}
// perform the look check
function OnTimer() {
if (message().name == "LookCheck") {
// fetch instance params
local maxDist = getParam(CLASSNAME + "MaxDist", 150); // maximum check distance
local checkInt = getParam(CLASSNAME + "CheckInt", 0.25); // check interval, in seconds
local repeatOn = getParam(CLASSNAME + "RepeatOn", false); // send continuous TurnOns when looking
local ignoreAI = getParam(CLASSNAME + "IgnoreAI", false); // exclude AIs from line-of-sight check
local ignoreUnrendered = getParam(CLASSNAME + "IgnoreUnrendered", true); // exclude objects with render mode Not Rendered
// check for line of sight
local wasLooking = GetData("WasLooking");
local isLooking = false;
if (!Camera.IsRemote()) {
local pos = Camera.GetPosition();
local face = Camera.GetFacing() * (PI / 180); // get facing as radians
// calc distant point player is looking at
local testPos = vector(
pos.x + maxDist * cos(face.y) * cos(face.z),
pos.y + maxDist * cos(face.y) * sin(face.z),
pos.z + maxDist * sin(face.y - PI));
local hitObj = object();
isLooking = (Engine.ObjRaycast(pos, testPos, vector(), hitObj, 0, ignoreAI.tointeger() + ignoreUnrendered.tointeger() * 2, null, null) > 1) && (hitObj.tointeger() == self);
}
// detect state transitions
if (isLooking && (!wasLooking || repeatOn)) {
Link.BroadcastOnAllLinks(self, "TurnOn", LINKFLAVOR);
SetData("WasLooking", 1);
}
else if (!isLooking && wasLooking) {
Link.BroadcastOnAllLinks(self, "TurnOff", LINKFLAVOR);
SetData("WasLooking", 0);
}
SetData("LookTimer", SetOneShotTimer("LookCheck", checkInt));
}
}
// fetch a parameter or return default value
function getParam(key, defVal) {
return key in userparams() ? userparams()[key] : defVal;
}
}
Place on object you want to check looking at, it'll send TurnOn when looked at, TurnOff when looked away from. Supports some design note configurating:
* ZBLookTrapIgnoreUnrendered: Exclude objects with render mode Not Rendered (default true)