dcl-npc-toolkit 1.2.1 → 1.2.2-20240115125339.commit-cf7e18d

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.
Files changed (2) hide show
  1. package/README.md +355 -1477
  2. package/package.json +2 -2
package/README.md CHANGED
@@ -1,74 +1,21 @@
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
1
  # NPC-library
9
2
 
10
-
11
-
12
-
13
-
14
-
15
-
16
3
  A collection of tools for creating Non-Player-Characters (NPCs). These are capable of having conversations with the player, and play different animations.
17
4
 
18
-
19
-
20
-
21
-
22
-
23
-
24
5
  Capabilities of the NPCs in this library:
25
6
 
26
-
27
-
28
-
29
-
30
-
31
-
32
7
  - Start a conversation when clicked or when walking near
33
8
 
34
-
35
-
36
-
37
-
38
9
  - Trigger any action when clicked or when walking near
39
10
 
40
-
41
-
42
-
43
-
44
11
  - Trigger any action when the player walks away
45
12
 
46
-
47
-
48
-
49
-
50
13
  - Turn around slowly to always face the player
51
14
 
52
-
53
-
54
-
55
-
56
15
  - Play an animation in the NPC 3d model, optionally returning to loop the idle animation afterwards
57
16
 
58
-
59
-
60
-
61
-
62
-
63
-
64
17
  The dialog messages can also require that the player chooses options, and any action can be triggered when the player picks an option or advances past a message.
65
18
 
66
-
67
-
68
-
69
-
70
-
71
-
72
19
  ## Install the library
73
20
 
74
21
  ## Via the Decentraland Editor
