earthmc
Version:
An unofficial EarthMC library providing handy methods and extensive info.
170 lines (135 loc) • 5.89 kB
text/typescript
import striptags from 'striptags'
import type Dynmap from "./Dynmap.js"
import {
formatString, asBool,
calcArea, midrange,
getExisting,
isInvitable
} from '../../utils/functions.js'
import {
FetchError,
InvalidError,
NotFoundError
} from "../../utils/errors.js"
import type { EntityApi } from '../../helpers/EntityApi.js'
import type { Nation, StrictPoint2D, Town } from '../../types/index.js'
import { getNearest } from '../common.js'
class Towns implements EntityApi<Town | NotFoundError> {
#map: Dynmap
get map() { return this.#map }
constructor(map: Dynmap) {
this.#map = map
}
readonly fromNation = async(nationName: string) => {
if (!nationName) throw new InvalidError(`Parameter 'nation' is ${nationName}`)
const nation = await this.map.Nations.get(nationName) as Nation
if (nation instanceof Error) throw nation
return await this.get(...nation.towns)
}
readonly get = async(...townList: string[]) => {
const towns = await this.all()
if (!towns) throw new FetchError('Error fetching towns! Please try again.')
const existing = getExisting(towns, townList, 'name')
return existing.length > 1 ? Promise.all(existing) : Promise.resolve(existing[0])
}
readonly all = async(removeAccents = false) => {
let cachedTowns: Town[] = this.map.getFromCache('towns')
if (cachedTowns) return cachedTowns
const markerset = await this.map.markerset()
if (!markerset?.areas) {
throw new ReferenceError('No areas found on markerset!')
}
const townsArray: Town[] = []
const areas = Object.values(markerset.areas)
const len = areas.length
for (let i = 0; i < len; i++) {
const town = areas[i]
const rawinfo = town.desc.split("<br />")
const info = rawinfo.map(i => striptags(i, ['a']))
const firstEl = info[0]
if (firstEl.includes("(Shop)")) continue
const mayor = info[1].slice(7)
if (mayor == "") continue
let split: string | string[] = firstEl.split(" (")
split = (split[2] ?? split[1]).slice(0, -1)
const residents = info[2].slice(9).split(", ")
const capital = asBool(info[9]?.slice(9))
let nationName = split
let wikiPage = null
// Check if we have a wiki
if (split.includes('href')) {
nationName = split.slice(split.indexOf('>') + 1).replace('</a>', '')
split = split.replace('<a href="', '')
if (capital) wikiPage = split.substring(0, split.indexOf('"'))
}
const home = nationName != "" ? markerset.markers[`${town.label}__home`] : null
const [townX, townZ] = [town.x, town.z]
const area = calcArea(townX, townZ, townX.length)
const currentTown: Town = {
name: formatString(town.label, removeAccents),
nation: nationName == "" ? "No Nation" : formatString(nationName.trim(), removeAccents),
mayor, area,
x: home?.x ?? midrange(townX),
z: home?.z ?? midrange(townZ),
bounds: {
x: townX.map(num => Math.round(num)),
z: townZ.map(num => Math.round(num))
},
residents: residents,
flags: {
pvp: asBool(info[4]?.slice(5)),
mobs: asBool(info[5]?.slice(6)),
public: asBool(info[6]?.slice(8)),
explosion: asBool(info[7]?.slice(11)),
fire: asBool(info[8]?.slice(6)),
capital: capital
},
colours: {
fill: town.fillcolor,
outline: town.color
},
opacities: {
fill: town.fillopacity,
outline: town.opacity
}
}
if (wikiPage) {
currentTown['wiki'] = wikiPage
}
townsArray.push(currentTown)
}
//#region Remove duplicates & add to area
const temp: Record<string, Town> = {}
cachedTowns = []
const townsArrLen = townsArray.length
for (let i = 0; i < townsArrLen; i++) {
const town = townsArray[i]
const name = town.name
if (temp[name]) temp[name].area += town.area
else {
temp[name] = town
cachedTowns.push(town)
}
}
//#endregion
if (cachedTowns.length > 0) {
this.map.putInCache('towns', cachedTowns)
//this.map.unrefIfNode()
}
return cachedTowns
}
readonly nearby = async (location: StrictPoint2D, radius: StrictPoint2D, towns?: Town[]) =>
getNearest<Town>(location, radius, towns, this.all)
readonly invitable = async(nationName: string, includeBelonging = false) => {
const nation = await this.map.Nations.get(nationName) as Nation
if (nation instanceof NotFoundError) throw new Error("Error checking invitable: Nation does not exist!")
if (!nation) throw new Error("Error checking invitable: Could not fetch the nation!")
const towns = await this.all()
if (!towns) throw new FetchError('An error occurred fetching towns!')
return towns.filter(t => isInvitable(t, nation, this.map.inviteRange, includeBelonging))
}
}
export {
Towns,
Towns as default
}