UNPKG

@nxg-org/mineflayer-tracker

Version:

Provides functionality for more accurate entity and projectile tracking.

715 lines (714 loc) 32 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.Physics = exports.CheapEnchantments = exports.CheapEffects = void 0; const vec3_1 = require("vec3"); const mineflayer_util_plugin_1 = require("@nxg-org/mineflayer-util-plugin"); const math = __importStar(require("../distTwo/math")); const attributes = __importStar(require("../distTwo/attributes")); const physicsUtils_1 = require("../extras/physicsUtils"); const physicsSettings_1 = require("../extras/physicsSettings"); /** * Looking at this code, it's too specified towards players. * * I will eventually split this code into PlayerState and bot.entityState, where bot.entityState contains fewer controls. */ var CheapEffects; (function (CheapEffects) { CheapEffects[CheapEffects["SPEED"] = 0] = "SPEED"; CheapEffects[CheapEffects["JUMP_BOOST"] = 1] = "JUMP_BOOST"; CheapEffects[CheapEffects["SLOWNESS"] = 2] = "SLOWNESS"; CheapEffects[CheapEffects["DOLPHINS_GRACE"] = 3] = "DOLPHINS_GRACE"; CheapEffects[CheapEffects["SLOW_FALLING"] = 4] = "SLOW_FALLING"; CheapEffects[CheapEffects["LEVITATION"] = 5] = "LEVITATION"; })(CheapEffects || (exports.CheapEffects = CheapEffects = {})); var CheapEnchantments; (function (CheapEnchantments) { CheapEnchantments[CheapEnchantments["DEPTH_STRIDER"] = 0] = "DEPTH_STRIDER"; })(CheapEnchantments || (exports.CheapEnchantments = CheapEnchantments = {})); class Physics { constructor(mcData) { this.data = mcData; this.supportFeature = (0, physicsUtils_1.makeSupportFeature)(mcData); this.settings = new physicsSettings_1.PhysicsSettings(this); const blocksByName = mcData.blocksByName; this.blockSlipperiness = {}; this.slimeBlockId = blocksByName.slime_block ? blocksByName.slime_block.id : blocksByName.slime.id; this.blockSlipperiness[this.slimeBlockId] = 0.8; this.blockSlipperiness[blocksByName.ice.id] = 0.98; this.blockSlipperiness[blocksByName.packed_ice.id] = 0.98; if (blocksByName.frosted_ice) { // 1.9+ this.blockSlipperiness[blocksByName.frosted_ice.id] = 0.98; } if (blocksByName.blue_ice) { // 1.13+ this.blockSlipperiness[blocksByName.blue_ice.id] = 0.989; } this.soulsandId = blocksByName.soul_sand.id; this.honeyblockId = blocksByName.honey_block ? blocksByName.honey_block.id : -1; // 1.15+ this.webId = blocksByName.cobweb ? blocksByName.cobweb.id : blocksByName.web.id; this.waterId = blocksByName.water.id; this.lavaId = blocksByName.lava.id; this.ladderId = blocksByName.ladder.id; this.vineId = blocksByName.vine.id; this.waterLike = new Set(); if (blocksByName.seagrass) this.waterLike.add(blocksByName.seagrass.id); // 1.13+ if (blocksByName.tall_seagrass) this.waterLike.add(blocksByName.tall_seagrass.id); // 1.13+ if (blocksByName.kelp) this.waterLike.add(blocksByName.kelp.id); // 1.13+ this.bubblecolumnId = blocksByName.bubble_column ? blocksByName.bubble_column.id : -1; // 1.13+ if (blocksByName.bubble_column) this.waterLike.add(this.bubblecolumnId); this.statusEffectNames = {}; // mmm, speed. this.enchantmentNames = {}; //mmm, double speed. let ind = 0; const tmp = (0, physicsUtils_1.getStatusEffectNamesForVersion)((0, physicsUtils_1.makeSupportFeature)(this.data)); for (const key in tmp) { this.statusEffectNames[ind] = tmp[key]; ind++; } ind = 0; const tmp1 = (0, physicsUtils_1.getEnchantmentNamesForVersion)((0, physicsUtils_1.makeSupportFeature)(this.data)); for (const key in tmp1) { this.enchantmentNames[ind] = tmp1[key]; } } getPlayerBB(pos) { const w = this.settings.playerHalfWidth; return new mineflayer_util_plugin_1.AABB(-w, 0, -w, w, this.settings.playerHeight, w).offset(pos.x, pos.y, pos.z); } setPositionToBB(bb, pos) { pos.x = bb.minX + this.settings.playerHalfWidth; pos.y = bb.minY; pos.z = bb.minZ + this.settings.playerHalfWidth; } getUnderlyingBlockBBs(queryBB, world /*prismarine-world*/) { const surroundingBBs = []; const cursor = new vec3_1.Vec3(0, Math.floor(queryBB.minY) - 0.251, 0); for (cursor.z = Math.floor(queryBB.minZ); cursor.z <= Math.floor(queryBB.maxZ); cursor.z++) { for (cursor.x = Math.floor(queryBB.minX); cursor.x <= Math.floor(queryBB.maxX); cursor.x++) { const block = world.getBlock(cursor); if (block) { const blockPos = block.position; for (const shape of block.shapes) { const blockBB = new mineflayer_util_plugin_1.AABB(shape[0], shape[1], shape[2], shape[3], shape[4], shape[5]); blockBB.offset(blockPos.x, blockPos.y, blockPos.z); surroundingBBs.push(blockBB); } } } } return surroundingBBs; } getSurroundingBBs(queryBB, world /*prismarine-world*/) { const surroundingBBs = []; const cursor = new vec3_1.Vec3(0, 0, 0); for (cursor.y = Math.floor(queryBB.minY) - 1; cursor.y <= Math.floor(queryBB.maxY); cursor.y++) { for (cursor.z = Math.floor(queryBB.minZ); cursor.z <= Math.floor(queryBB.maxZ); cursor.z++) { for (cursor.x = Math.floor(queryBB.minX); cursor.x <= Math.floor(queryBB.maxX); cursor.x++) { const block = world.getBlock(cursor); if (block) { const blockPos = block.position; for (const shape of block.shapes) { const blockBB = new mineflayer_util_plugin_1.AABB(shape[0], shape[1], shape[2], shape[3], shape[4], shape[5]); blockBB.offset(blockPos.x, blockPos.y, blockPos.z); surroundingBBs.push(blockBB); } } } } } return surroundingBBs; } adjustPositionHeight(pos, world /*prismarine-world*/) { const playerBB = this.getPlayerBB(pos); const queryBB = playerBB.clone().extend(0, -1, 0); const surroundingBBs = this.getSurroundingBBs(queryBB, world); let dy = -1; for (const blockBB of surroundingBBs) { dy = blockBB.computeOffsetY(playerBB, dy); } pos.y += dy; } moveEntity(entity, dx, dy, dz, world /*prismarine-world*/) { const vel = entity.velocity; const pos = entity.position; if (entity.isInWeb) { dx *= 0.25; dy *= 0.05; dz *= 0.25; vel.x = 0; vel.y = 0; vel.z = 0; entity.isInWeb = false; } const oldOldVelX = dx; let oldVelX = dx; const oldVelY = dy; let oldVelZ = dz; const oldOldVelZ = dz; if (entity.controlState.sneak && entity.onGround) { const step = 0.05; // In the 3 loops bellow, y offset should be -1, but that doesnt reproduce vanilla behavior. for (; dx !== 0 && this.getSurroundingBBs(this.getPlayerBB(pos).offset(dx, 0, 0), world).length === 0; oldVelX = dx) { if (dx < step && dx >= -step) dx = 0; else if (dx > 0) dx -= step; else dx += step; } for (; dz !== 0 && this.getSurroundingBBs(this.getPlayerBB(pos).offset(0, 0, dz), world).length === 0; oldVelZ = dz) { if (dz < step && dz >= -step) dz = 0; else if (dz > 0) dz -= step; else dz += step; } while (dx !== 0 && dz !== 0 && this.getSurroundingBBs(this.getPlayerBB(pos).offset(dx, 0, dz), world).length === 0) { if (dx < step && dx >= -step) dx = 0; else if (dx > 0) dx -= step; else dx += step; if (dz < step && dz >= -step) dz = 0; else if (dz > 0) dz -= step; else dz += step; oldVelX = dx; oldVelZ = dz; } } let playerBB = this.getPlayerBB(pos); const queryBB = playerBB.clone().extend(dx, dy, dz); const surroundingBBs = this.getSurroundingBBs(queryBB, world); const oldBB = playerBB.clone(); for (const blockBB of surroundingBBs) { dy = blockBB.computeOffsetY(playerBB, dy); } playerBB.offset(0, dy, 0); for (const blockBB of surroundingBBs) { dx = blockBB.computeOffsetX(playerBB, dx); } playerBB.offset(dx, 0, 0); for (const blockBB of surroundingBBs) { dz = blockBB.computeOffsetZ(playerBB, dz); } playerBB.offset(0, 0, dz); // Step on block if height < stepHeight if (this.settings.stepHeight > 0 && (entity.onGround || (dy !== oldVelY && oldVelY < 0)) && (dx !== oldVelX || dz !== oldVelZ)) { const oldVelXCol = dx; const oldVelYCol = dy; const oldVelZCol = dz; const oldBBCol = playerBB.clone(); dy = this.settings.stepHeight; const queryBB = oldBB.clone().extend(oldVelX, dy, oldVelZ); const surroundingBBs = this.getSurroundingBBs(queryBB, world); const BB1 = oldBB.clone(); const BB2 = oldBB.clone(); const BB_XZ = BB1.clone().extend(dx, 0, dz); let dy1 = dy; let dy2 = dy; for (const blockBB of surroundingBBs) { dy1 = blockBB.computeOffsetY(BB_XZ, dy1); dy2 = blockBB.computeOffsetY(BB2, dy2); } BB1.offset(0, dy1, 0); BB2.offset(0, dy2, 0); let dx1 = oldVelX; let dx2 = oldVelX; for (const blockBB of surroundingBBs) { dx1 = blockBB.computeOffsetX(BB1, dx1); dx2 = blockBB.computeOffsetX(BB2, dx2); } BB1.offset(dx1, 0, 0); BB2.offset(dx2, 0, 0); let dz1 = oldVelZ; let dz2 = oldVelZ; for (const blockBB of surroundingBBs) { dz1 = blockBB.computeOffsetZ(BB1, dz1); dz2 = blockBB.computeOffsetZ(BB2, dz2); } BB1.offset(0, 0, dz1); BB2.offset(0, 0, dz2); const norm1 = dx1 * dx1 + dz1 * dz1; const norm2 = dx2 * dx2 + dz2 * dz2; if (norm1 > norm2) { dx = dx1; dy = -dy1; dz = dz1; playerBB = BB1; } else { dx = dx2; dy = -dy2; dz = dz2; playerBB = BB2; } for (const blockBB of surroundingBBs) { dy = blockBB.computeOffsetY(playerBB, dy); } playerBB.offset(0, dy, 0); if (oldVelXCol * oldVelXCol + oldVelZCol * oldVelZCol >= dx * dx + dz * dz) { dx = oldVelXCol; dy = oldVelYCol; dz = oldVelZCol; playerBB = oldBBCol; } } // Update flags this.setPositionToBB(playerBB, pos); entity.sneakCollision = dx !== oldOldVelX || dz !== oldOldVelZ; entity.isCollidedHorizontally = dx !== oldVelX || dz !== oldVelZ; entity.isCollidedVertically = dy !== oldVelY; entity.onGround = entity.isCollidedVertically && oldVelY < 0; const blockAtFeet = world.getBlock(pos.offset(0, -0.2, 0)); if (dx !== oldVelX) vel.x = 0; if (dz !== oldVelZ) vel.z = 0; if (dy !== oldVelY) { if (blockAtFeet && blockAtFeet.type === this.slimeBlockId && !entity.controlState.sneak) { vel.y = -vel.y; } else { vel.y = 0; } } // Finally, apply block collisions (web, soulsand...) playerBB.contract(0.001, 0.001, 0.001); const cursor = new vec3_1.Vec3(0, 0, 0); for (cursor.y = Math.floor(playerBB.minY); cursor.y <= Math.floor(playerBB.maxY); cursor.y++) { for (cursor.z = Math.floor(playerBB.minZ); cursor.z <= Math.floor(playerBB.maxZ); cursor.z++) { for (cursor.x = Math.floor(playerBB.minX); cursor.x <= Math.floor(playerBB.maxX); cursor.x++) { const block = world.getBlock(cursor); if (block) { if (this.supportFeature("velocityBlocksOnCollision")) { if (block.type === this.soulsandId) { vel.x *= this.settings.soulsandSpeed; vel.z *= this.settings.soulsandSpeed; } else if (block.type === this.honeyblockId) { vel.x *= this.settings.honeyblockSpeed; vel.z *= this.settings.honeyblockSpeed; } } if (block.type === this.webId) { entity.isInWeb = true; } else if (block.type === this.bubblecolumnId) { const down = !block.metadata; const aboveBlock = world.getBlock(cursor.offset(0, 1, 0)); const bubbleDrag = aboveBlock && aboveBlock.type === 0 /* air */ ? this.settings.bubbleColumnSurfaceDrag : this.settings.bubbleColumnDrag; if (down) { vel.y = Math.max(bubbleDrag.maxDown, vel.y - bubbleDrag.down); } else { vel.y = Math.min(bubbleDrag.maxUp, vel.y + bubbleDrag.up); } } } } } } if (this.supportFeature("velocityBlocksOnTop")) { const blockBelow = world.getBlock(entity.position.floored().offset(0, -0.5, 0)); if (blockBelow) { if (blockBelow.type === this.soulsandId) { vel.x *= this.settings.soulsandSpeed; vel.z *= this.settings.soulsandSpeed; } else if (blockBelow.type === this.honeyblockId) { vel.x *= this.settings.honeyblockSpeed; vel.z *= this.settings.honeyblockSpeed; } } } } applyHeading(entity, strafe, forward, multiplier) { let speed = Math.sqrt(strafe * strafe + forward * forward); if (speed < 0.01) return new vec3_1.Vec3(0, 0, 0); speed = multiplier / Math.max(speed, 1); strafe *= speed; forward *= speed; const yaw = Math.PI - entity.yaw; const sin = Math.sin(yaw); const cos = Math.cos(yaw); const vel = entity.velocity; vel.x += strafe * cos - forward * sin; vel.z += forward * cos + strafe * sin; } getEffectLevelCustom(wantedEffect, effects) { const effectDescriptor = this.data.effectsByName[this.statusEffectNames[wantedEffect]]; if (!effectDescriptor) { return 0; } const effectInfo = effects[effectDescriptor.id]; if (!effectInfo) { return 0; } return effectInfo.amplifier + 1; } getEnchantmentLevelCustom(wantedEnchantment, enchantments) { const enchantmentName = this.enchantmentNames[wantedEnchantment]; const enchantmentDescriptor = this.data.enchantmentsByName[enchantmentName]; if (!enchantmentDescriptor) { return 0; } for (const enchInfo of enchantments) { if (typeof enchInfo.id === "string") { if (enchInfo.id.includes(enchantmentName)) { return enchInfo.lvl; } } else if (enchInfo.id === enchantmentDescriptor.id) { return enchInfo.lvl; } } return 0; } getEffectLevel(effectName, effects) { const effectDescriptor = this.data.effectsByName[effectName]; if (!effectDescriptor) { return 0; } const effectInfo = effects[effectDescriptor.id]; if (!effectInfo) { return 0; } return effectInfo.amplifier + 1; } /** * Slightly modified since I cannot find the typing. */ getEnchantmentLevelTest(enchantmentName, enchantments) { const enchantmentDescriptor = this.data.enchantmentsByName[enchantmentName]; if (!enchantmentDescriptor) { return 0; } for (const enchInfo of enchantments) { if (typeof enchInfo.name === "string") { if (enchInfo.name.includes(enchantmentName)) { return enchInfo.lvl; } } else if (enchInfo.name === enchantmentDescriptor.id) { return enchInfo.lvl; } } return 0; } getEnchantmentLevel(enchantmentName, enchantments) { const enchantmentDescriptor = this.data.enchantmentsByName[enchantmentName]; if (!enchantmentDescriptor) { return 0; } for (const enchInfo of enchantments) { if (typeof enchInfo.id === "string") { if (enchInfo.id.includes(enchantmentName)) { return enchInfo.lvl; } } else if (enchInfo.id === enchantmentDescriptor.id) { return enchInfo.lvl; } } return 0; } isOnLadder(pos, world /*prismarine-world*/) { const block = world.getBlock(pos); return block && (block.type === this.ladderId || block.type === this.vineId); } doesNotCollide(pos, world /*prismarine-world*/) { const pBB = this.getPlayerBB(pos); return !this.getSurroundingBBs(pBB, world).some((x) => pBB.intersects(x)) && this.getWaterInBB(pBB, world).length === 0; } isMaterialInBB(queryBB, type, world /*prismarine-world*/) { const cursor = new vec3_1.Vec3(0, 0, 0); for (cursor.y = Math.floor(queryBB.minY); cursor.y <= Math.floor(queryBB.maxY); cursor.y++) { for (cursor.z = Math.floor(queryBB.minZ); cursor.z <= Math.floor(queryBB.maxZ); cursor.z++) { for (cursor.x = Math.floor(queryBB.minX); cursor.x <= Math.floor(queryBB.maxX); cursor.x++) { const block = world.getBlock(cursor); if (block && block.type === type) return true; } } } return false; } getWaterInBB(bb, world /*prismarine-world*/) { const waterBlocks = []; const cursor = new vec3_1.Vec3(0, 0, 0); for (cursor.y = Math.floor(bb.minY); cursor.y <= Math.floor(bb.maxY); cursor.y++) { for (cursor.z = Math.floor(bb.minZ); cursor.z <= Math.floor(bb.maxZ); cursor.z++) { for (cursor.x = Math.floor(bb.minX); cursor.x <= Math.floor(bb.maxX); cursor.x++) { const block = world.getBlock(cursor); if (block && (block.type === this.waterId || this.waterLike.has(block.type) || block.getProperties().waterlogged)) { const waterLevel = cursor.y + 1 - this.getLiquidHeightPcent(block); if (Math.ceil(bb.maxY) >= waterLevel) waterBlocks.push(block); } } } } return waterBlocks; } getLiquidHeightPcent(block) { return (this.getRenderedDepth(block) + 1) / 9; } getRenderedDepth(block) { if (!block) return -1; if (this.waterLike.has(block.type)) return 0; if (block.getProperties().waterlogged) return 0; if (block.type !== this.waterId) return -1; const meta = block.metadata; return meta >= 8 ? 0 : meta; } getFlow(block, world /*prismarine-world*/) { const curlevel = this.getRenderedDepth(block); const flow = new vec3_1.Vec3(0, 0, 0); for (const [dx, dz] of [ [0, 1], [-1, 0], [0, -1], [1, 0], ]) { const adjBlock = world.getBlock(block.position.offset(dx, 0, dz)); const adjLevel = this.getRenderedDepth(adjBlock); if (adjLevel < 0) { if (adjBlock && adjBlock.boundingBox !== "empty") { const adjLevel = this.getRenderedDepth(world.getBlock(block.position.offset(dx, -1, dz))); if (adjLevel >= 0) { const f = adjLevel - (curlevel - 8); flow.x += dx * f; flow.z += dz * f; } } } else { const f = adjLevel - curlevel; flow.x += dx * f; flow.z += dz * f; } } if (block.metadata >= 8) { for (const [dx, dz] of [ [0, 1], [-1, 0], [0, -1], [1, 0], ]) { const adjBlock = world.getBlock(block.position.offset(dx, 0, dz)); const adjUpBlock = world.getBlock(block.position.offset(dx, 1, dz)); if ((adjBlock && adjBlock.boundingBox !== "empty") || (adjUpBlock && adjUpBlock.boundingBox !== "empty")) { flow.normalize().translate(0, -6, 0); } } } return flow.normalize(); } isInWaterApplyCurrent(bb, vel, world /*prismarine-world*/) { const acceleration = new vec3_1.Vec3(0, 0, 0); const waterBlocks = this.getWaterInBB(bb, world); const isInWater = waterBlocks.length > 0; for (const block of waterBlocks) { const flow = this.getFlow(block, world); acceleration.add(flow); } const len = acceleration.norm(); if (len > 0) { vel.x += (acceleration.x / len) * 0.014; vel.y += (acceleration.y / len) * 0.014; vel.z += (acceleration.z / len) * 0.014; } return isInWater; } moveEntityWithHeading(entity, strafe, forward, world /*prismarine-world*/) { const vel = entity.velocity; const pos = entity.position; const gravityMultiplier = vel.y <= 0 && entity.slowFalling > 0 ? this.settings.slowFalling : 1; if (!entity.isInWater && !entity.isInLava) { // Normal movement let acceleration = this.settings.airborneAcceleration; let inertia = this.settings.airborneInertia; const blockUnder = world.getBlock(pos.offset(0, -1, 0)); if (entity.onGround && blockUnder) { let playerSpeedAttribute; if (entity.attributes && entity.attributes[this.settings.movementSpeedAttribute]) { // Use server-side player attributes playerSpeedAttribute = entity.attributes[this.settings.movementSpeedAttribute]; } else { // Create an attribute if the player does not have it playerSpeedAttribute = attributes.createAttributeValue(this.settings.playerSpeed); } // Client-side sprinting (don't rely on server-side sprinting) // setSprinting in LivingEntity.java playerSpeedAttribute = attributes.deleteAttributeModifier(playerSpeedAttribute, this.settings.sprintingUUID); // always delete sprinting (if it exists) if (entity.controlState.sprint) { if (!attributes.checkAttributeModifier(playerSpeedAttribute, this.settings.sprintingUUID)) { playerSpeedAttribute = attributes.addAttributeModifier(playerSpeedAttribute, { uuid: this.settings.sprintingUUID, amount: this.settings.sprintSpeed, operation: 2, }); } } // Calculate what the speed is (0.1 if no modification) const attributeSpeed = attributes.getAttributeValue(playerSpeedAttribute); inertia = (this.blockSlipperiness[blockUnder.type] || this.settings.defaultSlipperiness) * 0.91; acceleration = attributeSpeed * (0.1627714 / (inertia * inertia * inertia)); if (acceleration < 0) acceleration = 0; // acceleration should not be negative } this.applyHeading(entity, strafe, forward, acceleration); if (this.isOnLadder(pos, world)) { vel.x = math.clamp(-this.settings.ladderMaxSpeed, vel.x, this.settings.ladderMaxSpeed); vel.z = math.clamp(-this.settings.ladderMaxSpeed, vel.z, this.settings.ladderMaxSpeed); vel.y = Math.max(vel.y, entity.controlState.sneak ? 0 : -this.settings.ladderMaxSpeed); } this.moveEntity(entity, vel.x, vel.y, vel.z, world); if (this.isOnLadder(pos, world) && (entity.isCollidedHorizontally || (this.supportFeature("climbUsingJump") && entity.controlState.jump))) { vel.y = this.settings.ladderClimbSpeed; // climb ladder } // Apply friction and gravity if (entity.levitation > 0) { vel.y += (0.05 * entity.levitation - vel.y) * 0.2; } else { vel.y -= this.settings.gravity * gravityMultiplier; } vel.y *= this.settings.airdrag; vel.x *= inertia; vel.z *= inertia; } else { // Water / Lava movement const lastY = pos.y; let acceleration = this.settings.liquidAcceleration; const inertia = entity.isInWater ? this.settings.waterInertia : this.settings.lavaInertia; let horizontalInertia = inertia; if (entity.isInWater) { let strider = Math.min(entity.depthStrider, 3); if (!entity.onGround) { strider *= 0.5; } if (strider > 0) { horizontalInertia += ((0.546 - horizontalInertia) * strider) / 3; acceleration += ((0.7 - acceleration) * strider) / 3; } if (entity.dolphinsGrace > 0) horizontalInertia = 0.96; } this.applyHeading(entity, strafe, forward, acceleration); this.moveEntity(entity, vel.x, vel.y, vel.z, world); vel.y *= inertia; vel.y -= (entity.isInWater ? this.settings.waterGravity : this.settings.lavaGravity) * gravityMultiplier; vel.x *= horizontalInertia; vel.z *= horizontalInertia; if (entity.isCollidedHorizontally && this.doesNotCollide(pos.offset(vel.x, vel.y + 0.6 - pos.y + lastY, vel.z), world)) { vel.y = this.settings.outOfLiquidImpulse; // jump out of liquid } } } simulatePlayer(entity, world /*prismarine-world*/) { const vel = entity.velocity; const pos = entity.position; const waterBB = this.getPlayerBB(pos).contract(0.001, 0.401, 0.001); const lavaBB = this.getPlayerBB(pos).contract(0.1, 0.4, 0.1); entity.isInWater = this.isInWaterApplyCurrent(waterBB, vel, world); entity.isInLava = this.isMaterialInBB(lavaBB, this.lavaId, world); // Reset velocity component if it falls under the threshold if (Math.abs(vel.x) < this.settings.negligeableVelocity) vel.x = 0; if (Math.abs(vel.y) < this.settings.negligeableVelocity) vel.y = 0; if (Math.abs(vel.z) < this.settings.negligeableVelocity) vel.z = 0; // Handle inputs if (entity.controlState.jump || entity.jumpQueued) { if (entity.jumpTicks > 0) entity.jumpTicks--; if (entity.isInWater || entity.isInLava) { vel.y += 0.04; //0.03999999910593033 } else if (entity.onGround && entity.jumpTicks === 0) { const blockBelow = world.getBlock(entity.position.floored().offset(0, -0.5, 0)); vel.y = Math.fround(0.42) * (blockBelow && blockBelow.type === this.honeyblockId ? this.settings.honeyblockJumpSpeed : 1); if (entity.jumpBoost > 0) { vel.y += 0.1 * entity.jumpBoost; } if (entity.controlState.sprint) { const yaw = Math.PI - entity.yaw; vel.x -= Math.sin(yaw) * 0.2; vel.z += Math.cos(yaw) * 0.2; } entity.jumpTicks = this.settings.autojumpCooldown; } } else { entity.jumpTicks = 0; // reset autojump cooldown } entity.jumpQueued = false; let strafe = (entity.controlState.right - entity.controlState.left) * 0.98; let forward = (entity.controlState.forward - entity.controlState.back) * 0.98; if (entity.controlState.sneak) { strafe *= this.settings.sneakSpeed; forward *= this.settings.sneakSpeed; } if (entity.isUsingItem) { strafe *= this.settings.usingItemSpeed; forward *= this.settings.usingItemSpeed; } this.moveEntityWithHeading(entity, strafe, forward, world); return entity; } } exports.Physics = Physics;