@@ -84,20 +31,26 @@ Follow the steps in [Manage Dependencies](https://docs.decentraland.org/creator/
84
31
  4. Import the library into the scene's script. Add this line at the start of your `index.ts` file, or any other TypeScript files that require it:
85
32
 
86
33
  ```ts
87
- import * as npc from 'dcl-npc-toolkit'
34
+ import * as npc from 'dcl-npc-toolkit'
88
35
  ```
89
- 5. In your TypeScript file, call the `create` function passing it a `TransformType` and a `NPCData` object. The `NPCData` object requires a minimum of a `NPCType` and a function to trigger when the NPC is activated:
90
36
 
37
+ 5. In your TypeScript file, call the `create` function passing it a `TransformType` and a `NPCData` object. The `NPCData` object requires a minimum of a `NPCType` and a function to trigger when the NPC is activated:
91
38
 
92
39
  ```ts
93
- export function main(){
94
- export let myNPC = npc.create(
95
- {position: Vector3.create(8,0,8),rotation:Quaternion.Zero(), scale: Vector3.create(1,1,1)},
40
+ export function main() {
41
+ export let myNPC = npc.create(
42
+ {
43
+ position: Vector3.create(8, 0, 8),
44
+ rotation: Quaternion.Zero(),
45
+ scale: Vector3.create(1, 1, 1),
46
+ },
96
47
  //NPC Data Object
97
48
  {
98
- type: npc.NPCType.CUSTOM,
99
- model: 'models/npc.glb',
100
- onActivate:()=>{console.log('npc activated');}
49
+ type: npc.NPCType.CUSTOM,
50
+ model: 'models/npc.glb',
51
+ onActivate: () => {
52
+ console.log('npc activated')
53
+ },
101
54
  }
102
55
  )
103
56
  }
@@ -105,293 +58,151 @@ export function main(){
105
58
 
106
59
  6. Write a dialog script for your character, preferably on a separate file, making it of type `Dialog[]`.
107
60
 
108
-
109
61
  ```ts
110
- import { Dialog } from 'dcl-npc-toolkit'
62
+ import { Dialog } from 'dcl-npc-toolkit'
111
63
 
112
- export let ILoveCats: Dialog[] = [
64
+ export let ILoveCats: Dialog[] = [
113
65
  {
114
- text: `I really lo-ove cats`,
115
- isEndOfDialog: true
116
- }
66
+ text: `I really lo-ove cats`,
67
+ isEndOfDialog: true,
68
+ },
117
69
  ]
118
70
  ```
119
-
71
+
120
72
  ## Via the CLI
121
-
122
73
 
123
74
  1. Install the library as an npm bundle. Run this command in your scene's project folder:
124
75
 
125
-
126
-
127
-
128
-
129
-
130
-
131
76
  ```ts
132
77
  npm i dcl-npc-toolkit
133
78
  ```
134
79
 
135
-
136
-
137
-
138
-
139
80
  2. Install the dependent sdk utils library as an npm bundle. Run this command in your scene's project folder:
140
81
 
141
-
142
-
143
-
144
-
145
-
146
-
147
82
  ```
148
83
  npm i @dcl-sdk/utils -B
149
84
  ```
150
85
 
151
-
152
-
153
-
154
-
155
86
  3. Run `dcl start` or `dcl build` so the dependencies are correctly installed.
156
87
 
157
-
158
-
159
-
160
-
161
-
162
-
163
88
  4. Import the library into the scene's script. Add this line at the start of your `index.ts` file, or any other TypeScript files that require it:
164
89
 
165
-
166
-
167
-
168
-
169
-
170
-
171
90
  ```ts
172
- import * as npc from 'dcl-npc-toolkit'
91
+ import * as npc from 'dcl-npc-toolkit'
173
92
  ```
174
93
 
175
-
176
-
177
-
178
-
179
-
180
-
181
94
  5. In your TypeScript file, call the `create` function passing it a `TransformType` and a `NPCData` object. The `NPCData` object requires a minimum of a `NPCType` and a function to trigger when the NPC is activated:
182
95
 
183
-
184
-
185
-
186
-
187
-
188
-
189
96
  ```ts
190
- export let myNPC = npc.create(
191
- {position: Vector3.create(8,0,8),rotation:Quaternion.Zero(), scale: Vector3.create(1,1,1)},
97
+ export let myNPC = npc.create(
98
+ {
99
+ position: Vector3.create(8, 0, 8),
100
+ rotation: Quaternion.Zero(),
101
+ scale: Vector3.create(1, 1, 1),
102
+ },
192
103
  //NPC Data Object
193
104
  {
194
- type: npc.NPCType.CUSTOM,
195
- model: 'models/npc.glb',
196
- onActivate:()=>{console.log('npc activated');}
105
+ type: npc.NPCType.CUSTOM,
106
+ model: 'models/npc.glb',
107
+ onActivate: () => {
108
+ console.log('npc activated')
109
+ },
197
110
  }
198
111
  )
199
112
  ```
200
113
 
201
-
202
-
203
-
204
-
205
-
206
-
207
114
  6. Write a dialog script for your character, preferably on a separate file, making it of type `Dialog[]`.
208
115
 
209
-
210
-
211
-
212
-
213
-
214
-
215
116
  ```ts
216
- import { Dialog } from 'dcl-npc-toolkit'
117
+ import { Dialog } from 'dcl-npc-toolkit'
217
118
 
218
- export let ILoveCats: Dialog[] = [
119
+ export let ILoveCats: Dialog[] = [
219
120
  {
220
- text: `I really lo-ove cats`,
221
- isEndOfDialog: true
222
- }
121
+ text: `I really lo-ove cats`,
122
+ isEndOfDialog: true,
123
+ },
223
124
  ]
224
125
  ```
225
126
 
226
-
227
-
228
-
229
-
230
-
231
-
232
127
  ## NPC Default Behavior
233
128
 
234
-
235
-
236
-
237
-
238
-
239
-
240
129
  NPCs at the very least must have:
241
130
 
242
-
243
-
244
-
245
-
246
-
247
-
248
- - `position`: (_TransformType_) Must include position, rotation and scale.
249
-
250
-
251
-
252
-
253
-
254
- - `NPCData`: (_Data Object_) with a minimum of two variables
131
+ - `position`: (_TransformType_) Must include position, rotation and scale.
255
132
 
256
-
133
+ - `NPCData`: (_Data Object_) with a minimum of two variables
257
134
 
258
-
135
+ - `type`: (_NPCType_) you have the choice to use a custom GLB object or an `AvatarShape` for your npc
259
136
 
260
- - `type`: (_NPCType_) you have the choice to use a custom GLB object or an `AvatarShape` for your npc
137
+ - `NPCType.CUSTOM`
261
138
 
262
-
139
+ - `NPCType.AVATAR`
263
140
 
264
-
141
+ - `onActivate()`: (_()=> void_) A function to call when the NPC is activated.
265
142
 
266
- - `NPCType.CUSTOM`
143
+ _if you decide to use a `NPCType.CUSTOM` GLB model for your avatar, you must pass in a model object inside the `NPCData`_
267
144
 
268
-
269
-
270
-
271
-
272
- - `NPCType.AVATAR`
273
-
274
-
275
-
276
-
277
-
278
- - `onActivate()`: (_()=> void_) A function to call when the NPC is activated.
279
-
280
-
281
-
282
-
283
-
284
-
285
-
286
- *if you decide to use a `NPCType.CUSTOM` GLB model for your avatar, you must pass in a model object inside the `NPCData`*
287
-
288
-
289
-
290
-
291
-
292
- - `model`: (_string_) The path to a 3D model
293
-
294
-
295
-
296
-
297
-
298
-
145
+ - `model`: (_string_) The path to a 3D model
299
146
 
300
147
  ```ts
301
- export let myNPC = npc.create({position: Vector3.create(8,0,8),rotation:Quaternion.Zero(), scale: Vector3.create(1,1,1)},
148
+ export let myNPC = npc.create(
149
+ {
150
+ position: Vector3.create(8, 0, 8),
151
+ rotation: Quaternion.Zero(),
152
+ scale: Vector3.create(1, 1, 1),
153
+ },
302
154
 
303
- //NPC Data Object
304
- {
305
- type: npc.NPCType.CUSTOM,
306
- model: 'models/npc.glb',
307
- onActivate:()=>{console.log('npc activated');}
308
- }
155
+ //NPC Data Object
156
+ {
157
+ type: npc.NPCType.CUSTOM,
158
+ model: 'models/npc.glb',
159
+ onActivate: () => {
160
+ console.log('npc activated')
161
+ },
162
+ }
309
163
  )
310
164
  ```
311
165
 
312
-
313
-
314
-
315
-
316
-
317
-
318
166
  With this default configuration, the NPC behaves in the following way:
319
167
 
320
-
321
-
322
-
323
-
324
-
325
-
326
168
  - The `onActivate()` function is called when pressing E on the NPC, and when the player walks near at a distance of 6 meters.
327
169
 
328
-
329
-
330
-
331
-
332
170
  - Once activated, there's a cooldown period of 5 seconds, that prevents the NPC to be activated again.
333
171
 
334
-
335
-
336
-
337
-
338
172
  - After walking away from the NPC, if its dialog window was open it will be closed, and if the NPC was rotating to follow the player it will stop.
339
173
 
340
-
341
-
342
-
343
-
344
174
  - If the NPC already has an open dialog window, clicking on the NPC won't do anything, to prevent accidentally clicking on it while flipping through the conversation.
345
175
 
346
-
347
-
348
-
349
-
350
176
  - If the NPC has an animation named 'Idle', it will play it in a loop. If other non-looping animations are played, it will return to looping the 'Idle' animation after the indicated duration.
351
177
 
352
-
353
-
354
-
355
-
356
-
357
-
358
178
  Many of these behaviors can be overridden or tweaked with the exposed properties.
359
179
 
360
-
361
-
362
180
  ## SDK7 UI
363
181
 
364
182
  With sdk7, there are new ways to implement similar features from sdk6, one of them being the way 2D UI objects get created. To add the NPC dialogs to your sdk7 2D UI:
365
183
 
366
- - create a variable to hold *all* of your 2D UI objects
184
+ - create a variable to hold _all_ of your 2D UI objects
367
185
 
368
186
  - import the NPC UI from the library and add the React object to your scene UI tree
369
187
 
370
188
  - create a function to be called once to render all of your 2D UI objects
371
189
 
372
-
373
-
374
190
  ```ts
375
- import ReactEcs, { Label, ReactEcsRenderer, UiEntity } from '@dcl/sdk/react-ecs'
376
- import { NpcUtilsUi } from 'dcl-npc-toolkit'
377
-
378
- const SceneOwnedUi = () =>
379
-
380
- <UiEntity>
381
- <NpcUtilsUi />
382
-
383
- { /* rest of user defined UI */ }
384
- </UiEntity>
385
-
386
-
191
+ import ReactEcs, { Label, ReactEcsRenderer, UiEntity } from '@dcl/sdk/react-ecs'
192
+ import { NpcUtilsUi } from 'dcl-npc-toolkit'
193
+
194
+ const SceneOwnedUi = () => (
195
+ <UiEntity>
196
+ <NpcUtilsUi />
197
+ {/* rest of user defined UI */}
198
+ </UiEntity>
199
+ )
387
200
 
388
- export function setupUi() {
201
+ export function setupUi() {
389
202
  ReactEcsRenderer.setUiRenderer(SceneOwnedUi)
390
203
  }
391
-
392
204
  ```
393
205
 
394
-
395
206
  Note: The UI drawn by this library library requires fetching images from an external URL. For the scene to allow you to do this, you must include the `ALLOW_MEDIA_HOSTNAMES` scene permission and add `decentraland.org` to the list of allowed domains in your `scene.json` file. Learn more about [image permissions](https://docs.decentraland.org/creator/development-guide/sdk7/materials/#textures-from-an-external-url).
396
207
 
397
208
  ```json
@@ -403,1237 +214,497 @@ Note: The UI drawn by this library library requires fetching images from an exte
403
214
  ],
404
215
  ```
405
216
 
406
-
407
-
408
217
  ## NPC Additional Properties
409
218
 
410
-
411
-
412
-
413
-
414
-
415
-
416
219
  To configure other properties of an NPC, add a fourth argument as an `NPCData` object. This object can have the following optional properties:
417
220
 
418
-
221
+ - `idleAnim`: _(string)_ Name of the idle animation in the model. This animation is always looped. After playing a non-looping animation it returns to looping this one.
419
222
 
420
-
223
+ - `faceUser`: _(boolean)_ Set if the NPC rotates to face the user while active.
421
224
 
422
-
225
+ - `dialogSound`: _(string)_ Path to sound file to play once for every entry shown on the UI. If the dialog entry being shown has an `audio` field, the NPC will play the file referenced by the `audio` field instead.
423
226
 
424
- - `idleAnim`: _(string)_ Name of the idle animation in the model. This animation is always looped. After playing a non-looping animation it returns to looping this one.
227
+ - `coolDownDuration`: _(number)_ Change the cooldown period for activating the NPC again. The number is in seconds.
425
228
 
426
-
229
+ - `hoverText`: _(string)_ Set the UI hover feedback when pointing the cursor at the NPC. _TALK_ by default.
427
230
 
428
-
231
+ - `onlyClickTrigger`: _(boolean)_ If true, the NPC can't be activated by walking near. Just by clicking on it or calling its `activate()` function.
429
232
 
430
- - `faceUser`: _(boolean)_ Set if the NPC rotates to face the user while active.
233
+ - `onlyETrigger`: _(boolean)_ If true, the NPC can't be activated by walking near. Just by pressing the E key on it or calling its `activate()` function.
431
234
 
432
-
235
+ - `onlyExternalTrigger`: _(boolean)_ If true, the NPC can't be activated by clicking, pressing E, or walking near. Just by calling its `activate()` function.
433
236
 
434
-
237
+ - `reactDistance`: _(number)_ Radius in meters for the player to activate the NPC or trigger the `onWalkAway()` function when leaving the radius.
435
238
 
436
- - `dialogSound`: _(string)_ Path to sound file to play once for every entry shown on the UI. If the dialog entry being shown has an `audio` field, the NPC will play the file referenced by the `audio` field instead.
239
+ - `continueOnWalkAway`: _(boolean)_ If true,when the player walks out of the `reactDistance` radius, the dialog window stays open and the NPC keeps turning to face the player (if applicable). It doesn't affect the triggering of the `onWalkAway()` function.
437
240
 
438
-
241
+ - `onWalkAway`: (_()=> void_) Function to call every time the player walks out of the `reactDistance` radius.
439
242
 
440
-
243
+ - `walkingAnim`: _(string)_ Name of the walking animation on the model. This animation is looped when calling the `followPath()` function.
441
244
 
442
- - `coolDownDuration`: _(number)_ Change the cooldown period for activating the NPC again. The number is in seconds.
245
+ - `walkingSpeed`: _(number)_ Speed of the NPC when walking. By default _2_.
443
246
 
444
-
247
+ - `path`: _(Vector3)_ Default path to walk. If a value is provided for this field on NPC initialization, the NPC will walk over this path in loop from the start.
445
248
 
446
-
249
+ - `bubbleHeight`: _(number)_ The height at which to display the speech bubble above the head of the NPC.
447
250
 
448
- - `hoverText`: _(string)_ Set the UI hover feedback when pointing the cursor at the NPC. _TALK_ by default.
251
+ - `textBubble`: _(boolean)_ If true, NPC starts with a speech bubble object ready to be accessed from the start. Otherwise, they text bubble is only built on the first call to `talkBubble()` on the NPC.
449
252
 
450
-
253
+ - `noUI`: _(boolean)_ If true, no UI object is built for UI dialogs for this NPC. This may help optimize the scene if this feature is not used.
451
254
 
452
-
453
-
454
- - `onlyClickTrigger`: _(boolean)_ If true, the NPC can't be activated by walking near. Just by clicking on it or calling its `activate()` function.
455
-
456
-
255
+ ```ts
256
+ export let myNPC = npc.create(
257
+ {
258
+ position: Vector3.create(8, 0, 8),
259
+ rotation: Quaternion.Zero(),
260
+ scale: Vector3.create(1, 1, 1),
261
+ },
262
+ //NPC Data Object
263
+ {
264
+ type: npc.NPCType.CUSTOM,
265
+ model: 'models/npc.glb',
266
+ onActivate: () => {
267
+ console.log('npc activated')
268
+ },
269
+ onWalkAway: () => {
270
+ console.log('test on walk away function')
271
+ },
272
+ faceUser: true,
273
+ reactDistance: 3,
274
+ idleAnim: 'idle1',
275
+ walkingAnim: 'walk1',
276
+ hoverText: 'Activate',
277
+ continueOnWalkAway: true,
278
+ onlyClickTrigger: false,
279
+ onlyExternalTrigger: false,
280
+ }
281
+ )
282
+ ```
457
283
 
458
-
284
+ ## Get NPC Data
459
285
 
460
- - `onlyETrigger`: _(boolean)_ If true, the NPC can't be activated by walking near. Just by pressing the E key on it or calling its `activate()` function.
286
+ ```ts
287
+ npc.getData(myNPC)
288
+ ```
461
289
 
462
-
290
+ There are several properties you can check on an NPC to know what its current state is:
463
291
 
464
-
292
+ - `.state`: An enum value of type `NPCState`. Supported values are `NPCState.STANDING` (default), `NPCState.TALKING`, and `NPCState.FOLLOWPATH`. `TALKING` is applied when the dialog window is opened, and set back to `STANDING` when the window is closed. `FOLLOWPATH` is applied when the NPC starts walking, and set back to `STANDING` when the NPC finishes its path or is stopped.
465
293
 
466
- - `onlyExternalTrigger`: _(boolean)_ If true, the NPC can't be activated by clicking, pressing E, or walking near. Just by calling its `activate()` function.
294
+ - `.introduced`: Boolean, false by default. Set to true if the NPC has spoken to the player at least once in this session.
467
295
 
468
-
296
+ - `.visible`: Returns a Boolean, false by default. True if the dialog window for this NPC is currently open.
469
297
 
470
-
298
+ - `.inCooldown`: Boolean, false by default. True if the NPC was recently activated and it's now in cooldown. The NPC won't respond to being activated till `inCooldown` is false.
471
299
 
472
- - `reactDistance`: _(number)_ Radius in meters for the player to activate the NPC or trigger the `onWalkAway()` function when leaving the radius.
300
+ > TIP: If you want to force an activation of the NPC in spite of the `inCooldown` value, you can force this value to true before activating.
473
301
 
474
-
302
+ ## NPC Callable Actions
475
303
 
476
-
304
+ An NPC object has several callable functions that come with the class:
477
305
 
478
- - `continueOnWalkAway`: _(boolean)_ If true,when the player walks out of the `reactDistance` radius, the dialog window stays open and the NPC keeps turning to face the player (if applicable). It doesn't affect the triggering of the `onWalkAway()` function.
306
+ ### Talk
479
307
 
480
-
308
+ To start a conversation with the NPC using the dialog UI, call the `talk()` function. The function takes the following **required** parameter:
481
309
 
482
-
310
+ - `script`: _(Dialog[])_ This array contains the information to manage the conversation, including events that may be triggered, options to choose, etc.
483
311
 
484
- - `onWalkAway`: (_()=> void_) Function to call every time the player walks out of the `reactDistance` radius.
312
+ It can also take the following optional parameters:
485
313
 
486
-
314
+ - `startIndex`: _(number | string)_ The _Dialog_ object from the `script` array to open first. By default this is _0_, the first element of the array. Pass a number to open the entry on a given array position, or pass a string to open the entry with a `name` property matching that string.
487
315
 
488
-
316
+ - `duration`: _(number)_ Number of seconds to wait before closing the dialog window. If no value is set, the window is kept open till the player reaches the end of the conversation or something else closes it.
489
317
 
490
- - `walkingAnim`: _(string)_ Name of the walking animation on the model. This animation is looped when calling the `followPath()` function.
318
+ ```ts
319
+ npc.talk(myNPC, myScript, 0)
320
+ ```
491
321
 
492
-
322
+ Learn how to build a script object for NPCs in a section below.
493
323
 
494
-
324
+ ### Play Animations
495
325
 
496
- - `walkingSpeed`: _(number)_ Speed of the NPC when walking. By default _2_.
326
+ By default, the NPC will loop an animation named 'Idle', or with a name passed in the `idleAnim` parameter.
497
327
 
498
-
328
+ Make the NPC play another animation by calling the `playAnimation()` function. The function takes the following **required** parameter:
499
329
 
500
-
330
+ - `animationName`: _(string)_ The name of the animation to play.
501
331
 
502
- - `path`: _(Vector3)_ Default path to walk. If a value is provided for this field on NPC initialization, the NPC will walk over this path in loop from the start.
332
+ It can also take the following optional parameters:
503
333
 
504
-
334
+ - `noLoop`: _(boolean)_ If true, plays the animation just once. Otherwise, the animation is looped.
505
335
 
506
- - `bubbleHeight`: _(number)_ The height at which to display the speech bubble above the head of the NPC.
336
+ - `duration`: _(number)_ Specifies the duration in seconds of the animation. When finished, it returns to playing the idle animation.
507
337
 
508
-
338
+ > Note: If `noLoop` is true but no `duration` is set, the model will stay still after playing the animation instead of returning to the idle animation.
509
339
 
510
- - `textBubble`: _(boolean)_ If true, NPC starts with a speech bubble object ready to be accessed from the start. Otherwise, they text bubble is only built on the first call to `talkBubble()` on the NPC.
340
+ ```ts
341
+ npc.playAnimation(myNPC, `Head_Yes`, true, 2.63)
342
+ ```
511
343
 
512
-
344
+ ### Change idle animation
513
345
 
514
-
346
+ The NPC's idle animation is looped by default whenever the NPC is not playing any other animations. In some cases you may want to have different idle animations depending on the circumstances, like while in a conversation, or if the NPC changes its general attitude after some event.
515
347
 
516
- - `noUI`: _(boolean)_ If true, no UI object is built for UI dialogs for this NPC. This may help optimize the scene if this feature is not used.
348
+ You set the NPC's idle animation when creating the NPC, using the `idleAnim` field. To change this animation at some later time, use `changeIdleAnim()`.
517
349
 
518
-
350
+ The `changeIdleAnim()` function takes two arguments:
519
351
 
520
-
352
+ - `animation`: The name of the new animation to set as the idle animation
521
353
 
522
-
354
+ - `play`: Optionally pass this value as _true_ if you want this new animation to start playing right away.
523
355
 
524
356
  ```ts
525
- export let myNPC = npc.create({position: Vector3.create(8,0,8),rotation:Quaternion.Zero(), scale: Vector3.create(1,1,1)},
526
- //NPC Data Object
527
- {
528
- type: npc.NPCType.CUSTOM,
529
- model: 'models/npc.glb',
530
- onActivate: ()=>{console.log('npc activated');},
531
- onWalkAway: ()=>{console.log('test on walk away function')},
532
- faceUser: true,
533
- reactDistance: 3,
534
- idleAnim: 'idle1',
535
- walkingAnim: 'walk1',
536
- hoverText: 'Activate',
537
- continueOnWalkAway: true,
538
- onlyClickTrigger: false,
539
- onlyExternalTrigger: false
540
- }
541
- )
357
+ npc.changeIdleAnim(myNPC, `AngryIdle`, true)
542
358
  ```
543
359
 
544
-
545
-
546
-
360
+ ### Activate
547
361
 
548
-
362
+ The `activate()` function can be used to trigger the `onActivate()` function, as an alternative to pressing E or walking near.
549
363
 
550
- ## Get NPC Data
364
+ ```ts
365
+ npc.activate(myNPC)
366
+ ```
551
367
 
552
-
368
+ The `activate()` function is callable even when in cool down period, and it doesn't start a new cool down period.
553
369
 
554
-
370
+ ### Stop Walking
555
371
 
556
-
372
+ If the NPC is currently walking, call `stopWalking()` to stop it moving and return to playing its idle animation.
557
373
 
558
374
  ```ts
559
- npc.getData(myNPC)
375
+ npc.stopWalking(myNPC)
560
376
  ```
561
377
 
562
-
378
+ `stopWalking()` can be called with no parameters, or it can also be called with:
563
379
 
564
-
380
+ - `duration`: Seconds to wait before starting to walk again. If not provided, the NPC will stop walking indefinitely.
565
381
 
566
-
382
+ > Note: If the NPC is has its dialog window open when the timer for the `duration` ends, the NPC will not return to walking.
567
383
 
568
- There are several properties you can check on an NPC to know what its current state is:
384
+ To make the NPC play a different animation from idle when paused, call `playAnimation()` after `stopWalking()`.
569
385
 
570
-
386
+ ### Follow Path
571
387
 
572
-
388
+ Make an NPC walk following a path of `Vector3` points by calling `followPath()`. While walking, the NPC will play the `walkingAnim` if one was set when defining the NPC. The path can be taken once or on a loop.
573
389
 
574
-
390
+ `followPath()` can be called with no parameters if a `path` was already provided in the NPC's initialization or in a previous calling of `followPath()`. If the NPC was previously in the middle of walking a path and was interrupted, calling `followPath()` again with no arguments will return the NPC to that path.
575
391
 
576
- - `.state`: An enum value of type `NPCState`. Supported values are `NPCState.STANDING` (default), `NPCState.TALKING`, and `NPCState.FOLLOWPATH`. `TALKING` is applied when the dialog window is opened, and set back to `STANDING` when the window is closed. `FOLLOWPATH` is applied when the NPC starts walking, and set back to `STANDING` when the NPC finishes its path or is stopped.
392
+ ```ts
393
+ npc.followPath(myNPC)
394
+ ```
577
395
 
578
-
396
+ > Note: If the NPC is initialized with a `path` value, it will start out walking that path in a loop, no need to run `followPath()`.
579
397
 
580
-
398
+ `followPath()` has a single optional parameter of type `FollowPathData`. This object may have the following optional fields:
581
399
 
582
- - `.introduced`: Boolean, false by default. Set to true if the NPC has spoken to the player at least once in this session.
400
+ - path: Array of `Vector3` positions to walk over.
583
401
 
584
-
402
+ - speed: Speed to move at while walking this path. If no `speed` or `totalDuration` is provided, it uses the NPC's `walkingSpeed`, which is _2_ by default.
585
403
 
586
-
404
+ - totalDuration: The duration in _seconds_ that the whole path should take. The NPC will move at the constant speed required to finish in that time. This value overrides that of the _speed_.
587
405
 
588
- - `.visible`: Returns a Boolean, false by default. True if the dialog window for this NPC is currently open.
406
+ - loop: _boolean_ If true, the NPC walks in circles over the provided set of points in the path. _false_ by default, unless the NPC is initiated with a `path`, in which case it starts as _true_.
589
407
 
590
-
408
+ - curve: _boolean_ If true, the path is traced a single smooth curve that passes over each of the indicated points. The curve is made out of straight-line segments, the path is stored with 4 times as many points as originally defined. _false_ by default.
591
409
 
592
-
410
+ - startingPoint: Index position for what point to start from on the path. _0_ by default.
593
411
 
594
- - `.inCooldown`: Boolean, false by default. True if the NPC was recently activated and it's now in cooldown. The NPC won't respond to being activated till `inCooldown` is false.
412
+ - onFinishCallback: Function to call when the NPC finished walking over all the points on the path. This is only called when `loop` is _false_.
595
413
 
596
-
414
+ - onReachedPointCallback: Function to call once every time the NPC reaches a point in the path.
597
415
 
598
-
416
+ ```ts
417
+ export let myNPC = npc.create(
418
+ {
419
+ position: Vector3.create(8, 0, 8),
420
+ rotation: Quaternion.Zero(),
421
+ scale: Vector3.create(1, 1, 1),
422
+ },
599
423
 
600
-
424
+ //NPC Data Object
425
+ {
426
+ type: npc.NPCType.CUSTOM,
427
+ model: 'models/npc.glb',
428
+ onActivate: () => {
429
+ console.log('npc activated')
430
+ },
431
+ onWalkAway: () => {
432
+ console.log('test on walk away function')
433
+ },
434
+ faceUser: true,
435
+ reactDistance: 3,
436
+ idleAnim: 'idle1',
437
+ walkingAnim: 'walk1',
438
+ hoverText: 'Activate',
439
+ }
440
+ )
601
441
 
602
- > TIP: If you want to force an activation of the NPC in spite of the `inCooldown` value, you can force this value to true before activating.
442
+ npc.followPath(myNPC, {
443
+ path: path,
444
+ loop: true,
445
+ pathType: npc.NPCPathType.RIGID_PATH,
446
+ onFinishCallback: () => {
447
+ console.log('path is done')
448
+ },
449
+ onReachedPointCallback: () => {
450
+ console.log('ending oint')
451
+ },
452
+ totalDuration: 20,
453
+ })
454
+ ```
603
455
 
604
-
456
+ #### NPC Walking Speed
605
457
 
606
-
458
+ The following list of factors are used to determine speed in hierarchical order:
607
459
 
608
-
460
+ - `totalDuration` parameter set when calling `followPath()` is used over the total distance travelled over the path.
609
461
 
610
- ## NPC Callable Actions
462
+ - `speed` parameter set when calling `followPath()`
611
463
 
612
-
464
+ - `walkingSpeed` parameter set when initializing NPC
613
465
 
614
-
466
+ - Default value _2_.
615
467
 
616
-
468
+ #### Joining the path
617
469
 
618
- An NPC object has several callable functions that come with the class:
470
+ If the NPC's current position when calling `followPath()` doesn't match the first position in the `path` array (or the one that matches the `startingPoint` value), the current position is added to the `path` array. The NPC will start by walking from its current position to the first point provided in the path.
619
471
 
620
-
472
+ The `path` can be a single point, and the NPC will then walk a from its current position to that point.
621
473
 
622
-
474
+ > Note: If the speed of the NPC is determined by a `totalDuration` value, the segment that the NPC walks to join into the path is counted as part of the full path. If this segment is long, it will increase the NPC walking speed so that the full path lasts as what's indicated by the `totalDuration`.
623
475
 
624
-
476
+ In this example the NPC is far away from the start of the path. It will first walk from _10, 0, 10_ to _2, 0, 2_ and then continue the path.
625
477
 
626
- ### Talk
478
+ ```ts
479
+ export let myNPC = npc.create(
480
+ {
481
+ position: Vector3.create(10, 0, 10),
482
+ rotation: Quaternion.Zero(),
483
+ scale: Vector3.create(1, 1, 1),
484
+ },
627
485
 
628
-
486
+ //NPC Data Object
487
+ {
488
+ type: npc.NPCType.CUSTOM,
489
+ model: 'models/npc.glb',
490
+ onActivate: () => {
491
+ console.log('npc activated')
492
+ },
493
+ }
494
+ )
495
+ npc.followPath(myNPC, {
496
+ path: [new Vector3(2, 0, 2), new Vector3(4, 0, 4), new Vector3(6, 0, 6)],
497
+ })
498
+ ```
629
499
 
630
-
500
+ #### Example Interrupting the NPC
631
501
 
632
-
502
+ In the following example, an NPC starts roaming walking over a path, pausing on every point to call out for its lost kitten. If the player activates the NPC (by pressing E on it or walking near it) the NPC stops, and turns to face the player and talk. When the conversation is over, the NPC returns to walking its path from where it left off.
633
503
 
634
- To start a conversation with the NPC using the dialog UI, call the `talk()` function. The function takes the following **required** parameter:
504
+ ```ts
505
+ export let myNPC = npc.create(
506
+ {
507
+ position: Vector3.create(10, 0, 10),
508
+ rotation: Quaternion.Zero(),
509
+ scale: Vector3.create(1, 1, 1),
510
+ },
635
511
 
636
-
512
+ //NPC Data Object
513
+ {
514
+ type: npc.NPCType.CUSTOM,
515
+ model: 'models/npc.glb',
516
+ onActivate: () => {
517
+ npc.stopWalking(myNPC)
518
+ npc.talk(myNPC, lostCat, 0)
519
+ console.log('npc activated')
520
+ },
521
+ walkingAnim: 'walk1',
522
+ faceUser: true,
523
+ }
524
+ )
637
525
 
638
-
526
+ npc.followPath(myNPC, {
527
+ path: [new Vector3(4, 0, 30), new Vector3(6, 0, 29), new Vector3(15, 0, 25)],
528
+ loop: true,
529
+ onReachedPointCallback: () => {
530
+ npc.stopWalking(myNPC, 3)
531
+ npc.playAnimation(myNPC, `Cocky`, true, 2.93)
532
+ },
533
+ })
639
534
 
640
-
641
-
642
- - `script`: _(Dialog[])_ This array contains the information to manage the conversation, including events that may be triggered, options to choose, etc.
643
-
644
-
645
-
646
-
647
-
648
-
649
-
650
- It can also take the following optional parameters:
651
-
652
-
653
-
654
-
655
-
656
-
657
-
658
- - `startIndex`: _(number | string)_ The _Dialog_ object from the `script` array to open first. By default this is _0_, the first element of the array. Pass a number to open the entry on a given array position, or pass a string to open the entry with a `name` property matching that string.
659
-
660
-
661
-
662
-
663
-
664
- - `duration`: _(number)_ Number of seconds to wait before closing the dialog window. If no value is set, the window is kept open till the player reaches the end of the conversation or something else closes it.
665
-
666
-
667
-
668
-
669
-
670
-
671
-
672
- ```ts
673
- npc.talk(myNPC,myScript, 0)
674
- ```
675
-
676
-
677
-
678
-
679
-
680
-
681
-
682
- Learn how to build a script object for NPCs in a section below.
683
-
684
-
685
-
686
-
687
-
688
-
689
-
690
- ### Play Animations
691
-
692
-
693
-
694
-
695
-
696
-
697
-
698
- By default, the NPC will loop an animation named 'Idle', or with a name passed in the `idleAnim` parameter.
699
-
700
-
701
-
702
-
703
-
704
-
705
-
706
- Make the NPC play another animation by calling the `playAnimation()` function. The function takes the following **required** parameter:
707
-
708
-
709
-
710
-
711
-
712
-
713
-
714
- - `animationName`: _(string)_ The name of the animation to play.
715
-
716
-
717
-
718
-
719
-
720
-
721
-
722
- It can also take the following optional parameters:
723
-
724
-
725
-
726
-
727
-
728
-
729
-
730
- - `noLoop`: _(boolean)_ If true, plays the animation just once. Otherwise, the animation is looped.
731
-
732
-
733
-
734
-
735
-
736
- - `duration`: _(number)_ Specifies the duration in seconds of the animation. When finished, it returns to playing the idle animation.
737
-
738
-
739
-
740
-
741
-
742
-
743
-
744
- > Note: If `noLoop` is true but no `duration` is set, the model will stay still after playing the animation instead of returning to the idle animation.
745
-
746
-
747
-
748
-
749
-
750
-
751
-
752
- ```ts
753
- npc.playAnimation(myNPC, `Head_Yes`, true, 2.63)
754
- ```
755
-
756
-
757
-
758
-
759
-
760
-
761
-
762
- ### Change idle animation
763
-
764
-
765
-
766
-
767
-
768
-
769
-
770
- The NPC's idle animation is looped by default whenever the NPC is not playing any other animations. In some cases you may want to have different idle animations depending on the circumstances, like while in a conversation, or if the NPC changes its general attitude after some event.
771
-
772
-
773
-
774
-
775
-
776
-
777
-
778
- You set the NPC's idle animation when creating the NPC, using the `idleAnim` field. To change this animation at some later time, use `changeIdleAnim()`.
779
-
780
-
781
-
782
-
783
-
784
-
785
-
786
- The `changeIdleAnim()` function takes two arguments:
787
-
788
-
789
-
790
-
791
-
792
-
793
-
794
- - `animation`: The name of the new animation to set as the idle animation
795
-
796
-
797
-
798
-
799
-
800
- - `play`: Optionally pass this value as _true_ if you want this new animation to start playing right away.
801
-
802
-
803
-
804
-
805
-
806
-
807
-
808
- ```ts
809
- npc.changeIdleAnim(myNPC,`AngryIdle`, true)
810
- ```
811
-
812
-
813
-
814
-
815
-
816
-
817
-
818
- ### Activate
819
-
820
-
821
-
822
-
823
-
824
-
825
-
826
- The `activate()` function can be used to trigger the `onActivate()` function, as an alternative to pressing E or walking near.
827
-
828
-
829
-
830
-
831
-
832
-
833
-
834
- ```ts
835
- npc.activate(myNPC)
836
- ```
837
-
838
-
839
-
840
-
841
-
842
-
843
-
844
- The `activate()` function is callable even when in cool down period, and it doesn't start a new cool down period.
845
-
846
-
847
-
848
-
849
-
850
-
851
-
852
- ### Stop Walking
853
-
854
-
855
-
856
-
857
-
858
-
859
-
860
- If the NPC is currently walking, call `stopWalking()` to stop it moving and return to playing its idle animation.
861
-
862
-
863
-
864
-
865
-
866
-
867
-
868
- ```ts
869
- npc.stopWalking(myNPC)
870
- ```
871
-
872
-
873
-
874
-
875
-
876
-
877
-
878
- `stopWalking()` can be called with no parameters, or it can also be called with:
879
-
880
-
881
-
882
-
883
-
884
-
885
-
886
- - `duration`: Seconds to wait before starting to walk again. If not provided, the NPC will stop walking indefinitely.
887
-
888
-
889
-
890
-
891
-
892
-
893
-
894
- > Note: If the NPC is has its dialog window open when the timer for the `duration` ends, the NPC will not return to walking.
895
-
896
-
897
-
898
-
899
-
900
-
901
-
902
- To make the NPC play a different animation from idle when paused, call `playAnimation()` after `stopWalking()`.
903
-
904
-
905
-
906
-
907
-
908
-
909
-
910
- ### Follow Path
911
-
912
-
913
-
914
-
915
-
916
-
917
-
918
- Make an NPC walk following a path of `Vector3` points by calling `followPath()`. While walking, the NPC will play the `walkingAnim` if one was set when defining the NPC. The path can be taken once or on a loop.
919
-
920
-
921
-
922
-
923
-
924
-
925
-
926
- `followPath()` can be called with no parameters if a `path` was already provided in the NPC's initialization or in a previous calling of `followPath()`. If the NPC was previously in the middle of walking a path and was interrupted, calling `followPath()` again with no arguments will return the NPC to that path.
927
-
928
-
929
-
930
-
931
-
932
-
933
-
934
- ```ts
935
- npc.followPath(myNPC)
936
- ```
937
-
938
-
939
-
940
-
941
-
942
-
943
-
944
- > Note: If the NPC is initialized with a `path` value, it will start out walking that path in a loop, no need to run `followPath()`.
945
-
946
-
947
-
948
-
949
-
950
-
951
-
952
- `followPath()` has a single optional parameter of type `FollowPathData`. This object may have the following optional fields:
953
-
954
-
955
-
956
-
957
-
958
-
959
-
960
- - path: Array of `Vector3` positions to walk over.
961
-
962
-
963
-
964
-
965
-
966
- - speed: Speed to move at while walking this path. If no `speed` or `totalDuration` is provided, it uses the NPC's `walkingSpeed`, which is _2_ by default.
967
-
968
-
969
-
970
-
971
-
972
- - totalDuration: The duration in _seconds_ that the whole path should take. The NPC will move at the constant speed required to finish in that time. This value overrides that of the _speed_.
973
-
974
-
975
-
976
-
977
-
978
- - loop: _boolean_ If true, the NPC walks in circles over the provided set of points in the path. _false_ by default, unless the NPC is initiated with a `path`, in which case it starts as _true_.
979
-
980
-
981
-
982
-
983
-
984
- - curve: _boolean_ If true, the path is traced a single smooth curve that passes over each of the indicated points. The curve is made out of straight-line segments, the path is stored with 4 times as many points as originally defined. _false_ by default.
985
-
986
-
987
-
988
-
989
-
990
- - startingPoint: Index position for what point to start from on the path. _0_ by default.
991
-
992
-
993
-
994
-
995
-
996
- - onFinishCallback: Function to call when the NPC finished walking over all the points on the path. This is only called when `loop` is _false_.
997
-
998
-
999
-
1000
-
1001
-
1002
- - onReachedPointCallback: Function to call once every time the NPC reaches a point in the path.
1003
-
1004
-
1005
-
1006
-
1007
-
1008
-
1009
-
1010
- ```ts
1011
- export let myNPC = npc.create({position: Vector3.create(8,0,8),rotation:Quaternion.Zero(), scale: Vector3.create(1,1,1)},
1012
-
1013
- //NPC Data Object
1014
- {
1015
- type: npc.NPCType.CUSTOM,
1016
- model: 'models/npc.glb',
1017
- onActivate: ()=>{console.log('npc activated');},
1018
- onWalkAway: ()=>{console.log('test on walk away function')},
1019
- faceUser: true,
1020
- reactDistance: 3,
1021
- idleAnim: 'idle1',
1022
- walkingAnim: 'walk1',
1023
- hoverText: "Activate"
1024
- }
1025
- )
1026
-
1027
- npc.followPath(myNPC,
1028
- {
1029
- path:path,
1030
- loop:true,
1031
- pathType: npc.NPCPathType.RIGID_PATH,
1032
- onFinishCallback:()=>{console.log('path is done')},
1033
- onReachedPointCallback:()=>{console.log('ending oint')},
1034
- totalDuration: 20
1035
- }
1036
- )
1037
- ```
1038
-
1039
-
1040
-
1041
-
1042
-
1043
-
1044
-
1045
- #### NPC Walking Speed
1046
-
1047
-
1048
-
1049
-
1050
-
1051
-
1052
-
1053
- The following list of factors are used to determine speed in hierarchical order:
1054
-
1055
-
1056
-
1057
-
1058
-
1059
-
1060
-
1061
- - `totalDuration` parameter set when calling `followPath()` is used over the total distance travelled over the path.
1062
-
1063
-
1064
-
1065
-
1066
-
1067
- - `speed` parameter set when calling `followPath()`
1068
-
1069
-
1070
-
1071
-
1072
-
1073
- - `walkingSpeed` parameter set when initializing NPC
1074
-
1075
-
1076
-
1077
-
1078
-
1079
- - Default value _2_.
1080
-
1081
-
1082
-
1083
-
1084
-
1085
-
1086
-
1087
- #### Joining the path
1088
-
1089
-
1090
-
1091
-
1092
-
1093
-
1094
-
1095
- If the NPC's current position when calling `followPath()` doesn't match the first position in the `path` array (or the one that matches the `startingPoint` value), the current position is added to the `path` array. The NPC will start by walking from its current position to the first point provided in the path.
1096
-
1097
-
1098
-
1099
-
1100
-
1101
-
1102
-
1103
- The `path` can be a single point, and the NPC will then walk a from its current position to that point.
1104
-
1105
-
1106
-
1107
-
1108
-
1109
-
1110
-
1111
- > Note: If the speed of the NPC is determined by a `totalDuration` value, the segment that the NPC walks to join into the path is counted as part of the full path. If this segment is long, it will increase the NPC walking speed so that the full path lasts as what's indicated by the `totalDuration`.
1112
-
1113
-
1114
-
1115
-
1116
-
1117
-
1118
-
1119
- In this example the NPC is far away from the start of the path. It will first walk from _10, 0, 10_ to _2, 0, 2_ and then continue the path.
1120
-
1121
-
1122
-
1123
-
1124
-
1125
-
1126
-
1127
- ```ts
1128
- export let myNPC = npc.create({position: Vector3.create(10,0,10),rotation:Quaternion.Zero(), scale: Vector3.create(1,1,1)},
1129
-
1130
- //NPC Data Object
1131
- {
1132
- type: npc.NPCType.CUSTOM,
1133
- model: 'models/npc.glb',
1134
- onActivate: ()=>{console.log('npc activated');},
1135
- }
1136
- )
1137
- npc.followPath(myNPC,
1138
- {
1139
- path: [new Vector3(2, 0, 2), new Vector3(4, 0, 4), new Vector3(6, 0, 6)]
1140
- })
1141
- ```
1142
-
1143
-
1144
-
1145
-
1146
-
1147
-
1148
-
1149
- #### Example Interrupting the NPC
1150
-
1151
-
1152
-
1153
-
1154
-
1155
-
1156
-
1157
- In the following example, an NPC starts roaming walking over a path, pausing on every point to call out for its lost kitten. If the player activates the NPC (by pressing E on it or walking near it) the NPC stops, and turns to face the player and talk. When the conversation is over, the NPC returns to walking its path from where it left off.
1158
-
1159
-
1160
-
1161
-
1162
-
1163
-
1164
-
1165
- ```ts
1166
- export let myNPC = npc.create({position: Vector3.create(10,0,10),rotation:Quaternion.Zero(), scale: Vector3.create(1,1,1)},
1167
-
1168
- //NPC Data Object
1169
- {
1170
- type: npc.NPCType.CUSTOM,
1171
- model: 'models/npc.glb',
1172
- onActivate: ()=>{
1173
- npc.stopWalking(myNPC);
1174
- npc.talk(myNPC, lostCat, 0)
1175
- console.log('npc activated');
535
+ export let lostCat: Dialog[] = [
536
+ {
537
+ text: `I lost my cat, I'm going crazy here`,
1176
538
  },
1177
- walkingAnim: 'walk1',
1178
- faceUser:true
1179
- }
1180
- )
1181
-
1182
- npc.followPath(myNPC,
1183
- {
1184
- path: [new Vector3(4, 0, 30), new Vector3(6, 0, 29), new Vector3(15, 0, 25)],
1185
- loop: true,
1186
- onReachedPointCallback: () => {
1187
- npc.stopWalking(myNPC, 3)
1188
- npc.playAnimation(myNPC, `Cocky`, true, 2.93)
1189
- }
1190
- }
1191
- )
1192
539
 
