@makefully/platypus 4.1.4 → 4.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/README.md +4 -2
- package/lib/platypus.js +1 -1
- package/package.json +7 -2
- package/src/AABB.js +685 -0
- package/src/ActionState.js +121 -0
- package/src/AssetManager.js +206 -0
- package/src/CollisionData.js +96 -0
- package/src/CollisionDataContainer.js +127 -0
- package/src/CollisionShape.js +315 -0
- package/src/Component.js +231 -0
- package/src/Data.js +64 -0
- package/src/DataMap.js +204 -0
- package/src/Entity.js +404 -0
- package/src/EntityLinker.js +113 -0
- package/src/EventHandler.js +115 -0
- package/src/EventHandlerList.js +251 -0
- package/src/Game.js +840 -0
- package/src/Gamepad.js +175 -0
- package/src/ID3CaptionPlayer.js +61 -0
- package/src/Messenger.js +365 -0
- package/src/PIXIAnimation.js +622 -0
- package/src/RandomSet.js +140 -0
- package/src/SFXPlayer.js +120 -0
- package/src/StateMap.js +252 -0
- package/src/Storage.js +152 -0
- package/src/TickerClient.js +48 -0
- package/src/TickerServer.js +23 -0
- package/src/TimeEvent.js +50 -0
- package/src/TimeEventList.js +100 -0
- package/src/VOPlayer.js +684 -0
- package/src/Vector.js +702 -0
- package/src/components/AIChaser.js +161 -0
- package/src/components/AIPacer.js +107 -0
- package/src/components/AudioMusic.js +151 -0
- package/src/components/AudioSFX.js +673 -0
- package/src/components/AudioVO.js +301 -0
- package/src/components/CollisionBasic.js +826 -0
- package/src/components/CollisionFilter.js +74 -0
- package/src/components/CollisionGroup.js +448 -0
- package/src/components/CollisionTiles.js +608 -0
- package/src/components/ComponentSwitcher.js +173 -0
- package/src/components/ControllerInput.js +65 -0
- package/src/components/Counter.js +165 -0
- package/src/components/EntityCamera.js +215 -0
- package/src/components/EntityContainer.js +549 -0
- package/src/components/EntityController.js +541 -0
- package/src/components/HandlerCamera.js +1052 -0
- package/src/components/HandlerCollision.js +1318 -0
- package/src/components/HandlerController.js +291 -0
- package/src/components/HandlerLogic.js +394 -0
- package/src/components/HandlerRender.js +217 -0
- package/src/components/Interactive.js +502 -0
- package/src/components/LevelBuilder.js +524 -0
- package/src/components/LogicAngularMovement.js +154 -0
- package/src/components/LogicAttachment.js +265 -0
- package/src/components/LogicButton.js +466 -0
- package/src/components/LogicCarrier.js +39 -0
- package/src/components/LogicDestroyMe.js +58 -0
- package/src/components/LogicDirectionalMovement.js +325 -0
- package/src/components/LogicDragDrop.js +371 -0
- package/src/components/LogicImpactLaunch.js +144 -0
- package/src/components/LogicPacingPlatform.js +64 -0
- package/src/components/LogicPortable.js +112 -0
- package/src/components/LogicPortal.js +115 -0
- package/src/components/LogicPushable.js +98 -0
- package/src/components/LogicRebounder.js +136 -0
- package/src/components/LogicRegionSpawner.js +139 -0
- package/src/components/LogicRotationalMovement.js +205 -0
- package/src/components/LogicSpawner.js +159 -0
- package/src/components/LogicStateMachine.js +382 -0
- package/src/components/LogicSwitch.js +77 -0
- package/src/components/LogicTeleportee.js +68 -0
- package/src/components/LogicTeleporter.js +93 -0
- package/src/components/LogicTimer.js +178 -0
- package/src/components/LogicWindUpRacer.js +179 -0
- package/src/components/Motion.js +371 -0
- package/src/components/Mover.js +566 -0
- package/src/components/Node.js +289 -0
- package/src/components/NodeMap.js +295 -0
- package/src/components/NodeResident.js +636 -0
- package/src/components/Orientation.js +697 -0
- package/src/components/RandomEvents.js +73 -0
- package/src/components/RelativePosition.js +204 -0
- package/src/components/RelayFamily.js +106 -0
- package/src/components/RelayGame.js +53 -0
- package/src/components/RelayLinker.js +94 -0
- package/src/components/RelayParent.js +57 -0
- package/src/components/RelaySelf.js +55 -0
- package/src/components/Render.js +178 -0
- package/src/components/RenderAnimator.js +275 -0
- package/src/components/RenderContainer.js +646 -0
- package/src/components/RenderDebug.js +319 -0
- package/src/components/RenderDestroyMe.js +59 -0
- package/src/components/RenderLottie.js +149 -0
- package/src/components/RenderProgress.js +120 -0
- package/src/components/RenderSpine.js +833 -0
- package/src/components/RenderSprite.js +527 -0
- package/src/components/RenderText.js +147 -0
- package/src/components/RenderTiles.js +1391 -0
- package/src/components/SceneChanger.js +128 -0
- package/src/components/TiledLoader.js +1451 -0
- package/src/components/Timeline.js +314 -0
- package/src/components/Tutorial.js +267 -0
- package/src/components/Tween.js +296 -0
- package/src/components/VoiceOver.js +373 -0
- package/src/components/XHR.js +129 -0
- package/src/components/index.js +79 -0
- package/src/factory.js +305 -0
- package/src/index.js +159 -0
- package/src/mixins/PIXI.Graphics.js +41 -0
- package/src/platypus.css +68 -0
- package/src/sayHello.js +50 -0
- package/src/shapes/Capsule.js +74 -0
- package/src/shapes/Circle.js +43 -0
- package/src/shapes/Ellipse.js +117 -0
- package/src/shapes/Point.js +191 -0
- package/src/shapes/Polygon.js +112 -0
- package/src/shapes/Polyline.js +97 -0
- package/src/shapes/Rectangle.js +78 -0
- package/src/shapes/RoundedRectangle.js +33 -0
- package/src/shapes/Segment.js +346 -0
- package/src/shapes/adjustedXY.js +19 -0
- package/src/shapes/castFromBox2D.js +92 -0
- package/src/shapes/castFromObject.js +53 -0
- package/src/shapes/castFromPixiGraphics.js +6 -0
- package/src/shapes/castFromPixiShape.js +89 -0
- package/src/shapes/castToBox2D.js +190 -0
- package/src/shapes/castToPixiGraphics.js +100 -0
- package/src/shapes/castToPixiShape.js +61 -0
- package/src/shapes/checkPointsMap.js +313 -0
- package/src/shapes/index.js +35 -0
- package/src/utils/array.js +73 -0
- package/src/utils/string.js +39 -0
|
@@ -0,0 +1,636 @@
|
|
|
1
|
+
/**
|
|
2
|
+
# COMPONENT **NodeResident**
|
|
3
|
+
|
|
4
|
+
### Local Broadcasts:
|
|
5
|
+
- **next-to-[entity-type]** - This message is triggered when the entity is placed on a node. It will trigger on all neighboring entities, as well as on itself on behalf of neighboring entities.
|
|
6
|
+
- @param entity (Entity) - The entity that is next to the listening entity.
|
|
7
|
+
- **with-[entity-type]** - This message is triggered when the entity is placed on a node. It will trigger on all entities residing on the same node, as well as on itself on behalf of all resident entities.
|
|
8
|
+
- @param entity (Entity) - The entity that is with the listening entity.
|
|
9
|
+
- **left-node** - Triggered when the entity leaves a node.
|
|
10
|
+
- @param node (Node) - The node that the entity just left.
|
|
11
|
+
- **[Messages specified in definition]** - When the entity is placed on a node, it checks out the type of node and triggers a message on the entity if an event is listed for the current node type.
|
|
12
|
+
|
|
13
|
+
## States
|
|
14
|
+
- **on-node** - This state is true when the entity is on a node.
|
|
15
|
+
- **moving** - This state is true when the entity is moving from one node to another.
|
|
16
|
+
- **going-[direction]** - This state is true when the entity is moving (or has just moved) in a direction (determined by the NodeMap) from one node to another.
|
|
17
|
+
|
|
18
|
+
## JSON Definition
|
|
19
|
+
{
|
|
20
|
+
"type": "NodeResident",
|
|
21
|
+
|
|
22
|
+
"nodeId": "city-hall",
|
|
23
|
+
// Optional. The id of the node that this entity should start on. Uses the entity's nodeId property if not set here.
|
|
24
|
+
|
|
25
|
+
"friendlyNodes": {"path": "walking", "sidewalk": "walking", "road": "driving"],
|
|
26
|
+
// Optional. This is a list of node types that this entity can reside on. If not set, entity can reside on any type of node.
|
|
27
|
+
|
|
28
|
+
"friendlyEntities": ['friends','neighbors','city-council-members'],
|
|
29
|
+
// Optional. This is a list of entities that this entity can reside with on the same node. If not set, this entity can reside with any entities on the same node.
|
|
30
|
+
|
|
31
|
+
"speed": 5,
|
|
32
|
+
// Optional. Sets the speed with which the entity moves along an edge to an adjacent node. Default is 0 (instantaneous movement).
|
|
33
|
+
|
|
34
|
+
"updateOrientation": true
|
|
35
|
+
// Optional. Determines whether the entity's orientation is updated by movement across the NodeMap. Default is false.
|
|
36
|
+
}
|
|
37
|
+
*/
|
|
38
|
+
import {arrayCache, greenSlice} from '../utils/array.js';
|
|
39
|
+
import createComponentClass from '../factory.js';
|
|
40
|
+
|
|
41
|
+
const
|
|
42
|
+
createGateway = function (nodeDefinition, map, gateway) {
|
|
43
|
+
return function () {
|
|
44
|
+
// ensure it's a node if one is available at this gateway
|
|
45
|
+
const
|
|
46
|
+
node = map.getNode(nodeDefinition);
|
|
47
|
+
|
|
48
|
+
if (this.isPassable(node)) {
|
|
49
|
+
this.destinationNodes.length = 0;
|
|
50
|
+
this.destinationNodes.push(node);
|
|
51
|
+
|
|
52
|
+
if (this.node) {
|
|
53
|
+
this.onEdge(node);
|
|
54
|
+
} else {
|
|
55
|
+
this.distance = 0;
|
|
56
|
+
}
|
|
57
|
+
this.progress = 0;
|
|
58
|
+
|
|
59
|
+
this.setState('going-' + gateway);
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return false;
|
|
64
|
+
};
|
|
65
|
+
},
|
|
66
|
+
distance = function (origin, destination) {
|
|
67
|
+
const
|
|
68
|
+
x = destination.x - origin.x,
|
|
69
|
+
y = destination.y - origin.y,
|
|
70
|
+
z = destination.z - origin.z;
|
|
71
|
+
|
|
72
|
+
return Math.sqrt(x * x + y * y + z * z);
|
|
73
|
+
},
|
|
74
|
+
angle = function (origin, destination, distance, ratio) {
|
|
75
|
+
if (origin.rotation && destination.rotation) {
|
|
76
|
+
const
|
|
77
|
+
x = (origin.rotation + 180) % 360,
|
|
78
|
+
y = (destination.rotation + 180) % 360;
|
|
79
|
+
|
|
80
|
+
return (x * (1 - ratio) + y * ratio + 180) % 360;
|
|
81
|
+
} else {
|
|
82
|
+
const
|
|
83
|
+
x = destination.x - origin.x,
|
|
84
|
+
y = destination.y - origin.y;
|
|
85
|
+
let a = 0;
|
|
86
|
+
|
|
87
|
+
if (!distance) {
|
|
88
|
+
return a;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
a = Math.acos(x / distance);
|
|
92
|
+
if (y < 0) {
|
|
93
|
+
a = (Math.PI * 2) - a;
|
|
94
|
+
}
|
|
95
|
+
return a * 180 / Math.PI;
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
axisProgress = function (r, o, d, f) {
|
|
99
|
+
return o * (1 - r) + d * r + f;
|
|
100
|
+
},
|
|
101
|
+
isFriendly = function (entities, kinds) {
|
|
102
|
+
|
|
103
|
+
if (kinds !== null) {
|
|
104
|
+
let found = false;
|
|
105
|
+
|
|
106
|
+
for (let x = 0; x < entities.length; x++) {
|
|
107
|
+
for (let y = 0; y < kinds.length; y++) {
|
|
108
|
+
if (entities[x].type === kinds[y]) {
|
|
109
|
+
found = true;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
if (!found) {
|
|
113
|
+
return false;
|
|
114
|
+
} else {
|
|
115
|
+
found = false;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return true;
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
export default createComponentClass(/** @lends platypus.components.NodeResident.prototype */{
|
|
124
|
+
|
|
125
|
+
id: 'NodeResident',
|
|
126
|
+
|
|
127
|
+
properties: {
|
|
128
|
+
algorithm: distance,
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* This sets the resident's initial node.
|
|
132
|
+
*
|
|
133
|
+
* @property node
|
|
134
|
+
* @type Object
|
|
135
|
+
* @default null
|
|
136
|
+
*/
|
|
137
|
+
node: null,
|
|
138
|
+
|
|
139
|
+
offset: {},
|
|
140
|
+
|
|
141
|
+
snapToNodes: false,
|
|
142
|
+
|
|
143
|
+
updateOrientation: false
|
|
144
|
+
},
|
|
145
|
+
|
|
146
|
+
publicProperties: {
|
|
147
|
+
friendlyNodes: null,
|
|
148
|
+
|
|
149
|
+
friendlyEntities: null,
|
|
150
|
+
|
|
151
|
+
nodeId: '',
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* This describes the rate at which a node resident should progress along an edge to another node. This property is set on the entity itself and can be manipulated in real-time.
|
|
155
|
+
*
|
|
156
|
+
* @property speed
|
|
157
|
+
* @type Number
|
|
158
|
+
* @default 0
|
|
159
|
+
*/
|
|
160
|
+
speed: 0
|
|
161
|
+
},
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* This component connects an entity to its parent's [[NodeMap]]. It manages navigating the NodeMap and triggering events on the entity related to its position.
|
|
165
|
+
*
|
|
166
|
+
* @memberof platypus.components
|
|
167
|
+
* @uses platypus.Component
|
|
168
|
+
* @constructs
|
|
169
|
+
* @listens platypus.Entity#handle-logic
|
|
170
|
+
* @fires platypus.Entity#in-location
|
|
171
|
+
*/
|
|
172
|
+
initialize: function () {
|
|
173
|
+
const
|
|
174
|
+
offset = this.offset,
|
|
175
|
+
startingNode = this.node;
|
|
176
|
+
|
|
177
|
+
this.neighbors = {};
|
|
178
|
+
|
|
179
|
+
this.distance = 0;
|
|
180
|
+
this.progress = 0;
|
|
181
|
+
this.offset = {
|
|
182
|
+
x: offset.x || 0,
|
|
183
|
+
y: offset.y || 0,
|
|
184
|
+
z: offset.z || 0
|
|
185
|
+
};
|
|
186
|
+
this.destinationNodes = arrayCache.setUp();
|
|
187
|
+
|
|
188
|
+
this.state = this.owner.state;
|
|
189
|
+
this.state.set('moving', false);
|
|
190
|
+
this.state.set('on-node', false);
|
|
191
|
+
this.currentState = '';
|
|
192
|
+
|
|
193
|
+
Object.defineProperty(this.owner, 'node', {
|
|
194
|
+
get: () => this.node,
|
|
195
|
+
set: (value) => {
|
|
196
|
+
if (value) {
|
|
197
|
+
this.getOnNode(value);
|
|
198
|
+
} else {
|
|
199
|
+
this.getOffNode();
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
this.owner.node = startingNode;
|
|
204
|
+
},
|
|
205
|
+
|
|
206
|
+
events: {
|
|
207
|
+
"set-algorithm": function (algorithm) {
|
|
208
|
+
this.algorithm = algorithm || distance;
|
|
209
|
+
},
|
|
210
|
+
"handle-logic": function (resp) {
|
|
211
|
+
let ratio = 0,
|
|
212
|
+
momentum = 0;
|
|
213
|
+
|
|
214
|
+
if (!this.owner.node) {
|
|
215
|
+
const
|
|
216
|
+
arr = arrayCache.setUp(this.owner.x, this.owner.y);
|
|
217
|
+
|
|
218
|
+
this.owner.node = this.owner.parent.getClosestNode(arr);
|
|
219
|
+
arrayCache.recycle(arr);
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* This event is triggered if the entity is placed on the map but not assigned a node. It is moved to the nearest node and "in-location" is triggered.
|
|
223
|
+
*
|
|
224
|
+
* @event platypus.Entity#in-location
|
|
225
|
+
* @param entity {platypus.Entity} The entity that is in location.
|
|
226
|
+
*/
|
|
227
|
+
this.owner.triggerEvent('in-location', this.owner);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (this.followEntity) {
|
|
231
|
+
const
|
|
232
|
+
node = this.followEntity.node || this.followEntity;
|
|
233
|
+
|
|
234
|
+
if (node && node.isNode && (node !== this.node)) {
|
|
235
|
+
this.lag = 0;
|
|
236
|
+
this.state.set('moving', this.gotoNode());
|
|
237
|
+
if (this.followDistance) {
|
|
238
|
+
momentum = this.lag;
|
|
239
|
+
}
|
|
240
|
+
} else {
|
|
241
|
+
this.followEntity = null;
|
|
242
|
+
}
|
|
243
|
+
} else {
|
|
244
|
+
momentum = this.speed * resp.delta;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// if goto-node was blocked, try again.
|
|
248
|
+
if (this.blocked) {
|
|
249
|
+
this.blocked = false;
|
|
250
|
+
if (this.goingToNode) {
|
|
251
|
+
this.owner.triggerEvent('goto-closest-node', this.goingToNode);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (this.destinationNodes.length) {
|
|
256
|
+
this.state.set('moving', (this.speed !== 0));
|
|
257
|
+
if (this.node) {
|
|
258
|
+
this.onEdge(this.destinationNodes[0]);
|
|
259
|
+
} else if (!this.lastNode) {
|
|
260
|
+
this.owner.node = this.destinationNodes[0];
|
|
261
|
+
this.destinationNodes.shift();
|
|
262
|
+
if (!this.destinationNodes.length) {
|
|
263
|
+
this.state.set('moving', false);
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (this.snapToNodes) {
|
|
269
|
+
for (let i = 0; i < this.destinationNodes.length; i++) {
|
|
270
|
+
this.owner.node = this.destinationNodes[i];
|
|
271
|
+
}
|
|
272
|
+
this.destinationNodes.length = 0;
|
|
273
|
+
} else {
|
|
274
|
+
while (this.destinationNodes.length && momentum) {
|
|
275
|
+
if ((this.progress + momentum) >= this.distance) {
|
|
276
|
+
const
|
|
277
|
+
node = this.destinationNodes[0];
|
|
278
|
+
|
|
279
|
+
momentum -= (this.distance - this.progress);
|
|
280
|
+
this.progress = 0;
|
|
281
|
+
this.destinationNodes.shift();
|
|
282
|
+
this.owner.node = node;
|
|
283
|
+
if (this.destinationNodes.length && momentum) {
|
|
284
|
+
this.onEdge(this.destinationNodes[0]);
|
|
285
|
+
}
|
|
286
|
+
} else {
|
|
287
|
+
this.progress += momentum;
|
|
288
|
+
ratio = this.progress / this.distance;
|
|
289
|
+
this.owner.x = axisProgress(ratio, this.lastNode.x, this.destinationNodes[0].x, this.offset.x);
|
|
290
|
+
this.owner.y = axisProgress(ratio, this.lastNode.y, this.destinationNodes[0].y, this.offset.y);
|
|
291
|
+
this.owner.z = axisProgress(ratio, this.lastNode.z, this.destinationNodes[0].z, this.offset.z);
|
|
292
|
+
if (this.updateOrientation) {
|
|
293
|
+
this.owner.rotation = angle(this.lastNode, this.destinationNodes[0], this.distance, ratio);
|
|
294
|
+
}
|
|
295
|
+
momentum = 0;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
} else {
|
|
300
|
+
this.state.set('moving', false);
|
|
301
|
+
}
|
|
302
|
+
},
|
|
303
|
+
"goto-node": function (node) {
|
|
304
|
+
this.gotoNode(node);
|
|
305
|
+
},
|
|
306
|
+
"follow": function (entityOrNode) {
|
|
307
|
+
if (entityOrNode.entity) {
|
|
308
|
+
this.followDistance = entityOrNode.distance;
|
|
309
|
+
this.followEntity = entityOrNode.entity;
|
|
310
|
+
} else {
|
|
311
|
+
this.followDistance = 0;
|
|
312
|
+
this.followEntity = entityOrNode;
|
|
313
|
+
}
|
|
314
|
+
},
|
|
315
|
+
"goto-closest-node": (function () {
|
|
316
|
+
const
|
|
317
|
+
checkList = (here, list) => list.indexOf(here) >= 0,
|
|
318
|
+
checkType = (here, type) => here.type === type,
|
|
319
|
+
checkObjectType = (here, node) => here.type === node.type;
|
|
320
|
+
|
|
321
|
+
return function (nodesOrNodeType) {
|
|
322
|
+
const
|
|
323
|
+
origin = this.node ?? this.lastNode,
|
|
324
|
+
test = (typeof nodesOrNodeType === 'string') ? checkType : (typeof nodesOrNodeType.type === 'string') ? checkObjectType : checkList,
|
|
325
|
+
steps = nodesOrNodeType.steps ?? 0;
|
|
326
|
+
|
|
327
|
+
this.goingToNode = nodesOrNodeType;
|
|
328
|
+
|
|
329
|
+
if (origin && nodesOrNodeType && !test(origin, nodesOrNodeType)) {
|
|
330
|
+
const
|
|
331
|
+
nodes = arrayCache.setUp(),
|
|
332
|
+
travResp = this.traverseNode({
|
|
333
|
+
depth: 20, //arbitrary limit
|
|
334
|
+
origin: origin,
|
|
335
|
+
position: origin,
|
|
336
|
+
test,
|
|
337
|
+
destination: nodesOrNodeType,
|
|
338
|
+
nodes: nodes,
|
|
339
|
+
shortestPath: Infinity,
|
|
340
|
+
distance: 0,
|
|
341
|
+
found: false,
|
|
342
|
+
algorithm: this.algorithm,
|
|
343
|
+
blocked: false
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
travResp.distance -= this.progress;
|
|
347
|
+
|
|
348
|
+
if (travResp.found) {
|
|
349
|
+
//TODO: should probably set this up apart from this containing function
|
|
350
|
+
if (this.followEntity) {
|
|
351
|
+
if (!this.followDistance) {
|
|
352
|
+
this.setPath(travResp, steps);
|
|
353
|
+
} else if ((travResp.distance + (this.followEntity.progress || 0)) > this.followDistance) {
|
|
354
|
+
this.lag = travResp.distance + (this.followEntity.progress || 0) - this.followDistance;
|
|
355
|
+
this.setPath(travResp, steps);
|
|
356
|
+
} else {
|
|
357
|
+
this.lag = 0;
|
|
358
|
+
}
|
|
359
|
+
} else {
|
|
360
|
+
this.setPath(travResp, steps);
|
|
361
|
+
}
|
|
362
|
+
} else if (travResp.blocked) {
|
|
363
|
+
this.blocked = true;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
arrayCache.recycle(nodes);
|
|
367
|
+
}
|
|
368
|
+
};
|
|
369
|
+
}()),
|
|
370
|
+
"set-directions": function () {
|
|
371
|
+
const
|
|
372
|
+
node = this.node,
|
|
373
|
+
nodeNeighbors = node.neighbors,
|
|
374
|
+
keys = Object.keys(nodeNeighbors),
|
|
375
|
+
{length} = keys;
|
|
376
|
+
|
|
377
|
+
this.owner.triggerEvent('remove-directions');
|
|
378
|
+
|
|
379
|
+
for (let i = 0; i < length; i++) {
|
|
380
|
+
const
|
|
381
|
+
key = keys[i],
|
|
382
|
+
nodeNeighbor = nodeNeighbors[key],
|
|
383
|
+
nextNode = node.map.getNode(nodeNeighbor),
|
|
384
|
+
gateway = this.neighbors[key] = createGateway(nodeNeighbor, node.map, i);
|
|
385
|
+
|
|
386
|
+
this.addEventListener(key, gateway);
|
|
387
|
+
|
|
388
|
+
//trigger "next-to" events
|
|
389
|
+
if (nextNode) {
|
|
390
|
+
const
|
|
391
|
+
entities = nextNode.contains;
|
|
392
|
+
|
|
393
|
+
for (let j = 0; j < entities.length; j++) {
|
|
394
|
+
entities[j].triggerEvent("next-to-" + this.owner.type, this.owner);
|
|
395
|
+
this.owner.triggerEvent("next-to-" + entities[j].type, entities[j]);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
},
|
|
400
|
+
"remove-directions": function () {
|
|
401
|
+
const
|
|
402
|
+
neighbors = this.neighbors,
|
|
403
|
+
keys = Object.keys(neighbors),
|
|
404
|
+
{length} = keys;
|
|
405
|
+
|
|
406
|
+
for (let i = 0; i < length; i++) {
|
|
407
|
+
const
|
|
408
|
+
key = keys[i];
|
|
409
|
+
|
|
410
|
+
this.removeEventListener(key, neighbors[key]);
|
|
411
|
+
delete neighbors[key];
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
},
|
|
415
|
+
|
|
416
|
+
methods: {
|
|
417
|
+
gotoNode (node) {
|
|
418
|
+
const
|
|
419
|
+
origin = this.node ?? this.lastNode;
|
|
420
|
+
let moving = false;
|
|
421
|
+
|
|
422
|
+
if (!node && this.followEntity) {
|
|
423
|
+
node = this.followEntity.node ?? this.followEntity.lastNode ?? this.followEntity;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
if (origin && node && (this.node !== node)) {
|
|
427
|
+
const
|
|
428
|
+
nodes = arrayCache.setUp(),
|
|
429
|
+
travResp = this.traverseNode({
|
|
430
|
+
depth: 20, //arbitrary limit
|
|
431
|
+
origin,
|
|
432
|
+
position: origin,
|
|
433
|
+
test: (here, there) => here === there,
|
|
434
|
+
destination: node,
|
|
435
|
+
nodes,
|
|
436
|
+
shortestPath: Infinity,
|
|
437
|
+
distance: 0,
|
|
438
|
+
found: false,
|
|
439
|
+
algorithm: this.algorithm,
|
|
440
|
+
blocked: false
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
travResp.distance -= this.progress;
|
|
444
|
+
|
|
445
|
+
if (travResp.found) {
|
|
446
|
+
//TODO: should probably set this up apart from this containing function
|
|
447
|
+
if (this.followEntity) {
|
|
448
|
+
if (!this.followDistance) {
|
|
449
|
+
this.setPath(travResp);
|
|
450
|
+
moving = true;
|
|
451
|
+
} else if ((travResp.distance + (this.followEntity.progress || 0)) > this.followDistance) {
|
|
452
|
+
this.lag = travResp.distance + (this.followEntity.progress || 0) - this.followDistance;
|
|
453
|
+
this.setPath(travResp);
|
|
454
|
+
moving = true;
|
|
455
|
+
} else {
|
|
456
|
+
this.lag = 0;
|
|
457
|
+
}
|
|
458
|
+
} else {
|
|
459
|
+
this.setPath(travResp);
|
|
460
|
+
moving = true;
|
|
461
|
+
}
|
|
462
|
+
} else if (travResp.blocked) {
|
|
463
|
+
this.blocked = true;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
arrayCache.recycle(nodes);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
return moving;
|
|
470
|
+
},
|
|
471
|
+
|
|
472
|
+
getOnNode (node) {
|
|
473
|
+
const
|
|
474
|
+
entities = node.contains;
|
|
475
|
+
|
|
476
|
+
this.getOffNode();
|
|
477
|
+
this.node = node;
|
|
478
|
+
if (this.node?.removeFromEdge) {
|
|
479
|
+
this.node.removeFromEdge(this.owner);
|
|
480
|
+
}
|
|
481
|
+
if (this.lastNode?.removeFromEdge) {
|
|
482
|
+
this.lastNode.removeFromEdge(this.owner);
|
|
483
|
+
}
|
|
484
|
+
this.node.addToNode(this.owner);
|
|
485
|
+
|
|
486
|
+
this.setState('on-node');
|
|
487
|
+
|
|
488
|
+
this.owner.x = this.node.x + this.offset.x;
|
|
489
|
+
this.owner.y = this.node.y + this.offset.y;
|
|
490
|
+
this.owner.z = this.node.z + this.offset.z;
|
|
491
|
+
if (this.updateOrientation && this.node.rotation) {
|
|
492
|
+
this.owner.rotation = this.node.rotation;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
//add listeners for directions
|
|
496
|
+
this.owner.triggerEvent('set-directions');
|
|
497
|
+
|
|
498
|
+
//trigger mapped messages for node types
|
|
499
|
+
if (this.friendlyNodes && this.friendlyNodes[node.type]) {
|
|
500
|
+
this.owner.trigger(this.friendlyNodes[node.type], node);
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
//trigger "with" events
|
|
504
|
+
for (let j = 0; j < entities.length; j++) {
|
|
505
|
+
if (this.owner !== entities[j]) {
|
|
506
|
+
entities[j].triggerEvent("with-" + this.owner.type, this.owner);
|
|
507
|
+
this.owner.triggerEvent("with-" + entities[j].type, entities[j]);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
this.owner.triggerEvent('on-node', node);
|
|
512
|
+
},
|
|
513
|
+
|
|
514
|
+
getOffNode: function () {
|
|
515
|
+
if (this.node) {
|
|
516
|
+
this.node.removeFromNode(this.owner);
|
|
517
|
+
this.owner.triggerEvent('left-node', this.node);
|
|
518
|
+
this.owner.triggerEvent('remove-directions');
|
|
519
|
+
}
|
|
520
|
+
this.lastNode = this.node;
|
|
521
|
+
this.node = null;
|
|
522
|
+
},
|
|
523
|
+
|
|
524
|
+
isPassable: function (node) {
|
|
525
|
+
return node && (this.node !== node) && (!this.friendlyNodes || (typeof this.friendlyNodes[node.type] !== 'undefined')) && (!node.contains.length || isFriendly(node.contains, this.friendlyEntities));
|
|
526
|
+
},
|
|
527
|
+
traverseNode: function (record) {
|
|
528
|
+
//TODO: may want to make this use A*. Currently node traversal order is arbitrary and essentially searches entire graph, but does clip out paths that are too long.
|
|
529
|
+
const
|
|
530
|
+
{position, test} = record;
|
|
531
|
+
|
|
532
|
+
if ((record.depth === 0) || (record.distance > record.shortestPath)) {
|
|
533
|
+
// if we've reached our search depth or are following a path longer than our recorded successful distance, bail
|
|
534
|
+
return record;
|
|
535
|
+
} else if (test(position, record.destination)) {
|
|
536
|
+
// if we've reached our destination, set shortest path information and bail.
|
|
537
|
+
record.found = true;
|
|
538
|
+
record.shortestPath = record.distance;
|
|
539
|
+
return record;
|
|
540
|
+
} else if (record.nodes.includes(position, 1)) { //Make sure we do not trace an infinite node loop.
|
|
541
|
+
return record;
|
|
542
|
+
} else {
|
|
543
|
+
const
|
|
544
|
+
algorithm = record.algorithm || distance,
|
|
545
|
+
map = position.map,
|
|
546
|
+
neighbors = position.neighbors,
|
|
547
|
+
keys = Object.keys(neighbors),
|
|
548
|
+
{length} = keys,
|
|
549
|
+
savedResp = {
|
|
550
|
+
shortestPath: Infinity,
|
|
551
|
+
found: false,
|
|
552
|
+
blocked: false
|
|
553
|
+
};
|
|
554
|
+
let blocked = true,
|
|
555
|
+
hasNeighbor = false;
|
|
556
|
+
|
|
557
|
+
for (let i = 0; i < length; i++) {
|
|
558
|
+
const
|
|
559
|
+
key = keys[i],
|
|
560
|
+
node = map.getNode(neighbors[key]);
|
|
561
|
+
|
|
562
|
+
hasNeighbor = true;
|
|
563
|
+
|
|
564
|
+
if (this.isPassable(node)) {
|
|
565
|
+
const
|
|
566
|
+
nodeList = [
|
|
567
|
+
...record.nodes,
|
|
568
|
+
node
|
|
569
|
+
],
|
|
570
|
+
resp = this.traverseNode({
|
|
571
|
+
depth: record.depth - 1,
|
|
572
|
+
origin: record.origin,
|
|
573
|
+
position: node,
|
|
574
|
+
destination: record.destination,
|
|
575
|
+
test,
|
|
576
|
+
algorithm: algorithm,
|
|
577
|
+
nodes: nodeList,
|
|
578
|
+
shortestPath: record.shortestPath,
|
|
579
|
+
distance: record.distance + algorithm(position, node),
|
|
580
|
+
gateway: record.gateway || key,
|
|
581
|
+
found: false,
|
|
582
|
+
blocked: false
|
|
583
|
+
});
|
|
584
|
+
|
|
585
|
+
if (resp.found && (savedResp.shortestPath > resp.shortestPath)) {
|
|
586
|
+
savedResp = resp;
|
|
587
|
+
}
|
|
588
|
+
blocked = false;
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
savedResp.blocked = (hasNeighbor && blocked);
|
|
592
|
+
return savedResp;
|
|
593
|
+
}
|
|
594
|
+
},
|
|
595
|
+
setPath: function (resp, steps) {
|
|
596
|
+
if (resp.nodes[0] === this.node) {
|
|
597
|
+
resp.nodes.shift();
|
|
598
|
+
}
|
|
599
|
+
arrayCache.recycle(this.destinationNodes);
|
|
600
|
+
this.destinationNodes = greenSlice(resp.nodes);
|
|
601
|
+
if (steps) {
|
|
602
|
+
this.destinationNodes.length = Math.min(steps, this.destinationNodes.length);
|
|
603
|
+
}
|
|
604
|
+
},
|
|
605
|
+
setState: function (state) {
|
|
606
|
+
if (state === 'on-node') {
|
|
607
|
+
this.state.set('on-node', true);
|
|
608
|
+
} else {
|
|
609
|
+
this.state.set('on-node', false);
|
|
610
|
+
if (this.currentState) {
|
|
611
|
+
this.state.set(this.currentState, false);
|
|
612
|
+
}
|
|
613
|
+
this.currentState = state;
|
|
614
|
+
this.state.set(state, true);
|
|
615
|
+
}
|
|
616
|
+
},
|
|
617
|
+
onEdge: function (toNode) {
|
|
618
|
+
this.distance = distance(this.node, toNode);
|
|
619
|
+
if (this.updateOrientation) {
|
|
620
|
+
this.owner.rotation = angle(this.node, toNode, this.distance, this.progress / this.distance);
|
|
621
|
+
}
|
|
622
|
+
if (this.node.addToEdge) {
|
|
623
|
+
this.node.addToEdge(this.owner);
|
|
624
|
+
}
|
|
625
|
+
if (toNode.addToEdge) {
|
|
626
|
+
toNode.addToEdge(this.owner);
|
|
627
|
+
}
|
|
628
|
+
this.owner.node = null;
|
|
629
|
+
},
|
|
630
|
+
destroy: function () {
|
|
631
|
+
arrayCache.recycle(this.destinationNodes);
|
|
632
|
+
this.destinationNodes = null;
|
|
633
|
+
this.state = null;
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
});
|