Skip to content

Commit 04351b4

Browse files
authored
Add support for elytra and rockets (#106)
* Add support for elytra and rockets * Add elytra docs and tests * Elytra flying moved to entity * Fix looking dir comment math * Add more directions
1 parent 1f95aaa commit 04351b4

File tree

5 files changed

+855
-33
lines changed

5 files changed

+855
-33
lines changed

README.md

+7-2
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,13 @@ const player = {
3939
isInWeb: false,
4040
isCollidedHorizontally: false,
4141
isCollidedVertically: false,
42-
yaw: 0
42+
elytraFlying: false,
43+
yaw: 0,
44+
pitch: 0
4345
},
4446
jumpTicks: 0,
45-
jumpQueued: false
47+
jumpQueued: false,
48+
fireworkRocketDuration: 0
4649
}
4750
const playerState = new PlayerState(player, controls)
4851

@@ -76,8 +79,10 @@ Read / Write properties:
7679
- isInWeb : (boolean) is the player in a web ?
7780
- isCollidedHorizontally : (boolean) is the player collided horizontally with a solid block ?
7881
- isCollidedVertically : (boolean) is the player collided vertically with a solid block ?
82+
- elytraFlying : (boolean) is the player elytra flying ?
7983
- jumpTicks : (integer) number of ticks before the player can auto-jump again
8084
- 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 ?
8186

8287
Read only properties:
8388
- yaw : (float) the yaw angle, in radians, of the player entity

examples/basic.js

+3
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,19 @@ function fakePlayer (pos, baseVersion) {
2222
isInWater: false,
2323
isInLava: false,
2424
isInWeb: false,
25+
elytraFlying: false,
2526
isCollidedHorizontally: false,
2627
isCollidedVertically: false,
2728
yaw: 0,
29+
pitch: 0,
2830
effects: []
2931
},
3032
inventory: {
3133
slots: []
3234
},
3335
jumpTicks: 0,
3436
jumpQueued: false,
37+
fireworkRocketDuration: 0,
3538
version: baseVersion
3639
}
3740
}

index.js

+150-31
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,64 @@ function Physics (mcData, world) {
343343
}
344344
}
345345

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+
346404
function applyHeading (entity, strafe, forward, multiplier) {
347405
let speed = Math.sqrt(strafe * strafe + forward * forward)
348406
if (speed < 0.01) return new Vec3(0, 0, 0)
@@ -377,7 +435,76 @@ function Physics (mcData, world) {
377435

378436
const gravityMultiplier = (vel.y <= 0 && entity.slowFalling > 0) ? physics.slowFalling : 1
379437

380-
if (!entity.isInWater && !entity.isInLava) {
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 {
381508
// Normal movement
382509
let acceleration = 0.0
383510
let inertia = 0.0
@@ -442,36 +569,6 @@ function Physics (mcData, world) {
442569
vel.y *= physics.airdrag
443570
vel.x *= inertia
444571
vel.z *= inertia
445-
} else {
446-
// Water / Lava movement
447-
const lastY = pos.y
448-
let acceleration = physics.liquidAcceleration
449-
const inertia = entity.isInWater ? physics.waterInertia : physics.lavaInertia
450-
let horizontalInertia = inertia
451-
452-
if (entity.isInWater) {
453-
let strider = Math.min(entity.depthStrider, 3)
454-
if (!entity.onGround) {
455-
strider *= 0.5
456-
}
457-
if (strider > 0) {
458-
horizontalInertia += (0.546 - horizontalInertia) * strider / 3
459-
acceleration += (0.7 - acceleration) * strider / 3
460-
}
461-
462-
if (entity.dolphinsGrace > 0) horizontalInertia = 0.96
463-
}
464-
465-
applyHeading(entity, strafe, forward, acceleration)
466-
moveEntity(entity, world, vel.x, vel.y, vel.z)
467-
vel.y *= inertia
468-
vel.y -= (entity.isInWater ? physics.waterGravity : physics.lavaGravity) * gravityMultiplier
469-
vel.x *= horizontalInertia
470-
vel.z *= horizontalInertia
471-
472-
if (entity.isCollidedHorizontally && doesNotCollide(world, pos.offset(vel.x, vel.y + 0.6 - pos.y + lastY, vel.z))) {
473-
vel.y = physics.outOfLiquidImpulse // jump out of liquid
474-
}
475572
}
476573
}
477574

@@ -617,6 +714,20 @@ function Physics (mcData, world) {
617714
forward *= physics.sneakSpeed
618715
}
619716

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+
620731
moveEntityWithHeading(entity, world, strafe, forward)
621732

622733
return entity
@@ -669,8 +780,10 @@ class PlayerState {
669780
this.isInWeb = bot.entity.isInWeb
670781
this.isCollidedHorizontally = bot.entity.isCollidedHorizontally
671782
this.isCollidedVertically = bot.entity.isCollidedVertically
783+
this.elytraFlying = bot.entity.elytraFlying
672784
this.jumpTicks = bot.jumpTicks
673785
this.jumpQueued = bot.jumpQueued
786+
this.fireworkRocketDuration = bot.fireworkRocketDuration
674787

675788
// Input only (not modified)
676789
this.attributes = bot.entity.attributes
@@ -698,6 +811,10 @@ class PlayerState {
698811
} else {
699812
this.depthStrider = 0
700813
}
814+
815+
// extra elytra requirements
816+
const item = bot.inventory.slots[6]
817+
this.elytraEquipped = item != null && item.name === 'elytra'
701818
}
702819

703820
apply (bot) {
@@ -709,8 +826,10 @@ class PlayerState {
709826
bot.entity.isInWeb = this.isInWeb
710827
bot.entity.isCollidedHorizontally = this.isCollidedHorizontally
711828
bot.entity.isCollidedVertically = this.isCollidedVertically
829+
bot.entity.elytraFlying = this.elytraFlying
712830
bot.jumpTicks = this.jumpTicks
713831
bot.jumpQueued = this.jumpQueued
832+
bot.fireworkRocketDuration = this.fireworkRocketDuration
714833
}
715834
}
716835

test/basic.test.js

+3
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,14 @@ function fakePlayer (pos) {
2727
isInWeb: false,
2828
isCollidedHorizontally: false,
2929
isCollidedVertically: false,
30+
elytraFlying: false,
3031
yaw: 0,
32+
pitch: 0,
3133
effects: {}
3234
},
3335
jumpTicks: 0,
3436
jumpQueued: false,
37+
fireworkRocketDuration: 0,
3538
version: '1.13.2',
3639
inventory: {
3740
slots: []

0 commit comments

Comments
 (0)