1193
- export let lostCat: Dialog[] = [
1194
- {
1195
- text: `I lost my cat, I'm going crazy here`
1196
- },
1197
-
1198
540
  {
1199
- text: `Have you seen it anywhere?`
541
+ text: `Have you seen it anywhere?`,
1200
542
  },
1201
-
543
+
1202
544
  {
1203
- text: `Ok, I'm gonna go back to looking for it`,
1204
- triggeredByNext: () => {
1205
- npc.followPath(myNPC)
545
+ text: `Ok, I'm gonna go back to looking for it`,
546
+ triggeredByNext: () => {
547
+ npc.followPath(myNPC)
548
+ },
549
+ isEndOfDialog: true,
1206
550
  },
1207
- isEndOfDialog: true
1208
- }
1209
551
  ]
1210
552
  ```
1211
553
 
1212
-
1213
-
1214
-
1215
-
1216
-
1217
-
1218
554
  ### End interaction
1219
555
 
1220
-
1221
-
1222
-
1223
-
1224
-
1225
-
1226
556
  The `endInteraction()` function can be used to abruptly end interactions with the NPC.
1227
557
 
1228
-
1229
-
1230
-
1231
-
1232
-
1233
-
1234
558
  If applicable, it closes the dialog UI, hides speech bubbles, and makes the NPC stop rotating to face the player.
1235
559
 
1236
-
1237
-
1238
-
1239
-
1240
-
1241
-
1242
560
  ```ts
