Created
April 2, 2024 04:48
-
-
Save ultraviolet-jordan/b0a0b0ff5be8a0cc80cd12b266a43372 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import fs from 'fs'; | |
import Packet from '#jagex2/io/Packet.js'; | |
import ZoneManager from '#lostcity/engine/zone/ZoneManager.js'; | |
import FloorCollider from '#lostcity/engine/collision/FloorCollider.js'; | |
import WallCollider from '#lostcity/engine/collision/WallCollider.js'; | |
import LocCollider from '#lostcity/engine/collision/LocCollider.js'; | |
import LocAngle from '#lostcity/engine/collision/LocAngle.js'; | |
import LocLayer from '#lostcity/engine/collision/LocLayer.js'; | |
import NpcCollider from '#lostcity/engine/collision/NpcCollider.js'; | |
import { LocShapes } from '#lostcity/engine/collision/LocShape.js'; | |
import RoofCollider from '#lostcity/engine/collision/RoofCollider.js'; | |
import PlayerCollider from '#lostcity/engine/collision/PlayerCollider.js'; | |
// all of this above needs to be refactored into an export ^ for one line imports. | |
import LocType from '#lostcity/cache/LocType.js'; | |
import Loc from '#lostcity/entity/Loc.js'; | |
import { CollisionFlagMap, LineValidator, NaivePathFinder, PathFinder, StepValidator } from '@2004scape/rsmod-pathfinder'; | |
export default class CollisionManager { | |
private readonly floorCollider: FloorCollider; | |
private readonly wallCollider: WallCollider; | |
private readonly locCollider: LocCollider; | |
private readonly npcCollider: NpcCollider; | |
private readonly roofCollider: RoofCollider; | |
private readonly playerCollider: PlayerCollider; | |
readonly flags: CollisionFlagMap; | |
readonly stepValidator: StepValidator; | |
readonly pathFinder: PathFinder; | |
readonly naivePathFinder: NaivePathFinder; | |
readonly lineValidator: LineValidator; | |
constructor() { | |
this.flags = new CollisionFlagMap(); | |
this.stepValidator = new StepValidator(this.flags); | |
this.floorCollider = new FloorCollider(this.flags); | |
this.wallCollider = new WallCollider(this.flags); | |
this.locCollider = new LocCollider(this.flags); | |
this.npcCollider = new NpcCollider(this.flags); | |
this.roofCollider = new RoofCollider(this.flags); | |
this.playerCollider = new PlayerCollider(this.flags); | |
this.pathFinder = new PathFinder(this.flags); | |
this.naivePathFinder = new NaivePathFinder(this.stepValidator); | |
this.lineValidator = new LineValidator(this.flags); | |
} | |
init(zoneManager: ZoneManager) { | |
console.time('Loading collision'); | |
const maps: string[] = fs.readdirSync('data/pack/server/maps').filter((x: string): boolean => x[0] === 'm'); | |
for (let index: number = 0; index < maps.length; index++) { | |
const [mx, mz] = maps[index].substring(1).split('_').map((x: string) => parseInt(x)); | |
const mapsquareX: number = mx << 6; | |
const mapsquareZ: number = mz << 6; | |
const lands: Int8Array = new Int8Array(4 * 64 * 64); // 4 * 64 * 64 size is guaranteed for lands | |
this.decodeLands(lands, Packet.load(`data/pack/server/maps/m${mx}_${mz}`), mapsquareX, mapsquareZ); | |
this.decodeLocs(zoneManager, lands, Packet.load(`data/pack/server/maps/l${mx}_${mz}`), mapsquareX, mapsquareZ); | |
} | |
console.timeEnd('Loading collision'); | |
} | |
/** | |
* Change collision at a specified Position for lands/floors. | |
* @param x The x pos. | |
* @param z The z pos. | |
* @param level The level pos. | |
* @param add True if adding this collision. False if removing. | |
*/ | |
changeLandCollision(x: number, z: number, level: number, add: boolean): void { | |
this.floorCollider.change(x, z, level, add); | |
} | |
/** | |
* Change collision at a specified Position for locs. | |
* @param shape The shape of the loc to change. | |
* @param angle The angle of the loc to change. | |
* @param blockrange If this loc blocks range. | |
* @param length The length of this loc. | |
* @param width The width of this loc. | |
* @param active If this loc is active. | |
* @param x The x pos. | |
* @param z The z pos. | |
* @param level The level pos. | |
* @param add True if adding this collision. False if removing. | |
*/ | |
changeLocCollision(shape: number, angle: number, blockrange: boolean, length: number, width: number, active: number, x: number, z: number, level: number, add: boolean): void { | |
const locLayer: LocLayer = LocShapes.layer(shape); | |
if (locLayer === LocLayer.WALL) { | |
this.wallCollider.change(x, z, level, angle, shape, blockrange, add); | |
} else if (locLayer === LocLayer.GROUND) { | |
if (angle === LocAngle.NORTH || angle === LocAngle.SOUTH) { | |
this.locCollider.change(x, z, level, length, width, blockrange, add); | |
} else { | |
this.locCollider.change(x, z, level, width, length, blockrange, add); | |
} | |
} else if (locLayer === LocLayer.GROUND_DECOR) { | |
if (active === 1) { | |
this.floorCollider.change(x, z, level, add); | |
} | |
} | |
} | |
/** | |
* Change collision at a specified Position for npcs. | |
* @param size The size square of this npc. (1x1, 2x2, etc). | |
* @param x The x pos. | |
* @param z The z pos. | |
* @param level The level pos. | |
* @param add True if adding this collision. False if removing. | |
*/ | |
changeNpcCollision(size: number, x: number, z: number, level: number, add: boolean): void { | |
this.npcCollider.change(x, z, level, size, add); | |
} | |
/** | |
* Change collision at a specified Position for players. | |
* @param size The size square of this npc. (1x1, 2x2, etc). | |
* @param x The x pos. | |
* @param z The z pos. | |
* @param level The level pos. | |
* @param add True if adding this collision. False if removing. | |
*/ | |
changePlayerCollision(size: number, x: number, z: number, level: number, add: boolean): void { | |
this.playerCollider.change(x, z, level, size, add); | |
} | |
/** | |
* Change collision at a specified Position for roofs. | |
* @param x The x pos. | |
* @param z The z pos. | |
* @param level The level pos. | |
* @param add True if adding this collision. False if removing. | |
*/ | |
changeRoofCollision(x: number, z: number, level: number, add: boolean): void { | |
this.roofCollider.change(x, z, level, add); | |
} | |
private decodeLands(lands: Int8Array, packet: Packet, mapsquareX: number, mapsquareZ: number): void { | |
for (let level: number = 0; level < 4; level++) { | |
for (let x: number = 0; x < 64; x++) { | |
const absoluteX: number = x + mapsquareX; | |
for (let z: number = 0; z < 64; z++) { | |
const absoluteZ: number = z + mapsquareZ; | |
if (x % 7 === 0 && z % 7 === 0) { // allocate per zone | |
this.flags.allocateIfAbsent(absoluteX, absoluteZ, level); | |
} | |
const coord: number = this.packCoord(x, z, level); | |
const land: number = lands[coord] = this.decodeLand(packet); | |
if ((land & 0x4) !== 0) { | |
this.changeRoofCollision(absoluteX, absoluteZ, level, true); | |
} | |
const bridged: boolean = (level === 1 ? land & 0x2 : lands[this.packCoord(x, z, 1)] & 0x2) === 2; | |
if (level === 1 && bridged) { | |
this.changeLandCollision(absoluteX, absoluteZ, 0, false); | |
} | |
if ((land & 0x1) !== 1) { | |
continue; | |
} | |
this.changeLandCollision(absoluteX, absoluteZ, bridged ? level - 1 : level, true); | |
} | |
} | |
} | |
} | |
private decodeLand(packet: Packet, collision: number = 0): number { | |
const opcode: number = packet.g1(); | |
if (opcode === 0 || opcode === 1) { | |
if (opcode === 1) { | |
packet.g1(); | |
} | |
return collision; | |
} | |
if (opcode >= 2 && opcode <= 49) { | |
packet.g1s(); | |
} | |
return this.decodeLand(packet, opcode >= 50 && opcode <= 81 ? opcode - 49 : collision); | |
} | |
private decodeLocs(zoneManager: ZoneManager, lands: Int8Array, packet: Packet, mapsquareX: number, mapsquareZ: number): void { | |
let locId: number = -1; | |
let locIdOffset: number = packet.gsmart(); | |
while (locIdOffset !== 0) { | |
locId += locIdOffset; | |
let coord: number = 0; | |
let coordOffset: number = packet.gsmart(); | |
while (coordOffset !== 0) { | |
const {x, z, level} = this.unpackCoord(coord += coordOffset - 1); | |
const bridged: boolean = (level === 1 ? lands[coord] & 0x2 : lands[this.packCoord(x, z, 1)] & 0x2) === 2; | |
const blevel: number = bridged ? level - 1 : level; | |
if (blevel < 0) { | |
continue; | |
} | |
const type: LocType = LocType.get(locId); | |
const width: number = type.width; | |
const length: number = type.length; | |
const info: number = packet.g1(); | |
const shape: number = info >> 2; | |
const angle: number = info & 0x3; | |
const absoluteX: number = x + mapsquareX; | |
const absoluteZ: number = z + mapsquareZ; | |
zoneManager.getZone(absoluteX, absoluteZ, blevel).addStaticLoc(new Loc(blevel, absoluteX, absoluteZ, width, length, locId, shape, angle)); | |
if (type.blockwalk) { | |
this.changeLocCollision(shape, angle, type.blockrange, length, width, type.active, absoluteX, absoluteZ, blevel, true); | |
} | |
coordOffset = packet.gsmart(); | |
} | |
locIdOffset = packet.gsmart(); | |
} | |
} | |
private packCoord(x: number, z: number, level: number): number { | |
return (z & 0x3f) | ((x & 0x3f) << 6) | ((level & 0x3) << 12); | |
} | |
private unpackCoord(packed: number): { level: number; x: number; z: number } { | |
return { | |
level: packed >> 12 & 0x3, | |
x: packed >> 6 & 0x3f, | |
z: packed & 0x3f, | |
}; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment