作者:mumy
B站:https://space.bilibili.com/363239963
这是个教学性脚本,可用于学习如何编写寻路
import Color = JVM.java$.awt$.Color;
import JavaBoolean = JVM.java$.lang$.Boolean;
import JavaFloat = JVM.java$.lang$.Float;
import JavaInteger = JVM.java$.lang$.Integer;
import JavaString = JVM.java$.lang$.String;
import AttackEvent = JVM.net$.ccbluex$.liquidbounce$.event$.AttackEvent;
import ClickBlockEvent = JVM.net$.ccbluex$.liquidbounce$.event$.ClickBlockEvent;
import JumpEvent = JVM.net$.ccbluex$.liquidbounce$.event$.JumpEvent;
import KeyEvent = JVM.net$.ccbluex$.liquidbounce$.event$.KeyEvent;
import MotionEvent = JVM.net$.ccbluex$.liquidbounce$.event$.MotionEvent;
import MoveEvent = JVM.net$.ccbluex$.liquidbounce$.event$.MoveEvent;
import PacketEvent = JVM.net$.ccbluex$.liquidbounce$.event$.PacketEvent;
import Render2DEvent = JVM.net$.ccbluex$.liquidbounce$.event$.Render2DEvent;
import Render3DEvent = JVM.net$.ccbluex$.liquidbounce$.event$.Render3DEvent;
import SlowDownEvent = JVM.net$.ccbluex$.liquidbounce$.event$.SlowDownEvent;
import StepEvent = JVM.net$.ccbluex$.liquidbounce$.event$.StepEvent;
import StrafeEvent = JVM.net$.ccbluex$.liquidbounce$.event$.StrafeEvent;
import UpdateEvent = JVM.net$.ccbluex$.liquidbounce$.event$.UpdateEvent;
import WorldEvent = JVM.net$.ccbluex$.liquidbounce$.event$.WorldEvent;
import RenderUtils = JVM.net$.ccbluex$.liquidbounce$.utils$.render$.RenderUtils;
import BlockValue = JVM.net$.ccbluex$.liquidbounce$.value$.BlockValue;
import BoolValue = JVM.net$.ccbluex$.liquidbounce$.value$.BoolValue;
import FloatValue = JVM.net$.ccbluex$.liquidbounce$.value$.FloatValue;
import IntegerValue = JVM.net$.ccbluex$.liquidbounce$.value$.IntegerValue;
import ListValue = JVM.net$.ccbluex$.liquidbounce$.value$.ListValue;
import TextValue = JVM.net$.ccbluex$.liquidbounce$.value$.TextValue;
import BlockSnow = JVM.net$.minecraft$.block$.BlockSnow;
import BlockWeb = JVM.net$.minecraft$.block$.BlockWeb;
import GlStateManager = JVM.net$.minecraft$.client$.renderer$.GlStateManager;
import AxisAlignedBB = JVM.net$.minecraft$.util$.AxisAlignedBB;
import BlockPos = JVM.net$.minecraft$.util$.BlockPos;
import GL11 = JVM.org$.lwjgl$.opengl$.GL11;
const scriptName = "PathAlgorithm";
const scriptVersion = 0.3;
const scriptAuthor = "mumy++";
class PathAlgorithm {
private readonly setting = {
float: (name: string, def: number, min: number, max: number, object: object = {}) => {
return new _AdaptedValue<number, JavaFloat>(new (Java.extend(FloatValue, object))(name, def, min, max));
},
integer: (name: string, def: number, min: number, max: number, object: object = {}) => {
return new _AdaptedValue<number, JavaInteger>(new (Java.extend(IntegerValue, object))(name, def, min, max));
},
boolean: (name: string, def: boolean, object: object = {}) => {
return new _AdaptedValue<boolean, JavaBoolean>(new (Java.extend(BoolValue, object))(name, def));
},
list: (name: string, values: string[], def: string, object: object = {}) => {
return new _AdaptedValue<string, JavaString>(new (Java.extend(ListValue, object))(name, values, def));
},
text: (name: string, def: string, object: object = {}) => {
return new _AdaptedValue<string, JavaString>(new (Java.extend(TextValue, object))(name, def));
},
block: (name: string, def: number, object: object = {}) => {
return new _AdaptedValue<number, JavaInteger>(new (Java.extend(BlockValue, object))(name, def));
}
};
private readonly settings = {
setStart: this.setting.boolean("SetStart", false, {
onChanged: (oldValue: boolean, newValue: boolean) => {
if (!newValue) {
return;
}
const player = mc.thePlayer!;
this.start = new PathAlgorithm.Pos(Math.floor(player.posX), Math.floor(player.posY), Math.floor(player.posZ));
this.aStarAlgorithm = null;
this.settings.setStart.set(false);
}
}),
setEnd: this.setting.boolean("SetEnd", false, {
onChanged: (oldValue: boolean, newValue: boolean) => {
if (!newValue) {
return;
}
const player = mc.thePlayer!;
this.end = new PathAlgorithm.Pos(Math.floor(player.posX), Math.floor(player.posY), Math.floor(player.posZ));
this.aStarAlgorithm = null;
this.settings.setEnd.set(false);
}
}),
gWeight: this.setting.float("GWeight", 1.00005, 0, 2),
hWeight: this.setting.float("HWeight", 1, 0, 2),
speed: this.setting.integer("Speed", 1, 1, 50),
reset: this.setting.boolean("Reset", false, {
onChanged: (oldValue: boolean, newValue: boolean) => {
if (!newValue) {
return;
}
this.start = null;
this.end = null;
this.aStarAlgorithm = null;
this.settings.reset.set(false);
}
})
};
private start = <typeof PathAlgorithm.Pos.prototype | null>null;
private end = <typeof PathAlgorithm.Pos.prototype | null>null;
private aStarAlgorithm = <typeof PathAlgorithm.AStarAlgorithm.prototype | null>null;
private readonly openColor = new Color(200, 200, 200);
private readonly closeColor = new Color(127, 127, 127);
private readonly pathColor = new Color(0, 220, 220);
public getName() {
return "PathAlgorithm";
}
public getDescription() {
return "PathAlgorithm-Module, By-mumy";
}
public getCategory() {
return "Misc";
}
public onEnable() {
this.start = null;
this.end = null;
this.aStarAlgorithm = null;
}
public onDisable() {
this.onEnable();
}
public onUpdate() {
if (this.start != null && this.end != null) {
if (this.aStarAlgorithm == null) {
this.aStarAlgorithm = new PathAlgorithm.AStarAlgorithm(this.start, this.end, this.settings.hWeight.get(), this.settings.gWeight.get());
}
this.aStarAlgorithm.update(this.settings.speed.get());
}
}
public onRender3D(event: Render3DEvent) {
if (this.start != null && this.aStarAlgorithm == null) {
const { x, y, z } = this.start;
this.drawEntityBox(new AxisAlignedBB(x, y, z, x + 1, y + 1, z + 1), this.openColor);
} if (this.end != null && this.aStarAlgorithm?.path == null) {
const { x, y, z } = this.end;
this.drawEntityBox(new AxisAlignedBB(x, y, z, x + 1, y + 1, z + 1), this.openColor);
} if (this.aStarAlgorithm != null) {
if (this.aStarAlgorithm.path != null) {
for (let pos of this.aStarAlgorithm.path) {
const { x, y, z } = pos;
this.drawEntityBox(new AxisAlignedBB(x, y, z, x + 1, y + 1, z + 1), this.pathColor);
}
} else {
for (let pos of this.aStarAlgorithm.openList) {
const { x, y, z } = pos;
this.drawEntityBox(new AxisAlignedBB(x, y, z, x + 1, y + 1, z + 1), this.openColor);
} for (let pos of this.aStarAlgorithm.closeList) {
const { x, y, z } = pos;
this.drawEntityBox(new AxisAlignedBB(x, y, z, x + 1, y + 1, z + 1), this.closeColor);
}
}
}
}
public onWorld(event: WorldEvent) {
moduleManager.getModule(this.getName())!.setState(false);
}
public addValues(values: _ValueAdapter) {
const settings = <{ [ket: string]: _AdaptedValue<unknown, unknown> }>this.settings;
for (let key in settings) {
values.add(settings[key]);
}
}
private drawEntityBox(entityBox: AxisAlignedBB, color: Color) {
const renderManager = mc.getRenderManager()!;
GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
RenderUtils.enableGlCap(GL11.GL_BLEND);
RenderUtils.disableGlCap(GL11.GL_TEXTURE_2D, GL11.GL_DEPTH_TEST);
GL11.glDepthMask(false);
RenderUtils.glColor(color.getRed(), color.getGreen(), color.getBlue(), 26);
const axisAlignedBB = new AxisAlignedBB(entityBox.minX - renderManager.renderPosX,
entityBox.minY - renderManager.renderPosY,
entityBox.minZ - renderManager.renderPosZ,
entityBox.maxX - renderManager.renderPosX,
entityBox.maxY - renderManager.renderPosY,
entityBox.maxZ - renderManager.renderPosZ);
RenderUtils.drawFilledBox(axisAlignedBB);
GL11.glLineWidth(1);
RenderUtils.enableGlCap(GL11.GL_LINE_SMOOTH);
RenderUtils.glColor(color.getRed(), color.getGreen(), color.getBlue(), 95);
RenderUtils.drawSelectionBoundingBox(axisAlignedBB);
GlStateManager.resetColor();
GL11.glDepthMask(true);
RenderUtils.resetCaps();
}
public static Pos = class Pos {
public constructor(public x: number, public y: number, public z: number) {}
public equals(pos: Pos) {
return pos.x === this.x && pos.y === this.y && pos.z === this.z;
}
}
public static Node = class Node extends PathAlgorithm.Pos {
public cost = 0;
public hCost = 0;
public constructor(public x: number, public y: number, public z: number, public parent: Node | null) {
super(x, y, z);
this.hCost = (parent?.hCost ?? -1) + 1;
}
}
private static AStarAlgorithm = class AStarAlgorithm {
private readonly start: typeof PathAlgorithm.Pos.prototype;
private readonly end: typeof PathAlgorithm.Pos.prototype;
public readonly openList = <typeof PathAlgorithm.Node.prototype[]>[];
public readonly closeList = <typeof PathAlgorithm.Pos.prototype[]>[];
private readonly hWeight: number;
private readonly gWeight: number;
public path = <typeof PathAlgorithm.Pos.prototype[] | null>null;
public constructor(start: typeof PathAlgorithm.Pos.prototype, end: typeof PathAlgorithm.Pos.prototype, hWeight: number, gWeight: number) {
this.start = start;
this.end = end;
this.hWeight = hWeight;
this.gWeight = gWeight;
this.openList.push(new PathAlgorithm.Node(start.x, start.y, start.z, null));
}
public update(loops: number) {
if (this.path != null) {
return false;
}
top: while (loops-- > 0) {
if (this.openList.length === 0) {
return false;
}
const node = this.getNode()!;
for (let pos of this.closeList) {
if (node.equals(pos)) {
continue top;
}
} if (!this.canPassable(new BlockPos(node.x, node.y, node.z))) {
continue;
} if (node.equals(this.end)) {
this.path = [];
let temp = <typeof PathAlgorithm.Node.prototype | null>node;
do {
this.path.push(temp!);
temp = temp!.parent;
} while (temp != null);
this.path.reverse();
return false;
}
this.closeList.push(node);
const { x, y, z } = node;
this.createNodeToOpenList(x + 1, y, z, node);
this.createNodeToOpenList(x, y + 1, z, node);
this.createNodeToOpenList(x, y, z + 1, node);
this.createNodeToOpenList(x - 1, y, z, node);
this.createNodeToOpenList(x, y - 1, z, node);
this.createNodeToOpenList(x, y, z - 1, node);
}
return true;
}
private createNodeToOpenList(x: number, y: number, z: number, parent: typeof PathAlgorithm.Node.prototype | null) {
const node = new PathAlgorithm.Node(x, y, z, parent);
const [ xDist, yDist, zDist ] = [Math.abs(node.x - this.end.x), Math.abs(node.y - this.end.y), Math.abs(node.z - this.end.z)];
node.cost = node.hCost * this.gWeight + (xDist + yDist + zDist) * this.hWeight;
this.openList.push(node);
}
private getNode() {
let finalIndex = -1;
let finalCost = -1;
let finalNode = <typeof PathAlgorithm.Node.prototype | null>null;
for (let i = this.openList.length - 1; !(i < 0); --i) {
const node = this.openList[i];
const cost = node.cost;
if (finalNode == null || cost < finalCost) {
finalNode = node;
finalCost = cost;
finalIndex = i;
} if (i === 0) {
this.openList.splice(finalIndex, 1);
}
}
return finalNode;
}
private canPassable(blockPos: BlockPos) {
const world = mc.theWorld!;
const iBlockState = world.getBlockState(blockPos)!;
const block = iBlockState.getBlock()!;
return block.getCollisionBoundingBox(world, blockPos, iBlockState) == null ? !(block instanceof BlockWeb) : block instanceof BlockSnow && block.isReplaceable(world, blockPos);
}
}
}
let scriptModule: any;
function onLoad() {}
function onEnable() {
scriptModule = moduleManager.registerModule(new PathAlgorithm());
}
function onDisable() {
moduleManager.unregisterModule(scriptModule);
}