1243
561
  npc.endInteraction(myNPC)
1244
562
  ```
1245
563
 
1246
-
1247
-
1248
-
1249
-
1250
-
1251
-
1252
564
  As an alternative, you can call the `handleWalkAway()` function, which has the same effects (as long as `continueOnWalkAway` isn't set to true), but also triggers the `onWalkAway()` function.
1253
565
 
1254
-
1255
-
1256
-
1257
-
1258
-
1259
-
1260
566
  ## NPC Dialog Window
1261
567
 
1262
-
1263
-
1264
-
1265
-
1266
-
1267
-
1268
568
  You can display an interactive dialog window to simulate a conversation with a non-player character (NPC).
1269
569
 
1270
-
1271
-
1272
-
1273
-
1274
-
1275
-
1276
570
  The conversation is based on a script in JSON format. The script can include questions that can take you forward or backward, or end the conversation.
1277
571
 
1278
-
1279
-
1280
-
1281
-
1282
-
1283
-
1284
572
  <img src="screenshots/NPC1.png" width="500">
1285
573
 
1286
-
1287
-
1288
-
1289
-
1290
-
1291
-
1292
574
  ### The NPC script
1293
575
 
1294
-
1295
-
1296
-
1297
-
1298
-
1299
-
1300
576
  Each entry on the script must include at least a `text` field, but can include several more fields to further customize it.
1301
577
 
1302
-
1303
-
1304
-
1305
-
1306
-
1307
-
1308
578
  Below is a minimal dialog.
1309
579
 
1310
-
1311
-
1312
-
1313
-
1314
-
1315
-
1316
580
  ```ts
