prismarine-physics 1.7.0 → 1.9.0
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/.github/workflows/commands.yml +22 -0
- package/HISTORY.md +11 -0
- package/README.md +7 -2
- package/examples/basic.js +3 -0
- package/index.js +174 -70
- package/lib/features.json +1 -6
- package/package.json +1 -1
- package/test/basic.test.js +3 -0
- package/test/elytra.test.js +692 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
name: Repo Commands
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
issue_comment: # Handle comment commands
|
|
5
|
+
types: [created]
|
|
6
|
+
pull_request: # Handle renamed PRs
|
|
7
|
+
types: [edited]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
comment-trigger:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- name: Check out repository
|
|
14
|
+
uses: actions/checkout@v3
|
|
15
|
+
- name: Run command handlers
|
|
16
|
+
uses: PrismarineJS/prismarine-repo-actions@master
|
|
17
|
+
with:
|
|
18
|
+
# NOTE: You must specify a Personal Access Token (PAT) with repo access here. While you can use the default GITHUB_TOKEN, actions taken with it will not trigger other actions, so if you have a CI workflow, commits created by this action will not trigger it.
|
|
19
|
+
token: ${{ secrets.PAT_PASSWORD }}
|
|
20
|
+
# See `Options` section below for more info on these options
|
|
21
|
+
install-command: npm install
|
|
22
|
+
/fixlint.fix-command: npm run fix
|
package/HISTORY.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
## History
|
|
2
2
|
|
|
3
|
+
### 1.9.0
|
|
4
|
+
* [add 1.21 to proportionalliquidgravity setting (#117)](https://github.com/PrismarineJS/prismarine-physics/commit/4986c8e395773e770d461db06bce38f9faed0757) (thanks @Madlykeanu)
|
|
5
|
+
|
|
6
|
+
### 1.8.0
|
|
7
|
+
* [Add support for elytra and rockets (#106)](https://github.com/PrismarineJS/prismarine-physics/commit/04351b4c5fa73e9eb7ca79b88c63f5bef59b5645) (thanks @lkwilson)
|
|
8
|
+
* [Fix "flowing" liquids not being included (#105)](https://github.com/PrismarineJS/prismarine-physics/commit/1f95aaad67684b381aedeb0a990e93dbb03025d0) (thanks @Flonja)
|
|
9
|
+
* [Update index.js (#104)](https://github.com/PrismarineJS/prismarine-physics/commit/dd159da044af4959f9fd8d7d97c14921e7288c1e) (thanks @Vakore)
|
|
10
|
+
* [Fixed airborne movement factor for sprinting (#75)](https://github.com/PrismarineJS/prismarine-physics/commit/116bb6fe3ae5066d53cedb69683a5fdb497c470d) (thanks @olie304)
|
|
11
|
+
* [Add command gh workflow allowing to use release command in comments (#102)](https://github.com/PrismarineJS/prismarine-physics/commit/6797491346be0ef1572aac94c78b2a1377982d54) (thanks @rom1504)
|
|
12
|
+
* [Fix 1.17 & 1.18 effect names (#101)](https://github.com/PrismarineJS/prismarine-physics/commit/cc7ae73d6929689645e77c7ba26bf0dcc4bc1e01) (thanks @killbinvlog)
|
|
13
|
+
|
|
3
14
|
### 1.7.0
|
|
4
15
|
|
|
5
16
|
* 1.20
|
package/README.md
CHANGED
|
@@ -39,10 +39,13 @@ const player = {
|
|
|
39
39
|
isInWeb: false,
|
|
40
40
|
isCollidedHorizontally: false,
|
|
41
41
|
isCollidedVertically: false,
|
|
42
|
-
|
|
42
|
+
elytraFlying: false,
|
|
43
|
+
yaw: 0,
|
|
44
|
+
pitch: 0
|
|
43
45
|
},
|
|
44
46
|
jumpTicks: 0,
|
|
45
|
-
jumpQueued: false
|
|
47
|
+
jumpQueued: false,
|
|
48
|
+
fireworkRocketDuration: 0
|
|
46
49
|
}
|
|
47
50
|
const playerState = new PlayerState(player, controls)
|
|
48
51
|
|
|
@@ -76,8 +79,10 @@ Read / Write properties:
|
|
|
76
79
|
- isInWeb : (boolean) is the player in a web ?
|
|
77
80
|
- isCollidedHorizontally : (boolean) is the player collided horizontally with a solid block ?
|
|
78
81
|
- isCollidedVertically : (boolean) is the player collided vertically with a solid block ?
|
|
82
|
+
- elytraFlying : (boolean) is the player elytra flying ?
|
|
79
83
|
- jumpTicks : (integer) number of ticks before the player can auto-jump again
|
|
80
84
|
- jumpQueued : (boolean) true if the jump control state was true between the last tick and the current one
|
|
85
|
+
- fireworkRocketDuration : (number) how many ticks of firework boost are remaining ?
|
|
81
86
|
|
|
82
87
|
Read only properties:
|
|
83
88
|
- yaw : (float) the yaw angle, in radians, of the player entity
|
package/examples/basic.js
CHANGED
|
@@ -22,9 +22,11 @@ function fakePlayer (pos, baseVersion) {
|
|
|
22
22
|
isInWater: false,
|
|
23
23
|
isInLava: false,
|
|
24
24
|
isInWeb: false,
|
|
25
|
+
elytraFlying: false,
|
|
25
26
|
isCollidedHorizontally: false,
|
|
26
27
|
isCollidedVertically: false,
|
|
27
28
|
yaw: 0,
|
|
29
|
+
pitch: 0,
|
|
28
30
|
effects: []
|
|
29
31
|
},
|
|
30
32
|
inventory: {
|
|
@@ -32,6 +34,7 @@ function fakePlayer (pos, baseVersion) {
|
|
|
32
34
|
},
|
|
33
35
|
jumpTicks: 0,
|
|
34
36
|
jumpQueued: false,
|
|
37
|
+
fireworkRocketDuration: 0,
|
|
35
38
|
version: baseVersion
|
|
36
39
|
}
|
|
37
40
|
}
|
package/index.js
CHANGED
|
@@ -30,14 +30,15 @@ function Physics (mcData, world) {
|
|
|
30
30
|
const soulsandId = blocksByName.soul_sand.id
|
|
31
31
|
const honeyblockId = blocksByName.honey_block ? blocksByName.honey_block.id : -1 // 1.15+
|
|
32
32
|
const webId = blocksByName.cobweb ? blocksByName.cobweb.id : blocksByName.web.id
|
|
33
|
-
const
|
|
34
|
-
const
|
|
33
|
+
const waterIds = [blocksByName.water.id, blocksByName.flowing_water ? blocksByName.flowing_water.id : -1]
|
|
34
|
+
const lavaIds = [blocksByName.lava.id, blocksByName.flowing_lava ? blocksByName.flowing_lava.id : -1]
|
|
35
35
|
const ladderId = blocksByName.ladder.id
|
|
36
36
|
const vineId = blocksByName.vine.id
|
|
37
37
|
const waterLike = new Set()
|
|
38
38
|
if (blocksByName.seagrass) waterLike.add(blocksByName.seagrass.id) // 1.13+
|
|
39
39
|
if (blocksByName.tall_seagrass) waterLike.add(blocksByName.tall_seagrass.id) // 1.13+
|
|
40
40
|
if (blocksByName.kelp) waterLike.add(blocksByName.kelp.id) // 1.13+
|
|
41
|
+
if (blocksByName.kelp_plant) waterLike.add(blocksByName.kelp_plant.id) // 1.13+
|
|
41
42
|
const bubblecolumnId = blocksByName.bubble_column ? blocksByName.bubble_column.id : -1 // 1.13+
|
|
42
43
|
if (blocksByName.bubble_column) waterLike.add(bubblecolumnId)
|
|
43
44
|
|
|
@@ -342,6 +343,64 @@ function Physics (mcData, world) {
|
|
|
342
343
|
}
|
|
343
344
|
}
|
|
344
345
|
|
|
346
|
+
function getLookingVector (entity) {
|
|
347
|
+
// given a yaw pitch, we need the looking vector
|
|
348
|
+
|
|
349
|
+
// yaw is right handed rotation about y (up) starting from -z (north)
|
|
350
|
+
// pitch is -90 looking down, 90 looking up, 0 looking at horizon
|
|
351
|
+
// lets get its coordinate system.
|
|
352
|
+
// let x' = -z (north)
|
|
353
|
+
// let y' = -x (west)
|
|
354
|
+
// let z' = y (up)
|
|
355
|
+
|
|
356
|
+
// the non normalized looking vector in x', y', z' space is
|
|
357
|
+
// x' is cos(yaw)
|
|
358
|
+
// y' is sin(yaw)
|
|
359
|
+
// z' is tan(pitch)
|
|
360
|
+
|
|
361
|
+
// substituting back in x, y, z, we get the looking vector in the normal x, y, z space
|
|
362
|
+
// -z = cos(yaw) => z = -cos(yaw)
|
|
363
|
+
// -x = sin(yaw) => x = -sin(yaw)
|
|
364
|
+
// y = tan(pitch)
|
|
365
|
+
|
|
366
|
+
// normalizing the vectors, we divide each by |sqrt(x*x + y*y + z*z)|
|
|
367
|
+
// x*x + z*z = sin^2 + cos^2 = 1
|
|
368
|
+
// so |sqrt(xx+yy+zz)| = |sqrt(1+tan^2(pitch))|
|
|
369
|
+
// = |sqrt(1+sin^2(pitch)/cos^2(pitch))|
|
|
370
|
+
// = |sqrt((cos^2+sin^2)/cos^2(pitch))|
|
|
371
|
+
// = |sqrt(1/cos^2(pitch))|
|
|
372
|
+
// = |+/- 1/cos(pitch)|
|
|
373
|
+
// = 1/cos(pitch) since pitch in [-90, 90]
|
|
374
|
+
|
|
375
|
+
// the looking vector is therefore
|
|
376
|
+
// x = -sin(yaw) * cos(pitch)
|
|
377
|
+
// y = tan(pitch) * cos(pitch) = sin(pitch)
|
|
378
|
+
// z = -cos(yaw) * cos(pitch)
|
|
379
|
+
|
|
380
|
+
const yaw = entity.yaw
|
|
381
|
+
const pitch = entity.pitch
|
|
382
|
+
const sinYaw = Math.sin(yaw)
|
|
383
|
+
const cosYaw = Math.cos(yaw)
|
|
384
|
+
const sinPitch = Math.sin(pitch)
|
|
385
|
+
const cosPitch = Math.cos(pitch)
|
|
386
|
+
const lookX = -sinYaw * cosPitch
|
|
387
|
+
const lookY = sinPitch
|
|
388
|
+
const lookZ = -cosYaw * cosPitch
|
|
389
|
+
const lookDir = new Vec3(lookX, lookY, lookZ)
|
|
390
|
+
return {
|
|
391
|
+
yaw,
|
|
392
|
+
pitch,
|
|
393
|
+
sinYaw,
|
|
394
|
+
cosYaw,
|
|
395
|
+
sinPitch,
|
|
396
|
+
cosPitch,
|
|
397
|
+
lookX,
|
|
398
|
+
lookY,
|
|
399
|
+
lookZ,
|
|
400
|
+
lookDir
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
345
404
|
function applyHeading (entity, strafe, forward, multiplier) {
|
|
346
405
|
let speed = Math.sqrt(strafe * strafe + forward * forward)
|
|
347
406
|
if (speed < 0.01) return new Vec3(0, 0, 0)
|
|
@@ -376,10 +435,79 @@ function Physics (mcData, world) {
|
|
|
376
435
|
|
|
377
436
|
const gravityMultiplier = (vel.y <= 0 && entity.slowFalling > 0) ? physics.slowFalling : 1
|
|
378
437
|
|
|
379
|
-
if (
|
|
438
|
+
if (entity.isInWater || entity.isInLava) {
|
|
439
|
+
// Water / Lava movement
|
|
440
|
+
const lastY = pos.y
|
|
441
|
+
let acceleration = physics.liquidAcceleration
|
|
442
|
+
const inertia = entity.isInWater ? physics.waterInertia : physics.lavaInertia
|
|
443
|
+
let horizontalInertia = inertia
|
|
444
|
+
|
|
445
|
+
if (entity.isInWater) {
|
|
446
|
+
let strider = Math.min(entity.depthStrider, 3)
|
|
447
|
+
if (!entity.onGround) {
|
|
448
|
+
strider *= 0.5
|
|
449
|
+
}
|
|
450
|
+
if (strider > 0) {
|
|
451
|
+
horizontalInertia += (0.546 - horizontalInertia) * strider / 3
|
|
452
|
+
acceleration += (0.7 - acceleration) * strider / 3
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
if (entity.dolphinsGrace > 0) horizontalInertia = 0.96
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
applyHeading(entity, strafe, forward, acceleration)
|
|
459
|
+
moveEntity(entity, world, vel.x, vel.y, vel.z)
|
|
460
|
+
vel.y *= inertia
|
|
461
|
+
vel.y -= (entity.isInWater ? physics.waterGravity : physics.lavaGravity) * gravityMultiplier
|
|
462
|
+
vel.x *= horizontalInertia
|
|
463
|
+
vel.z *= horizontalInertia
|
|
464
|
+
|
|
465
|
+
if (entity.isCollidedHorizontally && doesNotCollide(world, pos.offset(vel.x, vel.y + 0.6 - pos.y + lastY, vel.z))) {
|
|
466
|
+
vel.y = physics.outOfLiquidImpulse // jump out of liquid
|
|
467
|
+
}
|
|
468
|
+
} else if (entity.elytraFlying) {
|
|
469
|
+
const {
|
|
470
|
+
pitch,
|
|
471
|
+
sinPitch,
|
|
472
|
+
cosPitch,
|
|
473
|
+
lookDir
|
|
474
|
+
} = getLookingVector(entity)
|
|
475
|
+
const horizontalSpeed = Math.sqrt(vel.x * vel.x + vel.z * vel.z)
|
|
476
|
+
const cosPitchSquared = cosPitch * cosPitch
|
|
477
|
+
vel.y += physics.gravity * gravityMultiplier * (-1.0 + cosPitchSquared * 0.75)
|
|
478
|
+
// cosPitch is in [0, 1], so cosPitch > 0.0 is just to protect against
|
|
479
|
+
// divide by zero errors
|
|
480
|
+
if (vel.y < 0.0 && cosPitch > 0.0) {
|
|
481
|
+
const movingDownSpeedModifier = vel.y * (-0.1) * cosPitchSquared
|
|
482
|
+
vel.x += lookDir.x * movingDownSpeedModifier / cosPitch
|
|
483
|
+
vel.y += movingDownSpeedModifier
|
|
484
|
+
vel.z += lookDir.z * movingDownSpeedModifier / cosPitch
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
if (pitch < 0.0 && cosPitch > 0.0) {
|
|
488
|
+
const lookDownSpeedModifier = horizontalSpeed * (-sinPitch) * 0.04
|
|
489
|
+
vel.x += -lookDir.x * lookDownSpeedModifier / cosPitch
|
|
490
|
+
vel.y += lookDownSpeedModifier * 3.2
|
|
491
|
+
vel.z += -lookDir.z * lookDownSpeedModifier / cosPitch
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
if (cosPitch > 0.0) {
|
|
495
|
+
vel.x += (lookDir.x / cosPitch * horizontalSpeed - vel.x) * 0.1
|
|
496
|
+
vel.z += (lookDir.z / cosPitch * horizontalSpeed - vel.z) * 0.1
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
vel.x *= 0.99
|
|
500
|
+
vel.y *= 0.98
|
|
501
|
+
vel.z *= 0.99
|
|
502
|
+
moveEntity(entity, world, vel.x, vel.y, vel.z)
|
|
503
|
+
|
|
504
|
+
if (entity.onGround) {
|
|
505
|
+
entity.elytraFlying = false
|
|
506
|
+
}
|
|
507
|
+
} else {
|
|
380
508
|
// Normal movement
|
|
381
|
-
let acceleration =
|
|
382
|
-
let inertia =
|
|
509
|
+
let acceleration = 0.0
|
|
510
|
+
let inertia = 0.0
|
|
383
511
|
const blockUnder = world.getBlock(pos.offset(0, -1, 0))
|
|
384
512
|
if (entity.onGround && blockUnder) {
|
|
385
513
|
let playerSpeedAttribute
|
|
@@ -407,6 +535,14 @@ function Physics (mcData, world) {
|
|
|
407
535
|
inertia = (blockSlipperiness[blockUnder.type] || physics.defaultSlipperiness) * 0.91
|
|
408
536
|
acceleration = attributeSpeed * (0.1627714 / (inertia * inertia * inertia))
|
|
409
537
|
if (acceleration < 0) acceleration = 0 // acceleration should not be negative
|
|
538
|
+
} else {
|
|
539
|
+
acceleration = physics.airborneAcceleration
|
|
540
|
+
inertia = physics.airborneInertia
|
|
541
|
+
|
|
542
|
+
if (entity.control.sprint) {
|
|
543
|
+
const airSprintFactor = physics.airborneAcceleration * 0.3
|
|
544
|
+
acceleration += airSprintFactor
|
|
545
|
+
}
|
|
410
546
|
}
|
|
411
547
|
|
|
412
548
|
applyHeading(entity, strafe, forward, acceleration)
|
|
@@ -433,46 +569,16 @@ function Physics (mcData, world) {
|
|
|
433
569
|
vel.y *= physics.airdrag
|
|
434
570
|
vel.x *= inertia
|
|
435
571
|
vel.z *= inertia
|
|
436
|
-
} else {
|
|
437
|
-
// Water / Lava movement
|
|
438
|
-
const lastY = pos.y
|
|
439
|
-
let acceleration = physics.liquidAcceleration
|
|
440
|
-
const inertia = entity.isInWater ? physics.waterInertia : physics.lavaInertia
|
|
441
|
-
let horizontalInertia = inertia
|
|
442
|
-
|
|
443
|
-
if (entity.isInWater) {
|
|
444
|
-
let strider = Math.min(entity.depthStrider, 3)
|
|
445
|
-
if (!entity.onGround) {
|
|
446
|
-
strider *= 0.5
|
|
447
|
-
}
|
|
448
|
-
if (strider > 0) {
|
|
449
|
-
horizontalInertia += (0.546 - horizontalInertia) * strider / 3
|
|
450
|
-
acceleration += (0.7 - acceleration) * strider / 3
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
if (entity.dolphinsGrace > 0) horizontalInertia = 0.96
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
applyHeading(entity, strafe, forward, acceleration)
|
|
457
|
-
moveEntity(entity, world, vel.x, vel.y, vel.z)
|
|
458
|
-
vel.y *= inertia
|
|
459
|
-
vel.y -= (entity.isInWater ? physics.waterGravity : physics.lavaGravity) * gravityMultiplier
|
|
460
|
-
vel.x *= horizontalInertia
|
|
461
|
-
vel.z *= horizontalInertia
|
|
462
|
-
|
|
463
|
-
if (entity.isCollidedHorizontally && doesNotCollide(world, pos.offset(vel.x, vel.y + 0.6 - pos.y + lastY, vel.z))) {
|
|
464
|
-
vel.y = physics.outOfLiquidImpulse // jump out of liquid
|
|
465
|
-
}
|
|
466
572
|
}
|
|
467
573
|
}
|
|
468
574
|
|
|
469
|
-
function isMaterialInBB (world, queryBB,
|
|
575
|
+
function isMaterialInBB (world, queryBB, types) {
|
|
470
576
|
const cursor = new Vec3(0, 0, 0)
|
|
471
577
|
for (cursor.y = Math.floor(queryBB.minY); cursor.y <= Math.floor(queryBB.maxY); cursor.y++) {
|
|
472
578
|
for (cursor.z = Math.floor(queryBB.minZ); cursor.z <= Math.floor(queryBB.maxZ); cursor.z++) {
|
|
473
579
|
for (cursor.x = Math.floor(queryBB.minX); cursor.x <= Math.floor(queryBB.maxX); cursor.x++) {
|
|
474
580
|
const block = world.getBlock(cursor)
|
|
475
|
-
if (block && block.type
|
|
581
|
+
if (block && types.includes(block.type)) return true
|
|
476
582
|
}
|
|
477
583
|
}
|
|
478
584
|
}
|
|
@@ -487,7 +593,7 @@ function Physics (mcData, world) {
|
|
|
487
593
|
if (!block) return -1
|
|
488
594
|
if (waterLike.has(block.type)) return 0
|
|
489
595
|
if (block.getProperties().waterlogged) return 0
|
|
490
|
-
if (block.type
|
|
596
|
+
if (!waterIds.includes(block.type)) return -1
|
|
491
597
|
const meta = block.metadata
|
|
492
598
|
return meta >= 8 ? 0 : meta
|
|
493
599
|
}
|
|
@@ -534,7 +640,7 @@ function Physics (mcData, world) {
|
|
|
534
640
|
for (cursor.z = Math.floor(bb.minZ); cursor.z <= Math.floor(bb.maxZ); cursor.z++) {
|
|
535
641
|
for (cursor.x = Math.floor(bb.minX); cursor.x <= Math.floor(bb.maxX); cursor.x++) {
|
|
536
642
|
const block = world.getBlock(cursor)
|
|
537
|
-
if (block && (block.type
|
|
643
|
+
if (block && (waterIds.includes(block.type) || waterLike.has(block.type) || block.getProperties().waterlogged)) {
|
|
538
644
|
const waterLevel = cursor.y + 1 - getLiquidHeightPcent(block)
|
|
539
645
|
if (Math.ceil(bb.maxY) >= waterLevel) waterBlocks.push(block)
|
|
540
646
|
}
|
|
@@ -570,7 +676,7 @@ function Physics (mcData, world) {
|
|
|
570
676
|
const lavaBB = getPlayerBB(pos).contract(0.1, 0.4, 0.1)
|
|
571
677
|
|
|
572
678
|
entity.isInWater = isInWaterApplyCurrent(world, waterBB, vel)
|
|
573
|
-
entity.isInLava = isMaterialInBB(world, lavaBB,
|
|
679
|
+
entity.isInLava = isMaterialInBB(world, lavaBB, lavaIds)
|
|
574
680
|
|
|
575
681
|
// Reset velocity component if it falls under the threshold
|
|
576
682
|
if (Math.abs(vel.x) < physics.negligeableVelocity) vel.x = 0
|
|
@@ -608,6 +714,20 @@ function Physics (mcData, world) {
|
|
|
608
714
|
forward *= physics.sneakSpeed
|
|
609
715
|
}
|
|
610
716
|
|
|
717
|
+
entity.elytraFlying = entity.elytraFlying && entity.elytraEquipped && !entity.onGround && !entity.levitation
|
|
718
|
+
|
|
719
|
+
if (entity.fireworkRocketDuration > 0) {
|
|
720
|
+
if (!entity.elytraFlying) {
|
|
721
|
+
entity.fireworkRocketDuration = 0
|
|
722
|
+
} else {
|
|
723
|
+
const { lookDir } = getLookingVector(entity)
|
|
724
|
+
vel.x += lookDir.x * 0.1 + (lookDir.x * 1.5 - vel.x) * 0.5
|
|
725
|
+
vel.y += lookDir.y * 0.1 + (lookDir.y * 1.5 - vel.y) * 0.5
|
|
726
|
+
vel.z += lookDir.z * 0.1 + (lookDir.z * 1.5 - vel.z) * 0.5
|
|
727
|
+
--entity.fireworkRocketDuration
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
|
|
611
731
|
moveEntityWithHeading(entity, world, strafe, forward)
|
|
612
732
|
|
|
613
733
|
return entity
|
|
@@ -646,33 +766,10 @@ function getEnchantmentLevel (mcData, enchantmentName, enchantments) {
|
|
|
646
766
|
return 0
|
|
647
767
|
}
|
|
648
768
|
|
|
649
|
-
function getStatusEffectNamesForVersion (supportFeature) {
|
|
650
|
-
if (supportFeature('effectNamesAreRegistryNames')) {
|
|
651
|
-
return {
|
|
652
|
-
jumpBoostEffectName: 'jump_boost',
|
|
653
|
-
speedEffectName: 'speed',
|
|
654
|
-
slownessEffectName: 'slowness',
|
|
655
|
-
dolphinsGraceEffectName: 'dolphins_grace',
|
|
656
|
-
slowFallingEffectName: 'slow_falling',
|
|
657
|
-
levitationEffectName: 'levitation'
|
|
658
|
-
}
|
|
659
|
-
} else {
|
|
660
|
-
return {
|
|
661
|
-
jumpBoostEffectName: 'JumpBoost',
|
|
662
|
-
speedEffectName: 'Speed',
|
|
663
|
-
slownessEffectName: 'Slowness',
|
|
664
|
-
dolphinsGraceEffectName: 'DolphinsGrace',
|
|
665
|
-
slowFallingEffectName: 'SlowFalling',
|
|
666
|
-
levitationEffectName: 'Levitation'
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
}
|
|
670
|
-
|
|
671
769
|
class PlayerState {
|
|
672
770
|
constructor (bot, control) {
|
|
673
771
|
const mcData = require('minecraft-data')(bot.version)
|
|
674
772
|
const nbt = require('prismarine-nbt')
|
|
675
|
-
const supportFeature = makeSupportFeature(mcData)
|
|
676
773
|
|
|
677
774
|
// Input / Outputs
|
|
678
775
|
this.pos = bot.entity.position.clone()
|
|
@@ -683,8 +780,10 @@ class PlayerState {
|
|
|
683
780
|
this.isInWeb = bot.entity.isInWeb
|
|
684
781
|
this.isCollidedHorizontally = bot.entity.isCollidedHorizontally
|
|
685
782
|
this.isCollidedVertically = bot.entity.isCollidedVertically
|
|
783
|
+
this.elytraFlying = bot.entity.elytraFlying
|
|
686
784
|
this.jumpTicks = bot.jumpTicks
|
|
687
785
|
this.jumpQueued = bot.jumpQueued
|
|
786
|
+
this.fireworkRocketDuration = bot.fireworkRocketDuration
|
|
688
787
|
|
|
689
788
|
// Input only (not modified)
|
|
690
789
|
this.attributes = bot.entity.attributes
|
|
@@ -694,15 +793,14 @@ class PlayerState {
|
|
|
694
793
|
|
|
695
794
|
// effects
|
|
696
795
|
const effects = bot.entity.effects
|
|
697
|
-
const statusEffectNames = getStatusEffectNamesForVersion(supportFeature)
|
|
698
796
|
|
|
699
|
-
this.jumpBoost = getEffectLevel(mcData,
|
|
700
|
-
this.speed = getEffectLevel(mcData,
|
|
701
|
-
this.slowness = getEffectLevel(mcData,
|
|
797
|
+
this.jumpBoost = getEffectLevel(mcData, 'JumpBoost', effects)
|
|
798
|
+
this.speed = getEffectLevel(mcData, 'Speed', effects)
|
|
799
|
+
this.slowness = getEffectLevel(mcData, 'Slowness', effects)
|
|
702
800
|
|
|
703
|
-
this.dolphinsGrace = getEffectLevel(mcData,
|
|
704
|
-
this.slowFalling = getEffectLevel(mcData,
|
|
705
|
-
this.levitation = getEffectLevel(mcData,
|
|
801
|
+
this.dolphinsGrace = getEffectLevel(mcData, 'DolphinsGrace', effects)
|
|
802
|
+
this.slowFalling = getEffectLevel(mcData, 'SlowFalling', effects)
|
|
803
|
+
this.levitation = getEffectLevel(mcData, 'Levitation', effects)
|
|
706
804
|
|
|
707
805
|
// armour enchantments
|
|
708
806
|
const boots = bot.inventory.slots[8]
|
|
@@ -713,6 +811,10 @@ class PlayerState {
|
|
|
713
811
|
} else {
|
|
714
812
|
this.depthStrider = 0
|
|
715
813
|
}
|
|
814
|
+
|
|
815
|
+
// extra elytra requirements
|
|
816
|
+
const item = bot.inventory.slots[6]
|
|
817
|
+
this.elytraEquipped = item != null && item.name === 'elytra'
|
|
716
818
|
}
|
|
717
819
|
|
|
718
820
|
apply (bot) {
|
|
@@ -724,8 +826,10 @@ class PlayerState {
|
|
|
724
826
|
bot.entity.isInWeb = this.isInWeb
|
|
725
827
|
bot.entity.isCollidedHorizontally = this.isCollidedHorizontally
|
|
726
828
|
bot.entity.isCollidedVertically = this.isCollidedVertically
|
|
829
|
+
bot.entity.elytraFlying = this.elytraFlying
|
|
727
830
|
bot.jumpTicks = this.jumpTicks
|
|
728
831
|
bot.jumpQueued = this.jumpQueued
|
|
832
|
+
bot.fireworkRocketDuration = this.fireworkRocketDuration
|
|
729
833
|
}
|
|
730
834
|
}
|
|
731
835
|
|
package/lib/features.json
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
{
|
|
8
8
|
"name": "proportionalLiquidGravity",
|
|
9
9
|
"description": "Liquid gravity is a proportion of normal gravity",
|
|
10
|
-
"versions": ["1.13", "1.14", "1.15", "1.16", "1.17", "1.18", "1.19", "1.20"]
|
|
10
|
+
"versions": ["1.13", "1.14", "1.15", "1.16", "1.17", "1.18", "1.19", "1.20", "1.21"]
|
|
11
11
|
},
|
|
12
12
|
{
|
|
13
13
|
"name": "velocityBlocksOnCollision",
|
|
@@ -23,10 +23,5 @@
|
|
|
23
23
|
"name": "climbUsingJump",
|
|
24
24
|
"description": "Entity can climb ladders and vines by pressing jump",
|
|
25
25
|
"versions": ["1.14", "1.15", "1.17", "1.18"]
|
|
26
|
-
},
|
|
27
|
-
{
|
|
28
|
-
"name": "effectNamesAreRegistryNames",
|
|
29
|
-
"description": "Status effect names equal to their registry names",
|
|
30
|
-
"versions": ["1.17", "1.18"]
|
|
31
26
|
}
|
|
32
27
|
]
|
package/package.json
CHANGED
package/test/basic.test.js
CHANGED
|
@@ -27,11 +27,14 @@ function fakePlayer (pos) {
|
|
|
27
27
|
isInWeb: false,
|
|
28
28
|
isCollidedHorizontally: false,
|
|
29
29
|
isCollidedVertically: false,
|
|
30
|
+
elytraFlying: false,
|
|
30
31
|
yaw: 0,
|
|
32
|
+
pitch: 0,
|
|
31
33
|
effects: {}
|
|
32
34
|
},
|
|
33
35
|
jumpTicks: 0,
|
|
34
36
|
jumpQueued: false,
|
|
37
|
+
fireworkRocketDuration: 0,
|
|
35
38
|
version: '1.13.2',
|
|
36
39
|
inventory: {
|
|
37
40
|
slots: []
|
|
@@ -0,0 +1,692 @@
|
|
|
1
|
+
/* eslint-env mocha */
|
|
2
|
+
|
|
3
|
+
const { Physics, PlayerState } = require('prismarine-physics')
|
|
4
|
+
const { Vec3 } = require('vec3')
|
|
5
|
+
const expect = require('expect')
|
|
6
|
+
|
|
7
|
+
const mcData = require('minecraft-data')('1.13.2')
|
|
8
|
+
const Block = require('prismarine-block')('1.13.2')
|
|
9
|
+
|
|
10
|
+
const fakeWorld = {
|
|
11
|
+
getBlock: (pos) => {
|
|
12
|
+
const type = (pos.y < 60) ? mcData.blocksByName.stone.id : mcData.blocksByName.air.id
|
|
13
|
+
const b = new Block(type, 0, 0)
|
|
14
|
+
b.position = pos
|
|
15
|
+
return b
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const fakeWallWorld = {
|
|
20
|
+
getBlock: (pos) => {
|
|
21
|
+
const type = (pos.y < 60 || pos.x > 50) ? mcData.blocksByName.stone.id : mcData.blocksByName.air.id
|
|
22
|
+
const b = new Block(type, 0, 0)
|
|
23
|
+
b.position = pos
|
|
24
|
+
return b
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const fakeWebWorld = {
|
|
29
|
+
getBlock: (pos) => {
|
|
30
|
+
const type = (pos.y < 60) ? mcData.blocksByName.stone.id : mcData.blocksByName.cobweb.id
|
|
31
|
+
const b = new Block(type, 0, 0)
|
|
32
|
+
b.position = pos
|
|
33
|
+
return b
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const waterWorld = {
|
|
38
|
+
getBlock: (pos) => {
|
|
39
|
+
const type = (pos.y < 60) ? mcData.blocksByName.water.id : mcData.blocksByName.air.id
|
|
40
|
+
const b = new Block(type, 0, 0)
|
|
41
|
+
b.position = pos
|
|
42
|
+
return b
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const lavaWorld = {
|
|
47
|
+
getBlock: (pos) => {
|
|
48
|
+
const type = (pos.y < 60) ? mcData.blocksByName.lava.id : mcData.blocksByName.air.id
|
|
49
|
+
const b = new Block(type, 0, 0)
|
|
50
|
+
b.position = pos
|
|
51
|
+
return b
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function fakePlayer (pos) {
|
|
56
|
+
return {
|
|
57
|
+
entity: {
|
|
58
|
+
position: pos,
|
|
59
|
+
velocity: new Vec3(0, 0, 0),
|
|
60
|
+
onGround: false,
|
|
61
|
+
isInWater: false,
|
|
62
|
+
isInLava: false,
|
|
63
|
+
isInWeb: false,
|
|
64
|
+
isCollidedHorizontally: false,
|
|
65
|
+
isCollidedVertically: false,
|
|
66
|
+
elytraFlying: false,
|
|
67
|
+
yaw: Math.PI * 3 / 2, // east (+x)
|
|
68
|
+
pitch: 20 * Math.PI / 180,
|
|
69
|
+
effects: {}
|
|
70
|
+
},
|
|
71
|
+
jumpTicks: 0,
|
|
72
|
+
jumpQueued: false,
|
|
73
|
+
fireworkRocketDuration: 0,
|
|
74
|
+
version: '1.13.2',
|
|
75
|
+
inventory: {
|
|
76
|
+
slots: []
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function untilIdle (player, physics, world, controls) {
|
|
82
|
+
const playerState = new PlayerState(player, controls)
|
|
83
|
+
while (!playerState.onGround || playerState.vel.x !== 0 || playerState.vel.z !== 0) {
|
|
84
|
+
physics.simulatePlayer(playerState, world)
|
|
85
|
+
}
|
|
86
|
+
playerState.apply(player)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function passTicks (ticks, player, physics, world, controls) {
|
|
90
|
+
const playerState = new PlayerState(player, controls)
|
|
91
|
+
for (let i = 0; i < ticks; ++i) {
|
|
92
|
+
physics.simulatePlayer(playerState, world)
|
|
93
|
+
}
|
|
94
|
+
playerState.apply(player)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function mpsToTps (mps) {
|
|
98
|
+
// 20 ticks per second
|
|
99
|
+
return mps / 20
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
describe('Elytra tests', () => {
|
|
103
|
+
it('flies east and then back', () => {
|
|
104
|
+
const physics = Physics(mcData, fakeWorld)
|
|
105
|
+
const controls = {
|
|
106
|
+
forward: false,
|
|
107
|
+
back: false,
|
|
108
|
+
left: false,
|
|
109
|
+
right: false,
|
|
110
|
+
jump: false,
|
|
111
|
+
sprint: false,
|
|
112
|
+
sneak: false
|
|
113
|
+
}
|
|
114
|
+
const player = fakePlayer(new Vec3(0, 61, 0))
|
|
115
|
+
player.inventory.slots[6] = { name: 'elytra' }
|
|
116
|
+
|
|
117
|
+
// wait til on ground
|
|
118
|
+
untilIdle(player, physics, fakeWorld, controls)
|
|
119
|
+
|
|
120
|
+
expect(player.entity.position).toEqual(new Vec3(0, 60, 0))
|
|
121
|
+
|
|
122
|
+
// jump
|
|
123
|
+
player.jumpQueued = true
|
|
124
|
+
passTicks(10, player, physics, fakeWorld, controls)
|
|
125
|
+
expect(player.entity.onGround).toBeFalsy()
|
|
126
|
+
|
|
127
|
+
// fly
|
|
128
|
+
player.entity.elytraFlying = true
|
|
129
|
+
passTicks(1, player, physics, fakeWorld, controls)
|
|
130
|
+
|
|
131
|
+
// boost
|
|
132
|
+
player.fireworkRocketDuration = 20
|
|
133
|
+
|
|
134
|
+
// wait til on ground
|
|
135
|
+
untilIdle(player, physics, fakeWorld, controls)
|
|
136
|
+
|
|
137
|
+
expect(player.entity.elytraFlying).toBeFalsy()
|
|
138
|
+
expect(player.fireworkRocketDuration).toEqual(0)
|
|
139
|
+
|
|
140
|
+
expect(player.entity.position.x).toBeGreaterThan(140)
|
|
141
|
+
expect(player.entity.position.y).toEqual(60)
|
|
142
|
+
expect(player.entity.position.z).toBeCloseTo(0)
|
|
143
|
+
|
|
144
|
+
// turn around
|
|
145
|
+
player.entity.yaw = Math.PI / 2 // west (-x)
|
|
146
|
+
|
|
147
|
+
// jump
|
|
148
|
+
player.jumpQueued = true
|
|
149
|
+
passTicks(10, player, physics, fakeWorld, controls)
|
|
150
|
+
|
|
151
|
+
expect(player.entity.onGround).toBeFalsy()
|
|
152
|
+
|
|
153
|
+
// fly
|
|
154
|
+
player.entity.elytraFlying = true
|
|
155
|
+
passTicks(1, player, physics, fakeWorld, controls)
|
|
156
|
+
expect(player.entity.elytraFlying).toBeTruthy()
|
|
157
|
+
|
|
158
|
+
// boost
|
|
159
|
+
player.fireworkRocketDuration = 20
|
|
160
|
+
|
|
161
|
+
// wait til on ground
|
|
162
|
+
untilIdle(player, physics, fakeWorld, controls)
|
|
163
|
+
|
|
164
|
+
expect(player.entity.elytraFlying).toBeFalsy()
|
|
165
|
+
expect(player.fireworkRocketDuration).toEqual(0)
|
|
166
|
+
|
|
167
|
+
// should be back at start
|
|
168
|
+
expect(player.entity.position.x).toBeCloseTo(0)
|
|
169
|
+
expect(player.entity.position.y).toEqual(60)
|
|
170
|
+
expect(player.entity.position.z).toBeCloseTo(0)
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
it('flies south and then back', () => {
|
|
174
|
+
const physics = Physics(mcData, fakeWorld)
|
|
175
|
+
const controls = {
|
|
176
|
+
forward: false,
|
|
177
|
+
back: false,
|
|
178
|
+
left: false,
|
|
179
|
+
right: false,
|
|
180
|
+
jump: false,
|
|
181
|
+
sprint: false,
|
|
182
|
+
sneak: false
|
|
183
|
+
}
|
|
184
|
+
const player = fakePlayer(new Vec3(0, 61, 0))
|
|
185
|
+
player.inventory.slots[6] = { name: 'elytra' }
|
|
186
|
+
player.entity.yaw = 0 // north (-z)
|
|
187
|
+
|
|
188
|
+
// wait til on ground
|
|
189
|
+
untilIdle(player, physics, fakeWorld, controls)
|
|
190
|
+
|
|
191
|
+
expect(player.entity.position).toEqual(new Vec3(0, 60, 0))
|
|
192
|
+
|
|
193
|
+
// jump
|
|
194
|
+
player.jumpQueued = true
|
|
195
|
+
passTicks(10, player, physics, fakeWorld, controls)
|
|
196
|
+
expect(player.entity.onGround).toBeFalsy()
|
|
197
|
+
|
|
198
|
+
// fly
|
|
199
|
+
player.entity.elytraFlying = true
|
|
200
|
+
passTicks(1, player, physics, fakeWorld, controls)
|
|
201
|
+
|
|
202
|
+
// boost
|
|
203
|
+
player.fireworkRocketDuration = 20
|
|
204
|
+
|
|
205
|
+
// wait til on ground
|
|
206
|
+
untilIdle(player, physics, fakeWorld, controls)
|
|
207
|
+
|
|
208
|
+
expect(player.entity.elytraFlying).toBeFalsy()
|
|
209
|
+
expect(player.fireworkRocketDuration).toEqual(0)
|
|
210
|
+
|
|
211
|
+
expect(player.entity.position.x).toBeCloseTo(0)
|
|
212
|
+
expect(player.entity.position.y).toEqual(60)
|
|
213
|
+
expect(player.entity.position.z).toBeLessThan(-140)
|
|
214
|
+
|
|
215
|
+
// turn around
|
|
216
|
+
player.entity.yaw = Math.PI // south (z)
|
|
217
|
+
|
|
218
|
+
// jump
|
|
219
|
+
player.jumpQueued = true
|
|
220
|
+
passTicks(10, player, physics, fakeWorld, controls)
|
|
221
|
+
|
|
222
|
+
expect(player.entity.onGround).toBeFalsy()
|
|
223
|
+
|
|
224
|
+
// fly
|
|
225
|
+
player.entity.elytraFlying = true
|
|
226
|
+
passTicks(1, player, physics, fakeWorld, controls)
|
|
227
|
+
expect(player.entity.elytraFlying).toBeTruthy()
|
|
228
|
+
|
|
229
|
+
// boost
|
|
230
|
+
player.fireworkRocketDuration = 20
|
|
231
|
+
|
|
232
|
+
// wait til on ground
|
|
233
|
+
untilIdle(player, physics, fakeWorld, controls)
|
|
234
|
+
|
|
235
|
+
expect(player.entity.elytraFlying).toBeFalsy()
|
|
236
|
+
expect(player.fireworkRocketDuration).toEqual(0)
|
|
237
|
+
|
|
238
|
+
// should be back at start
|
|
239
|
+
expect(player.entity.position.x).toBeCloseTo(0)
|
|
240
|
+
expect(player.entity.position.y).toEqual(60)
|
|
241
|
+
expect(player.entity.position.z).toBeCloseTo(0)
|
|
242
|
+
})
|
|
243
|
+
|
|
244
|
+
it('flies north east and then back', () => {
|
|
245
|
+
const physics = Physics(mcData, fakeWorld)
|
|
246
|
+
const controls = {
|
|
247
|
+
forward: false,
|
|
248
|
+
back: false,
|
|
249
|
+
left: false,
|
|
250
|
+
right: false,
|
|
251
|
+
jump: false,
|
|
252
|
+
sprint: false,
|
|
253
|
+
sneak: false
|
|
254
|
+
}
|
|
255
|
+
const player = fakePlayer(new Vec3(0, 61, 0))
|
|
256
|
+
player.inventory.slots[6] = { name: 'elytra' }
|
|
257
|
+
// facing positive z, to the left is positive x
|
|
258
|
+
player.entity.yaw = -Math.PI / 4 + 2 * Math.PI // right 45 degrees of north (-z), towards east (+x)
|
|
259
|
+
|
|
260
|
+
// wait til on ground
|
|
261
|
+
untilIdle(player, physics, fakeWorld, controls)
|
|
262
|
+
|
|
263
|
+
expect(player.entity.position).toEqual(new Vec3(0, 60, 0))
|
|
264
|
+
|
|
265
|
+
// jump
|
|
266
|
+
player.jumpQueued = true
|
|
267
|
+
passTicks(10, player, physics, fakeWorld, controls)
|
|
268
|
+
expect(player.entity.onGround).toBeFalsy()
|
|
269
|
+
|
|
270
|
+
// fly
|
|
271
|
+
player.entity.elytraFlying = true
|
|
272
|
+
passTicks(1, player, physics, fakeWorld, controls)
|
|
273
|
+
|
|
274
|
+
// boost
|
|
275
|
+
player.fireworkRocketDuration = 20
|
|
276
|
+
|
|
277
|
+
// wait til on ground
|
|
278
|
+
untilIdle(player, physics, fakeWorld, controls)
|
|
279
|
+
|
|
280
|
+
expect(player.entity.elytraFlying).toBeFalsy()
|
|
281
|
+
expect(player.fireworkRocketDuration).toEqual(0)
|
|
282
|
+
|
|
283
|
+
expect(player.entity.position.x).toBeGreaterThan(140 * Math.sin(Math.PI / 4))
|
|
284
|
+
expect(player.entity.position.y).toEqual(60)
|
|
285
|
+
expect(player.entity.position.z).toBeLessThan(-140 * Math.sin(Math.PI / 4))
|
|
286
|
+
|
|
287
|
+
// turn around
|
|
288
|
+
player.entity.yaw = -Math.PI / 4 + Math.PI // south (z)
|
|
289
|
+
|
|
290
|
+
// jump
|
|
291
|
+
player.jumpQueued = true
|
|
292
|
+
passTicks(10, player, physics, fakeWorld, controls)
|
|
293
|
+
|
|
294
|
+
expect(player.entity.onGround).toBeFalsy()
|
|
295
|
+
|
|
296
|
+
// fly
|
|
297
|
+
player.entity.elytraFlying = true
|
|
298
|
+
passTicks(1, player, physics, fakeWorld, controls)
|
|
299
|
+
expect(player.entity.elytraFlying).toBeTruthy()
|
|
300
|
+
|
|
301
|
+
// boost
|
|
302
|
+
player.fireworkRocketDuration = 20
|
|
303
|
+
|
|
304
|
+
// wait til on ground
|
|
305
|
+
untilIdle(player, physics, fakeWorld, controls)
|
|
306
|
+
|
|
307
|
+
expect(player.entity.elytraFlying).toBeFalsy()
|
|
308
|
+
expect(player.fireworkRocketDuration).toEqual(0)
|
|
309
|
+
|
|
310
|
+
// should be back at start
|
|
311
|
+
expect(player.entity.position.x).toBeCloseTo(0)
|
|
312
|
+
expect(player.entity.position.y).toEqual(60)
|
|
313
|
+
expect(player.entity.position.z).toBeCloseTo(0)
|
|
314
|
+
})
|
|
315
|
+
|
|
316
|
+
it('stops flight without elytra', () => {
|
|
317
|
+
const physics = Physics(mcData, fakeWorld)
|
|
318
|
+
const controls = {
|
|
319
|
+
forward: false,
|
|
320
|
+
back: false,
|
|
321
|
+
left: false,
|
|
322
|
+
right: false,
|
|
323
|
+
jump: false,
|
|
324
|
+
sprint: false,
|
|
325
|
+
sneak: false
|
|
326
|
+
}
|
|
327
|
+
const player = fakePlayer(new Vec3(0, 80, 0))
|
|
328
|
+
player.inventory.slots[6] = { name: 'elytra' }
|
|
329
|
+
|
|
330
|
+
player.entity.elytraFlying = true
|
|
331
|
+
player.fireworkRocketDuration = 20
|
|
332
|
+
physics.simulatePlayer(new PlayerState(player, controls), fakeWorld).apply(player)
|
|
333
|
+
expect(player.entity.elytraFlying).toBeTruthy()
|
|
334
|
+
expect(player.fireworkRocketDuration).toEqual(19)
|
|
335
|
+
player.inventory.slots[6] = undefined
|
|
336
|
+
physics.simulatePlayer(new PlayerState(player, controls), fakeWorld).apply(player)
|
|
337
|
+
expect(player.entity.elytraFlying).toBeFalsy()
|
|
338
|
+
expect(player.fireworkRocketDuration).toEqual(0)
|
|
339
|
+
})
|
|
340
|
+
|
|
341
|
+
it('can fly out of water', () => {
|
|
342
|
+
const physics = Physics(mcData, waterWorld)
|
|
343
|
+
const controls = {
|
|
344
|
+
forward: false,
|
|
345
|
+
back: false,
|
|
346
|
+
left: false,
|
|
347
|
+
right: false,
|
|
348
|
+
jump: false,
|
|
349
|
+
sprint: false,
|
|
350
|
+
sneak: false
|
|
351
|
+
}
|
|
352
|
+
const player = fakePlayer(new Vec3(0, 40, 0))
|
|
353
|
+
player.inventory.slots[6] = { name: 'elytra' }
|
|
354
|
+
player.entity.pitch = 80 * Math.PI / 180
|
|
355
|
+
|
|
356
|
+
player.entity.elytraFlying = true
|
|
357
|
+
physics.simulatePlayer(new PlayerState(player, controls), waterWorld).apply(player)
|
|
358
|
+
expect(player.entity.isInWater).toBeTruthy()
|
|
359
|
+
expect(player.entity.elytraFlying).toBeTruthy()
|
|
360
|
+
physics.simulatePlayer(new PlayerState(player, controls), waterWorld).apply(player)
|
|
361
|
+
expect(player.entity.isInWater).toBeTruthy()
|
|
362
|
+
expect(player.entity.elytraFlying).toBeTruthy()
|
|
363
|
+
|
|
364
|
+
player.fireworkRocketDuration = 20
|
|
365
|
+
physics.simulatePlayer(new PlayerState(player, controls), waterWorld).apply(player)
|
|
366
|
+
expect(player.entity.elytraFlying).toBeTruthy()
|
|
367
|
+
expect(player.fireworkRocketDuration).toEqual(19)
|
|
368
|
+
|
|
369
|
+
const playerState = new PlayerState(player, controls)
|
|
370
|
+
while (playerState.isInWater) {
|
|
371
|
+
playerState.fireworkRocketDuration = 1
|
|
372
|
+
physics.simulatePlayer(playerState, waterWorld)
|
|
373
|
+
}
|
|
374
|
+
playerState.apply(player)
|
|
375
|
+
expect(player.fireworkRocketDuration).toEqual(0)
|
|
376
|
+
expect(player.entity.position.y).toBeGreaterThan(59)
|
|
377
|
+
})
|
|
378
|
+
|
|
379
|
+
it('can fly out of lava', () => {
|
|
380
|
+
const physics = Physics(mcData, lavaWorld)
|
|
381
|
+
const controls = {
|
|
382
|
+
forward: false,
|
|
383
|
+
back: false,
|
|
384
|
+
left: false,
|
|
385
|
+
right: false,
|
|
386
|
+
jump: false,
|
|
387
|
+
sprint: false,
|
|
388
|
+
sneak: false
|
|
389
|
+
}
|
|
390
|
+
const player = fakePlayer(new Vec3(0, 40, 0))
|
|
391
|
+
player.inventory.slots[6] = { name: 'elytra' }
|
|
392
|
+
player.entity.pitch = 80 * Math.PI / 180
|
|
393
|
+
|
|
394
|
+
player.entity.elytraFlying = true
|
|
395
|
+
physics.simulatePlayer(new PlayerState(player, controls), lavaWorld).apply(player)
|
|
396
|
+
expect(player.entity.isInLava).toBeTruthy()
|
|
397
|
+
expect(player.entity.elytraFlying).toBeTruthy()
|
|
398
|
+
physics.simulatePlayer(new PlayerState(player, controls), lavaWorld).apply(player)
|
|
399
|
+
expect(player.entity.isInLava).toBeTruthy()
|
|
400
|
+
expect(player.entity.elytraFlying).toBeTruthy()
|
|
401
|
+
|
|
402
|
+
player.fireworkRocketDuration = 20
|
|
403
|
+
physics.simulatePlayer(new PlayerState(player, controls), lavaWorld).apply(player)
|
|
404
|
+
expect(player.entity.elytraFlying).toBeTruthy()
|
|
405
|
+
expect(player.fireworkRocketDuration).toEqual(19)
|
|
406
|
+
|
|
407
|
+
const playerState = new PlayerState(player, controls)
|
|
408
|
+
while (playerState.isInLava) {
|
|
409
|
+
playerState.fireworkRocketDuration = 1
|
|
410
|
+
physics.simulatePlayer(playerState, lavaWorld)
|
|
411
|
+
}
|
|
412
|
+
playerState.apply(player)
|
|
413
|
+
expect(player.fireworkRocketDuration).toEqual(0)
|
|
414
|
+
expect(player.entity.position.y).toBeGreaterThan(59)
|
|
415
|
+
})
|
|
416
|
+
|
|
417
|
+
it('landing while jumping stops flight', () => {
|
|
418
|
+
const physics = Physics(mcData, fakeWorld)
|
|
419
|
+
const controls = {
|
|
420
|
+
forward: false,
|
|
421
|
+
back: false,
|
|
422
|
+
left: false,
|
|
423
|
+
right: false,
|
|
424
|
+
jump: false,
|
|
425
|
+
sprint: false,
|
|
426
|
+
sneak: false
|
|
427
|
+
}
|
|
428
|
+
const player = fakePlayer(new Vec3(0, 61, 0))
|
|
429
|
+
player.inventory.slots[6] = { name: 'elytra' }
|
|
430
|
+
|
|
431
|
+
// wait til on ground
|
|
432
|
+
untilIdle(player, physics, fakeWorld, controls)
|
|
433
|
+
expect(player.entity.position).toEqual(new Vec3(0, 60, 0))
|
|
434
|
+
|
|
435
|
+
// jump
|
|
436
|
+
player.jumpQueued = true
|
|
437
|
+
passTicks(10, player, physics, fakeWorld, controls)
|
|
438
|
+
expect(player.entity.onGround).toBeFalsy()
|
|
439
|
+
|
|
440
|
+
// fly
|
|
441
|
+
player.entity.elytraFlying = true
|
|
442
|
+
passTicks(1, player, physics, fakeWorld, controls)
|
|
443
|
+
|
|
444
|
+
// boost
|
|
445
|
+
player.fireworkRocketDuration = 20
|
|
446
|
+
passTicks(10, player, physics, fakeWorld, controls)
|
|
447
|
+
expect(player.entity.onGround).toBeFalsy()
|
|
448
|
+
|
|
449
|
+
controls.jump = true
|
|
450
|
+
const playerState = new PlayerState(player, controls)
|
|
451
|
+
while (!playerState.onGround) {
|
|
452
|
+
expect(playerState.elytraFlying).toBeTruthy()
|
|
453
|
+
physics.simulatePlayer(playerState, fakeWorld)
|
|
454
|
+
}
|
|
455
|
+
playerState.apply(player)
|
|
456
|
+
expect(player.entity.onGround).toBeTruthy()
|
|
457
|
+
expect(player.entity.elytraFlying).toBeFalsy()
|
|
458
|
+
|
|
459
|
+
// let jump controls cause a jump
|
|
460
|
+
passTicks(10, player, physics, fakeWorld, controls)
|
|
461
|
+
expect(player.entity.onGround).toBeFalsy()
|
|
462
|
+
expect(player.entity.elytraFlying).toBeFalsy()
|
|
463
|
+
})
|
|
464
|
+
|
|
465
|
+
it('cant fly in webs', () => {
|
|
466
|
+
const webPhysics = Physics(mcData, fakeWebWorld)
|
|
467
|
+
const airPhysics = Physics(mcData, fakeWorld)
|
|
468
|
+
const controls = {
|
|
469
|
+
forward: false,
|
|
470
|
+
back: false,
|
|
471
|
+
left: false,
|
|
472
|
+
right: false,
|
|
473
|
+
jump: false,
|
|
474
|
+
sprint: false,
|
|
475
|
+
sneak: false
|
|
476
|
+
}
|
|
477
|
+
const airPlayer = fakePlayer(new Vec3(0, 150, 0))
|
|
478
|
+
airPlayer.inventory.slots[6] = { name: 'elytra' }
|
|
479
|
+
airPlayer.entity.elytraFlying = true
|
|
480
|
+
passTicks(20 * 10, airPlayer, airPhysics, fakeWorld, controls)
|
|
481
|
+
|
|
482
|
+
const webPlayer = fakePlayer(new Vec3(0, 150, 0))
|
|
483
|
+
webPlayer.inventory.slots[6] = { name: 'elytra' }
|
|
484
|
+
webPlayer.entity.elytraFlying = true
|
|
485
|
+
passTicks(20 * 10, webPlayer, webPhysics, fakeWebWorld, controls)
|
|
486
|
+
|
|
487
|
+
// web player should have barely moved
|
|
488
|
+
expect(webPlayer.entity.position.x).toBeGreaterThan(0)
|
|
489
|
+
expect(webPlayer.entity.position.x).toBeLessThan(1)
|
|
490
|
+
expect(webPlayer.entity.position.y).toBeGreaterThan(149)
|
|
491
|
+
|
|
492
|
+
// elytra player should be far away and much lower
|
|
493
|
+
expect(airPlayer.entity.position.x).toBeGreaterThan(200)
|
|
494
|
+
expect(airPlayer.entity.position.y).toBeLessThan(110)
|
|
495
|
+
})
|
|
496
|
+
|
|
497
|
+
it('can hit a wall', () => {
|
|
498
|
+
const physics = Physics(mcData, fakeWallWorld)
|
|
499
|
+
const controls = {
|
|
500
|
+
forward: false,
|
|
501
|
+
back: false,
|
|
502
|
+
left: false,
|
|
503
|
+
right: false,
|
|
504
|
+
jump: false,
|
|
505
|
+
sprint: false,
|
|
506
|
+
sneak: false
|
|
507
|
+
}
|
|
508
|
+
const player = fakePlayer(new Vec3(0, 100, 0))
|
|
509
|
+
player.inventory.slots[6] = { name: 'elytra' }
|
|
510
|
+
player.entity.elytraFlying = true
|
|
511
|
+
player.fireworkRocketDuration = 100
|
|
512
|
+
|
|
513
|
+
// let bot accelerate from firework
|
|
514
|
+
passTicks(10, player, physics, fakeWallWorld, controls)
|
|
515
|
+
|
|
516
|
+
// should be moving 30 m/s (30 / 20 m/tick)
|
|
517
|
+
expect(player.entity.velocity.x).toBeGreaterThan(mpsToTps(30))
|
|
518
|
+
|
|
519
|
+
// until near wall
|
|
520
|
+
const playerState = new PlayerState(player, controls)
|
|
521
|
+
while (playerState.pos.x < 49) {
|
|
522
|
+
physics.simulatePlayer(playerState, fakeWallWorld)
|
|
523
|
+
}
|
|
524
|
+
playerState.apply(player)
|
|
525
|
+
|
|
526
|
+
// should still be moving fast
|
|
527
|
+
expect(player.entity.velocity.x).toBeGreaterThan(mpsToTps(30))
|
|
528
|
+
|
|
529
|
+
// 1 / (30/20) = 2/3, so 1 tick should be enough to hit the wall
|
|
530
|
+
passTicks(1, player, physics, fakeWallWorld, controls)
|
|
531
|
+
|
|
532
|
+
// it should have stopped in the x direction
|
|
533
|
+
expect(player.entity.velocity.x).toBeCloseTo(0)
|
|
534
|
+
})
|
|
535
|
+
|
|
536
|
+
it('can flight straight up', () => {
|
|
537
|
+
const physics = Physics(mcData, fakeWallWorld)
|
|
538
|
+
const controls = {
|
|
539
|
+
forward: false,
|
|
540
|
+
back: false,
|
|
541
|
+
left: false,
|
|
542
|
+
right: false,
|
|
543
|
+
jump: false,
|
|
544
|
+
sprint: false,
|
|
545
|
+
sneak: false
|
|
546
|
+
}
|
|
547
|
+
const player = fakePlayer(new Vec3(0, 100, 0))
|
|
548
|
+
player.inventory.slots[6] = { name: 'elytra' }
|
|
549
|
+
player.entity.elytraFlying = true
|
|
550
|
+
player.entity.pitch = Math.PI / 2
|
|
551
|
+
player.fireworkRocketDuration = 20
|
|
552
|
+
|
|
553
|
+
// let bot accelerate from firework
|
|
554
|
+
passTicks(20, player, physics, fakeWallWorld, controls)
|
|
555
|
+
|
|
556
|
+
// should be moving very fast
|
|
557
|
+
expect(player.entity.velocity.y).toBeGreaterThan(mpsToTps(25))
|
|
558
|
+
expect(player.entity.position.x).toBeCloseTo(0)
|
|
559
|
+
expect(player.entity.position.z).toBeCloseTo(0)
|
|
560
|
+
|
|
561
|
+
passTicks(20, player, physics, fakeWallWorld, controls)
|
|
562
|
+
expect(player.entity.velocity.y).toBeLessThan(mpsToTps(10))
|
|
563
|
+
expect(player.entity.position.x).toBeCloseTo(0)
|
|
564
|
+
expect(player.entity.position.z).toBeCloseTo(0)
|
|
565
|
+
|
|
566
|
+
passTicks(20, player, physics, fakeWallWorld, controls)
|
|
567
|
+
expect(player.entity.velocity.y).toBeLessThan(0)
|
|
568
|
+
expect(player.entity.position.x).toBeCloseTo(0)
|
|
569
|
+
expect(player.entity.position.z).toBeCloseTo(0)
|
|
570
|
+
|
|
571
|
+
passTicks(20, player, physics, fakeWallWorld, controls)
|
|
572
|
+
expect(player.entity.velocity.y).toBeLessThan(mpsToTps(20))
|
|
573
|
+
expect(player.entity.position.x).toBeCloseTo(0)
|
|
574
|
+
expect(player.entity.position.z).toBeCloseTo(0)
|
|
575
|
+
})
|
|
576
|
+
|
|
577
|
+
it('can flight straight down', () => {
|
|
578
|
+
const physics = Physics(mcData, fakeWallWorld)
|
|
579
|
+
const controls = {
|
|
580
|
+
forward: false,
|
|
581
|
+
back: false,
|
|
582
|
+
left: false,
|
|
583
|
+
right: false,
|
|
584
|
+
jump: false,
|
|
585
|
+
sprint: false,
|
|
586
|
+
sneak: false
|
|
587
|
+
}
|
|
588
|
+
const player = fakePlayer(new Vec3(0, 1000, 0))
|
|
589
|
+
player.inventory.slots[6] = { name: 'elytra' }
|
|
590
|
+
player.entity.elytraFlying = true
|
|
591
|
+
player.entity.pitch = -Math.PI / 2
|
|
592
|
+
player.fireworkRocketDuration = 20
|
|
593
|
+
|
|
594
|
+
// let bot accelerate from firework
|
|
595
|
+
passTicks(200, player, physics, fakeWallWorld, controls)
|
|
596
|
+
|
|
597
|
+
// should be moving very fast
|
|
598
|
+
expect(player.entity.position.y).toBeGreaterThan(100)
|
|
599
|
+
expect(player.entity.velocity.y).toBeLessThan(mpsToTps(-60))
|
|
600
|
+
expect(player.entity.position.x).toBeCloseTo(0)
|
|
601
|
+
expect(player.entity.position.z).toBeCloseTo(0)
|
|
602
|
+
|
|
603
|
+
// using firework actually slows the bot down
|
|
604
|
+
player.fireworkRocketDuration = 20
|
|
605
|
+
passTicks(10, player, physics, fakeWallWorld, controls)
|
|
606
|
+
|
|
607
|
+
// should be slower now
|
|
608
|
+
expect(player.entity.position.y).toBeGreaterThan(100)
|
|
609
|
+
expect(player.entity.velocity.y).toBeGreaterThan(mpsToTps(-40))
|
|
610
|
+
expect(player.entity.position.x).toBeCloseTo(0)
|
|
611
|
+
expect(player.entity.position.z).toBeCloseTo(0)
|
|
612
|
+
})
|
|
613
|
+
|
|
614
|
+
it('flies to point and then back', () => {
|
|
615
|
+
const physics = Physics(mcData, fakeWorld)
|
|
616
|
+
const controls = {
|
|
617
|
+
forward: false,
|
|
618
|
+
back: false,
|
|
619
|
+
left: false,
|
|
620
|
+
right: false,
|
|
621
|
+
jump: false,
|
|
622
|
+
sprint: false,
|
|
623
|
+
sneak: false
|
|
624
|
+
}
|
|
625
|
+
const player = fakePlayer(new Vec3(0, 61, 0))
|
|
626
|
+
player.inventory.slots[6] = { name: 'elytra' }
|
|
627
|
+
|
|
628
|
+
// wait til on ground
|
|
629
|
+
untilIdle(player, physics, fakeWorld, controls)
|
|
630
|
+
|
|
631
|
+
expect(player.entity.position).toEqual(new Vec3(0, 60, 0))
|
|
632
|
+
|
|
633
|
+
const unNormedFacing = new Vec3(300, 60, 400)
|
|
634
|
+
const facing = unNormedFacing.scale(1 / unNormedFacing.xzDistanceTo(new Vec3(0, 60, 0)))
|
|
635
|
+
|
|
636
|
+
// yaw is facing north (-z) and rotating to the left, west (-z), so
|
|
637
|
+
// atan2(-x, -z) is yaw
|
|
638
|
+
const yaw = Math.atan2(-facing.x, -facing.z)
|
|
639
|
+
player.entity.yaw = yaw
|
|
640
|
+
|
|
641
|
+
// jump
|
|
642
|
+
player.jumpQueued = true
|
|
643
|
+
passTicks(10, player, physics, fakeWorld, controls)
|
|
644
|
+
expect(player.entity.onGround).toBeFalsy()
|
|
645
|
+
|
|
646
|
+
// fly
|
|
647
|
+
player.entity.elytraFlying = true
|
|
648
|
+
passTicks(1, player, physics, fakeWorld, controls)
|
|
649
|
+
|
|
650
|
+
// boost
|
|
651
|
+
player.fireworkRocketDuration = 20
|
|
652
|
+
|
|
653
|
+
// wait til on ground
|
|
654
|
+
untilIdle(player, physics, fakeWorld, controls)
|
|
655
|
+
|
|
656
|
+
expect(player.entity.elytraFlying).toBeFalsy()
|
|
657
|
+
expect(player.fireworkRocketDuration).toEqual(0)
|
|
658
|
+
|
|
659
|
+
facing.scale(player.entity.position.xzDistanceTo(new Vec3(0, 60, 0)))
|
|
660
|
+
expect(player.entity.position.x).toBeCloseTo(facing.x)
|
|
661
|
+
expect(player.entity.position.y).toBeCloseTo(60)
|
|
662
|
+
expect(player.entity.position.z).toBeCloseTo(facing.z)
|
|
663
|
+
|
|
664
|
+
// turn around
|
|
665
|
+
player.entity.yaw += Math.PI
|
|
666
|
+
|
|
667
|
+
// jump
|
|
668
|
+
player.jumpQueued = true
|
|
669
|
+
passTicks(10, player, physics, fakeWorld, controls)
|
|
670
|
+
|
|
671
|
+
expect(player.entity.onGround).toBeFalsy()
|
|
672
|
+
|
|
673
|
+
// fly
|
|
674
|
+
player.entity.elytraFlying = true
|
|
675
|
+
passTicks(1, player, physics, fakeWorld, controls)
|
|
676
|
+
expect(player.entity.elytraFlying).toBeTruthy()
|
|
677
|
+
|
|
678
|
+
// boost
|
|
679
|
+
player.fireworkRocketDuration = 20
|
|
680
|
+
|
|
681
|
+
// wait til on ground
|
|
682
|
+
untilIdle(player, physics, fakeWorld, controls)
|
|
683
|
+
|
|
684
|
+
expect(player.entity.elytraFlying).toBeFalsy()
|
|
685
|
+
expect(player.fireworkRocketDuration).toEqual(0)
|
|
686
|
+
|
|
687
|
+
// should be back at start
|
|
688
|
+
expect(player.entity.position.x).toBeCloseTo(0)
|
|
689
|
+
expect(player.entity.position.y).toEqual(60)
|
|
690
|
+
expect(player.entity.position.z).toBeCloseTo(0)
|
|
691
|
+
})
|
|
692
|
+
})
|