-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpathfinder_example.lsl
117 lines (102 loc) · 4.65 KB
/
pathfinder_example.lsl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
// public domain / CC-0.
// example of how a script can ask the pathfinding object for a path
// should normally use an at_target + waypoint + llSetForce() locomotion or something.
// should match the ID in the object description of the node manager object we want
// to consult for pathing.
string NODE_MANAGER_NAME = "REPLACEME";
// how many seconds it should take to travel a meter away
float TRAVEL_S_PER_M = 0.5;
// say "I summon thee" to summon the object.
integer PATHFINDING_REQ_CHANNEL = -21461423;
integer PATHFINDING_RESP_CHANNEL = -21461424;
list gWaypoints;
vector gLastPos;
vector gDesiredPos;
float gHeightOffset;
vector waypointToPos(vector waypoint) {
// waypoint pos -> pos we should move to
// all waypoints are 0.5m high cubes with the bottom touching the ground
// chances are this object is not a 0.5m high cube, so the position it
// needs to be on z will be different.
// Note that this only really makes sense when you mainly have paths along the ground.
return <waypoint.x, waypoint.y, waypoint.z - 0.25 + gHeightOffset>;
}
vector posToWaypoint(vector pos) {
// our pos -> approximate waypoint pos
return <pos.x, pos.y, pos.z + 0.25 - gHeightOffset>;
}
goToNextWaypoint() {
while (gWaypoints != []) {
// consume a waypoint from the list
gDesiredPos = waypointToPos((vector)llList2String(gWaypoints, 0));
gWaypoints = llDeleteSubList(gWaypoints, 0, 0);
// KFM uses relative positions, so calculate the offset to where we want to go
vector diff = gDesiredPos - gLastPos;
float travel_time = llVecDist(gLastPos, gDesiredPos) * TRAVEL_S_PER_M;
// Just try the next waypoint immediately if we're exactly over this node already
if (travel_time >= 0.001) {
// don't actually use KFM to move to the next waypoint if it's very near,
// llSetKeyframedMotion() doesn't accept travel times this short.
// We'll just let the llSetRegionPos() in the timer() event move it.
if (travel_time >= 0.1) {
llSetKeyframedMotion([diff, travel_time], [KFM_DATA, KFM_TRANSLATION]);
}
llSetTimerEvent(travel_time);
return;
}
}
llSetTimerEvent(0.0);
}
default {
state_entry() {
// bottom corner of bounding box, coords relative to root center.
vector min_corner = llList2Vector(llGetBoundingBox(llGetKey()), 0);
// should be our height offset from the ground
gHeightOffset = -min_corner.z;
llSetLinkPrimitiveParamsFast(LINK_THIS, [PRIM_PHYSICS_SHAPE_TYPE, PRIM_PHYSICS_SHAPE_CONVEX]);
llSetKeyframedMotion([ZERO_VECTOR, 0.15], [KFM_DATA, KFM_TRANSLATION]);
llSitTarget(<0,0,1>, ZERO_ROTATION);
llListen(PATHFINDING_RESP_CHANNEL, "", "", "");
llListen(0, "", "", "I summon thee");
}
listen(integer channel, string name, key id, string msg) {
if (channel == PATHFINDING_RESP_CHANNEL) {
if (llGetOwnerKey(id) != llGetOwner())
return;
list params = llParseString2List(msg, [":"], []);
string cmd = llList2String(params, 0);
params = llDeleteSubList(params, 0, 0);
if (cmd != "path") {
return;
}
if (params == []) {
llOwnerSay("Couldn't find a path!");
return;
}
llOwnerSay((string)llGetTime());
gLastPos = llGetPos();
gWaypoints = params;
goToNextWaypoint();
} else if (channel == 0) {
// Stop moving if we were already moving, we were just summoned.
llSetKeyframedMotion([], [KFM_COMMAND, KFM_CMD_PAUSE]);
llSetTimerEvent(0);
// TODO: Should probably account for their height offset for ground targets.
vector target_pos = llList2Vector(llGetObjectDetails(id, [OBJECT_POS]), 0);
string resp_msg = "find_path_vectors:" + NODE_MANAGER_NAME + ":" + (string)posToWaypoint(llGetPos()) + ":" + (string)target_pos;
llRegionSay(PATHFINDING_REQ_CHANNEL, resp_msg);
llResetTime();
}
}
timer() {
llSetKeyframedMotion([], [KFM_COMMAND, KFM_CMD_PAUSE]);
// Have to set position at each waypoint because keyframed motion can
// be interrupted by a sitter or owner selecting the object.
// Might be better to have keyframed motion only move in the general direction
// of the target with a short time step and keep doing that in a timer until
// the target is actually reached.
llSetRegionPos(gDesiredPos);
gLastPos = gDesiredPos;
goToNextWaypoint();
}
}