1317
- export let NPCTalk: Dialog[] = [
581
+ export let NPCTalk: Dialog[] = [
1318
582
  {
1319
- text: 'Hi there'
583
+ text: 'Hi there',
1320
584
  },
1321
-
585
+
1322
586
  {
1323
- text: 'It sure is nice talking to you'
587
+ text: 'It sure is nice talking to you',
1324
588
  },
1325
-
589
+
1326
590
  {
1327
- text: 'I must go, my planet needs me',
1328
- isEndOfDialog: true
1329
- }
591
+ text: 'I must go, my planet needs me',
592
+ isEndOfDialog: true,
593
+ },
1330
594
  ]
1331
-
1332
-
1333
-
1334
-
1335
-
1336
595
  ```
1337
596
 
1338
-
1339
-
1340
-
1341
-
1342
-
1343
-
1344
597
  The player advances through each entry by clicking the mouse button. Once the last is reached, clicking again closes the window, as it's marked as `isEndOfDialog`.
1345
598
 
1346
-
1347
-
1348
-
1349
-
1350
-
1351
-
1352
599
  The script must adhere to the following schema:
1353
600
 
1354
-
1355
-
1356
-
1357
-
1358
-
1359
-
1360
601
  ```ts
1361
- class Dialog {
1362
- text: string
1363
- name?: string
1364
- fontSize?: number
1365
- offsetX?: number
1366
- offsetY?: number
1367
- typeSpeed?: number
1368
- isEndOfDialog?: boolean
1369
- triggeredByNext?: () => void
1370
- portrait?: ImageData
1371
- image?: ImageData
1372
- isQuestion?:boolean
1373
- buttons?: ButtonData[]
1374
- audio?: string
1375
- skipable?: boolean
602
+ class Dialog {
603
+ text: string
604
+ name?: string
605
+ fontSize?: number
606
+ offsetX?: number
607
+ offsetY?: number
608
+ typeSpeed?: number
609
+ isEndOfDialog?: boolean
610
+ triggeredByNext?: () => void
611
+ portrait?: ImageData
612
+ image?: ImageData
613
+ isQuestion?: boolean
614
+ buttons?: ButtonData[]
615
+ audio?: string
616
+ skipable?: boolean
1376
617
  }
1377
618
  ```
1378
619
 
1379
-
1380
-
1381
-
1382
-
1383
-
1384
-
1385
620
  > Note: A `Dialog` object can be used as an input both for the `talk()` function (that is displayed in the UI), and the `talkBubble()` function (that is displayed in a floating bubble over the NPC). Properties marked with `*` are only applicable to UI dialogs.
1386
621
 
1387
-
1388
-
1389
-
1390
-
1391
-
1392
-
1393
622
  You can set the following fields to change the appearance of a dialog:
1394
623
 
1395
-
624
+ - `text`: The dialog text
1396
625
 
1397
-
1398
-
1399
-
1400
-
1401
- - `text`: The dialog text
1402
-
1403
-
1404
-
1405
-
1406
-
1407
- - `fontSize`: Size of the text
1408
-
1409
-
1410
-
1411
-
1412
-
1413
-
626
+ - `fontSize`: Size of the text
1414
627
 
1415
628
  Other fields:
1416
629
 
1417
-
1418
-
1419
-
1420
-
1421
- - `buttons *`: An array of buttons to use in a question entry, covered in the next section.
1422
-
1423
-
1424
-
1425
-
1426
-
1427
- - `audio`: String with the path to an audio file to play once when this dialog is shown on the UI.
1428
-
1429
-
1430
-
1431
-
1432
-
1433
- - `typeSpeed`: The text appears one character at a time, simulating typing. Players can click to skip the animation. Tune the speed of this typing (30 by default) to go slower or faster. Set to _-1_ to skip the animation.
630
+ - `buttons *`: An array of buttons to use in a question entry, covered in the next section.
1434
631
 
1435
-
632
+ - `audio`: String with the path to an audio file to play once when this dialog is shown on the UI.
1436
633
 
1437
-
1438
-
1439
-
634
+ - `typeSpeed`: The text appears one character at a time, simulating typing. Players can click to skip the animation. Tune the speed of this typing (30 by default) to go slower or faster. Set to _-1_ to skip the animation.
1440
635
 
1441
636
  #### Questions and conversation trees
1442
637
 
1443
-
1444
-
1445
-
1446
-
1447
-
1448
-
1449
638
  The script can include questions that prompt the player to pick between two or up to four options. These questions can branch the conversation out and trigger other actions in the scene.
1450
639
 
1451
-
1452
-
1453
-
1454
-
1455
-
1456
-
1457
640
  <img src="screenshots/NPC2.png" width="500">
1458
641
 
1459
-
1460
-
1461
-
1462
-
1463
-
1464
-
1465
642
  > Note: Questions are only used by UI dialogs. If used in a speech bubble, questions will be displayed as regular entries with no buttons or options.
1466
643
 
1467
-
1468
-
1469
-
1470
-
1471
-
1472
-
1473
644
  To make an entry a question, set the `isQuestion` field to _true_. This displays a set of buttons rather than the click icon. It also disables the click to advance to the next entry.
1474
645
 
1475
-
1476
-
1477
-
1478
-
1479
-
1480
-
1481
646
  The `buttons` property of an entry contains an array of `ButtonData` objects, each one of these defines one button.
1482
647
 
1483
-
1484
-
1485
-
1486
-
1487
-
1488
-
1489
648
  When on a question entry, you must provide at least the following for each button:
1490
649
 
1491
-
1492
-
1493
-
1494
-
1495
-
1496
-
1497
- - `label`: _(string)_ The label to show on the button.
1498
-
1499
-
1500
-
1501
-
650
+ - `label`: _(string)_ The label to show on the button.
1502
651
 
1503
- - `goToDialog`: _(number | string)_ The index or name of the next dialog entry to display when activated.
1504
-
1505
-
1506
-
1507
-
1508
-
1509
-
652
+ - `goToDialog`: _(number | string)_ The index or name of the next dialog entry to display when activated.
1510
653
 
1511
654
  > TIP: It's always better to refer to an entry by name, since the array index might shift if you add more entries and it can get hard to keep track of these references.
1512
655
 
1513
-
1514
-
1515
-
1516
-
1517
-
1518
-
1519
656
  You can also set the following:
1520
657
 
1521
-
1522
-
1523
-
1524
-
1525
-
658
+ - `triggeredActions`: _( () => void )_ An additional function to run whenever the button is activated
1526
659
 
1527
- - `triggeredActions`: _( () => void )_ An additional function to run whenever the button is activated
660
+ - `fontSize`: _(number)_ Font size of the text
1528
661
 
1529
-
662
+ - `offsetX`: _(number)_ Offset of the label on the X axis, relative to its normal position.
1530
663
 
1531
-
1532
-
1533
- - `fontSize`: _(number)_ Font size of the text
1534
-
1535
-
1536
-
1537
-
1538
-
1539
- - `offsetX`: _(number)_ Offset of the label on the X axis, relative to its normal position.
1540
-
1541
-
1542
-
1543
-
1544
-
1545
- - `offsetY`: _(number)_ Offset of the label on the Y axis, relative to its normal position.
1546
-
1547
-
1548
-
1549
-
1550
-
1551
-
664
+ - `offsetY`: _(number)_ Offset of the label on the Y axis, relative to its normal position.
1552
665
 
1553
666
  All buttons can be clicked to activate them. Additionally, the first button in the array can be activated by pressing the _E_ key. The second button in the array can be activated by pressing the _F_ key,
1554
667
 
1555
-
1556
-
1557
-
1558
-
1559
-
1560
-
1561
668
  <img src="screenshots/NPC3.png" width="500">
1562
669
 
1563
-
1564
-
1565
-
1566
-
1567
-
1568
-
1569
670
  ```ts
1570
- export let GemsMission: Dialog[] = [
671
+ export let GemsMission: Dialog[] = [
1571
672
  {
1572
- text: `Hello stranger`
673
+ text: `Hello stranger`,
1573
674
  },
1574
675
 
1575
676
  {
1576
- text: `Can you help me finding my missing gems?`,
1577
- isQuestion: true,
1578
- buttons: [
1579
- { label: `Yes!`, goToDialog: 2 },
1580
- { label: `I'm busy`, goToDialog: 4 }
1581
- ]
677
+ text: `Can you help me finding my missing gems?`,
678
+ isQuestion: true,
679
+ buttons: [
680
+ { label: `Yes!`, goToDialog: 2 },
681
+ { label: `I'm busy`, goToDialog: 4 },
682
+ ],
1582
683
  },
1583
684
 
1584
685
  {
1585
- text: `Ok, awesome, thanks!`
686
+ text: `Ok, awesome, thanks!`,
1586
687
  },
1587
688
 
1588
689
  {
1589
- text: `I need you to find 10 gems scattered around this scene, go find them!`,
1590
- isEndOfDialog: true
690
+ text: `I need you to find 10 gems scattered around this scene, go find them!`,
691
+ isEndOfDialog: true,
1591
692
  },
1592
693
 
1593
694
  {
1594
- text: `Ok, come back soon`,
1595
- isEndOfDialog: true
1596
- }
695
+ text: `Ok, come back soon`,
696
+ isEndOfDialog: true,
697
+ },
1597
698
  ]
1598
699
  ```
1599
700
 
1600
-
1601
-
1602
-
1603
-
1604
-
1605
-
1606
701
  #### Triggering functions from the dialog
1607
702
 
1608
-
1609
-
1610
-
1611
-
1612
-
1613
-
1614
703
  You can run functions that may affect any other part of your scene. These functions get triggered when the player interacts with the dialog window, or when the NPC displays speech bubbles.
1615
704
 
1616
-
1617
-
1618
-
1619
-
1620
-
1621
-
1622
- - `triggeredByNext`: Is executed when the player advances to the next dialog on a non-question dialog. The function also gets called if the dialog is the end of the conversation. It also gets called when a speech bubble advances to the next entry.
1623
-
1624
-
1625
-
1626
-
1627
-
1628
-
1629
-
1630
- - `triggeredActions`: This property is associated to a button and is executed on a question dialog if the player activates the corresponding button. You can have up to 4 different buttons per entry, each with its own actions.
705
+ - `triggeredByNext`: Is executed when the player advances to the next dialog on a non-question dialog. The function also gets called if the dialog is the end of the conversation. It also gets called when a speech bubble advances to the next entry.
1631
706
 
1632
-
1633
-
1634
-
1635
-
1636
-
707
+ - `triggeredActions`: This property is associated to a button and is executed on a question dialog if the player activates the corresponding button. You can have up to 4 different buttons per entry, each with its own actions.
1637
708
 
1638
709
  ```ts
1639
710
  export let GemsMission: Dialog[] = [
@@ -1643,7 +714,7 @@ export let GemsMission: Dialog[] = [
1643
714
  // NPC plays animation to show a gem
1644
715
  }
1645
716
  },
1646
-
717
+
1647
718
  {
1648
719
  text: `Can you help me finding my missing gems?`,
1649
720
  isQuestion: true,
@@ -1665,18 +736,19 @@ export let GemsMission: Dialog[] = [
1665
736
  }
1666
737
  ]
1667
738
  },
