Ran into a wee issue when I tried to incorporate this into my rooms' general toString() which is then used in both logs & notifications - the game's notifier ended up malforming my links. Ended up just doing the same URL escape logic for HTML entities which prevents the game's notifier from automatically turning room names into links:
// to-string.ts
function roomToString(this: Room): string {
return `<a href="${this.replayLink()}" style="color:#9c05a6ff">${this.htmlName}</a>`;
}
// replay-link.ts
interface Ext {
replayLink: typeof replayLink;
/** Room name with HTML entities replacements to avoid it getting broken in notifications */
get htmlName(): string;
}
declare global {
interface Room extends Ext {
}
}
Object.defineProperties(Room.prototype, {
htmlName: lazyGetter('htmlName' satisfies keyof Room, function htmlName(this: Pick<Room, 'name' | 'htmlName'>): string {
return escapeName(this.name, HTML_MAPPINGS);
}),
replayLink: confValue(replayLink),
} satisfies TypeDescriptorMap<Ext>);
function replayLink(this: Pick<Room, 'name'>, tick = Game.time): string {
return `https://screeps.com/a/#!/history/${Game.shard.name}/${escapeName(this.name, URL_MAPPINGS)}?t=${tick}`;
}
const URL_MAPPINGS: Record<string, string> = {N: '%4E', S: '%53', E: '%45', W: '%57'};
const HTML_MAPPINGS: Record<string, string> = {N: 'N', S: 'S', E: 'E', W: 'W'};
function escapeName(roomName: string, mappings: Record<string, string>): string {
let out = '';
for (let i = 0, c: string; i < roomName.length; i++) {
c = roomName[i]!;
out += mappings[c] || c;
}
return out;
}