1668
-
739
+
1669
740
  {
1670
741
  text: `Ok, awesome, thanks!`,
1671
742
  },
1672
-
743
+
1673
744
  {
1674
745
  text: `I need you to find 10 gems scattered around this scene, go find them!`,
1675
746
  isEndOfDialog: true
1676
747
  triggeredByNext: () => {
1677
- // Gems are rendered all around the scene
1678
- },
1679
-
748
+ // Gems are rendered all around the scene
749
+ }
750
+ },
751
+
1680
752
  {
1681
753
  text: `Ok, come back soon`,
1682
754
  isEndOfDialog: true
@@ -1684,276 +756,82 @@ export let GemsMission: Dialog[] = [
1684
756
  ]
1685
757
  ```
1686
758
 
1687
-
1688
-
1689
-
1690
-
1691
759
  ## No-NPC Dialogs
1692
760
 
1693
-
1694
-
1695
-
1696
-
1697
-
1698
-
1699
761
  You can open a Dialog window that isn't associated with any `NPC` object in the scene. The `openDialogWindow()` function has all the same functionality as calling the `talk()` function on an NPC, but may be more practical in scenarios where a character isn't physically there, or where the conversation isn't with a particular character.
1700
762
 
1701
-
1702
-
1703
-
1704
-
1705
-
1706
-
1707
763
  ### The Dialog window
1708
764
 
1709
-
1710
-
1711
-
1712
-
1713
-
1714
-
1715
765
  To create a new dialog window, call `createDialogWindow()` and store as a variable. This will instantiate the window but keep it hidden until you open it.
1716
766
 
1717
-
1718
-
1719
-
1720
-
1721
767
  ```ts
1722
- let dialogWindow = npc.createDialogWindow()
768
+ let dialogWindow = npc.createDialogWindow()
1723
769
  ```
1724
770
 
1725
-
1726
-
1727
-
1728
-
1729
771
  <img src="screenshots/NPC1.png" width="500">
1730
772
 
1731
-
1732
-
1733
-
1734
-
1735
773
  When instantiating a new blank dialog, you can pass the following optional parameters:
1736
774
 
1737
-
775
+ - `defaultPortrait`: Sets a default portrait image to use on the left of all dialogs that don't specify an image. If a dialog has no portrait and no default is provided, no image is shown on the left. This field expects a `Portrait` object, that may include the following fields: - `path`: Path to the image file - `xOffset`: Offset on X, relative to the normal position of the portrait. - `yOffset`: Offset on Y, relative to the normal position of the portrait. - `section`: Use only a section of the image file, useful when arranging multiple icons into an image atlas. This field takes an `ImageSection` object, specifying `sourceWidth` and `sourceHeight`, and optionally also `sourceLeft` and `sourceTop`.
1738
776
 
1739
-
777
+ - `useDarkTheme`: Switch the style of the window to the dark theme.
1740
778
 
1741
- - `defaultPortrait`: Sets a default portrait image to use on the left of all dialogs that don't specify an image. If a dialog has no portrait and no default is provided, no image is shown on the left. This field expects a `Portrait` object, that may include the following fields: - `path`: Path to the image file - `xOffset`: Offset on X, relative to the normal position of the portrait. - `yOffset`: Offset on Y, relative to the normal position of the portrait. - `section`: Use only a section of the image file, useful when arranging multiple icons into an image atlas. This field takes an `ImageSection` object, specifying `sourceWidth` and `sourceHeight`, and optionally also `sourceLeft` and `sourceTop`.
1742
-
1743
-
1744
-
1745
-
1746
-
1747
- - `useDarkTheme`: Switch the style of the window to the dark theme.
1748
-
1749
-
1750
-
1751
-
1752
-
1753
- - `sound`: Path to a sound file that will be played once for every dialog entry shown, as long as the dialog entry doesn't have its own `audio` property.
1754
-
1755
-
1756
-
1757
-
1758
-
1759
-
779
+ - `sound`: Path to a sound file that will be played once for every dialog entry shown, as long as the dialog entry doesn't have its own `audio` property.
1760
780
 
1761
781
  Once you have created a dialog window, you can open a dialog window with the `openDialogWindow()` function.
1762
782
 
1763
-
1764
-
1765
-
1766
-
1767
-
1768
-
1769
783
  ```ts
1770
784
  npc.openDialogWindow(dialogWindow, NPCTalk, 0)
1771
785
  ```
1772
786
 
1773
-
1774
-
1775
-
1776
-
1777
-
1778
-
1779
787
  When calling this function, you must specify:
1780
788
 
1781
-
1782
-
1783
-
1784
-
1785
-
1786
-
1787
- - `NPCScript`: A JSON object composed of an array of `Dialog` objects, that includes all the dialog tree.
1788
-
1789
-
1790
-
1791
-
1792
-
1793
-
789
+ - `NPCScript`: A JSON object composed of an array of `Dialog` objects, that includes all the dialog tree.
1794
790
 
1795
791
  A second optional parameter is also available:
1796
792
 
1797
-
1798
-
1799
-
1800
-
1801
-
1802
-
1803
- - `textId`: The index or `name` property of the entry to show first from the script. The first entry is 0.
1804
-
1805
-
1806
-
1807
-
1808
-
1809
-
793
+ - `textId`: The index or `name` property of the entry to show first from the script. The first entry is 0.
1810
794
 
1811
795
  > TIP: It's always better to refer to an entry by name, since the array index might shift if you add more entries and it can get hard to keep track of these references.
1812
796
 
1813
-
1814
-
1815
-
1816
-
1817
-
1818
-
1819
797
  Close a dialog window at any time by calling the `closeDialogWindow()` function.
1820
798
 
1821
-
1822
-
1823
-
1824
-
1825
-
1826
-
1827
799
  ```ts
1828
800
  npc.closeDialogWindow(dialogWindow)
1829
801
  ```
1830
802
 
1831
-
1832
-
1833
-
1834
-
1835
-
1836
-
1837
803
  For details on how to construct the dialog tree, see the sections above. The required `NPCScript` by the `DialogWindow` has exactly the same characteristics as the one used on the `NPC` object when calling the `talk()` function.
1838
804
 
1839
-
1840
-
1841
-
1842
-
1843
-
1844
-
1845
805
  ---
1846
806
 
1847
-
1848
-
1849
-
1850
-
1851
807
  ## Contribute
1852
808
 
1853
-
1854
-
1855
-
1856
-
1857
-
1858
-
1859
809
  In order to test changes made to this repository in active scenes, do the following:
1860
810
 
1861
-
1862
-
1863
-
1864
-
1865
-
1866
-
1867
811
  1. Run `npm run build` for the internal files of the library to be generated
1868
812
 
1869
-
1870
-
1871
-
1872
-
1873
813
  2. Run `npm run link` on this repository
1874
814
 
1875
-
1876
-
1877
-
1878
-
1879
815
  3. On a new Decentraland scene, import this library as you normally would and include the tests you need
1880
816
 
1881
-
1882
-
1883
-
1884
-
1885
817
  4. On the scene directory, run `npm link dcl-npc-toolkit`
1886
818
 
1887
-
1888
-
1889
-
1890
-
1891
-
1892
-
1893
819
  > Note: When done testing, run `npm unlink` on both folders, so that the scene stops using the local version of the library.
1894
820
 
1895
-
1896
-
1897
-
1898
-
1899
-
1900
-
1901
821
  ## CI/CD
1902
822
 
1903
-
1904
-
1905
-
1906
-
1907
-
1908
-
1909
823
  This repository uses `semantic-release` to automatically release new versions of the package to NPM.
1910
824
 
1911
-
1912
-
1913
-
1914
-
1915
-
1916
-
1917
825
  Use the following convention for commit names:
1918
826
 
1919
-
1920
-
1921
-
1922
-
1923
-
1924
-
1925
827
  `feat: something`: Minor release, every time you add a feature or enhancement that doesn’t break the api.
1926
828
 
1927
-
1928
-
1929
-
1930
-
1931
-
1932
-
1933
829
  `fix: something`: Bug fixing / patch
1934
830
 
1935
-
1936
-
1937
-
1938
-
1939
-
1940
-
1941
831
  `chore: something`: Anything that doesn't require a release to npm, like changing the readme. Updating a dependency is **not** a chore if it fixes a bug or a vulnerability, that's a `fix`.
1942
832
 
1943
-
1944
-
1945
-
1946
-
1947
-
1948
-
1949
833
  If you break the API of the library, you need to do a major release, and that's done a different way. You need to add a second comment that starts with `BREAKING CHANGE`, like:
1950
834
 
1951
-
1952
-
1953
-
1954
-
1955
-
1956
-
1957
835
  ```
1958
836
  commit -m "feat: changed the signature of a method" -m "BREAKING CHANGE: this commit breaks the API, changing foo(arg1) to foo(arg1, arg2)"
1959
837
  ```