feat(character/talents): add talents page

This commit is contained in:
Axel Cocat 2022-01-11 23:58:21 +01:00
parent f32b70fb0b
commit f979241e37
12 changed files with 5165 additions and 79 deletions

View file

@ -2,6 +2,7 @@ import * as express from "express";
import { RowDataPacket } from "mysql2/promise";
import { Armory } from "../Armory";
import { IRealmConfig } from "../Config";
interface ICharacterData {
guid: number;
@ -32,6 +33,30 @@ interface ICustomizationOption {
}
const ITEM_CLASS_GEM = 3;
const RaceDisplayName = {
1: "Human",
2: "Orc",
3: "Dwarf",
4: "Night Elf",
5: "Undead",
6: "Tauren",
7: "Gnome",
8: "Troll",
10: "Blood Elf",
11: "Draenei",
};
const ClassDisplayName = {
1: "Warrior",
2: "Paladin",
3: "Hunter",
4: "Rogue",
5: "Priest",
6: "Death Knight",
7: "Shaman",
8: "Mage",
9: "Warlock",
11: "Druid",
};
export class CharacterController {
private armory: Armory;
@ -85,22 +110,24 @@ export class CharacterController {
}
public async character(req: express.Request, res: express.Response): Promise<void> {
const realm = req.params.realm;
const realmName = req.params.realm;
const charName = req.params.name;
if (this.armory.config.realms.find(r => r.name.toLowerCase() === realm.toLowerCase()) === undefined) {
const realm = this.getRealm(realmName);
if (realm === undefined) {
// Could not find realm
res.sendStatus(404);
return;
}
const charData = await this.getCharacterData(realm, charName);
const charData = await this.getCharacterData(realmName, charName);
if (charData === null) {
// Could not find character
res.sendStatus(404);
return;
}
const equipmentData = await this.getEquipmentData(realm, charData.guid);
const equipmentData = await this.getEquipmentData(realmName, charData.guid);
const customization = this.getCustomizationOptions(charData);
const equipment = equipmentData.map(row => {
(row as any).icon = this.itemIcons[row.itemEntry];
@ -111,13 +138,11 @@ export class CharacterController {
res.render("character.html", {
title: `Armory - ${charName}`,
...this.makeSharedDataObject(realm, charData),
data: JSON.stringify({
name: charData.name,
race: charData.race,
class: charData.class,
gender: charData.gender,
level: charData.level,
online: charData.online === 1,
class: charData.class,
characterModelItems: await this.getModelViewerItems(equipmentData, charData.class),
customizationOptions: customization,
equipment,
@ -127,6 +152,50 @@ export class CharacterController {
this.armory.gc();
}
public async talents(req: express.Request, res: express.Response): Promise<void> {
const realmName = req.params.realm;
const charName = req.params.name;
const realm = this.getRealm(realmName);
if (realm === undefined) {
// Could not find realm
res.sendStatus(404);
return;
}
const charData = await this.getCharacterData(realm.name, charName);
if (charData === null) {
// Could not find character
res.sendStatus(404);
return;
}
res.render("character-talents.html", {
title: `Armory - ${charName} - Talents`,
...this.makeSharedDataObject(realm, charData),
data: JSON.stringify({
talents: await this.getTalents(realm.name, charData.guid),
trees: await this.getTalentTrees(charData.class),
glyphs: await this.getGlyphs(realm.name, charData.guid),
}),
});
}
private makeSharedDataObject(realm: IRealmConfig, charData: ICharacterData) {
return {
realm: realm.name,
character: charData.name,
race: RaceDisplayName[charData.race],
class: ClassDisplayName[charData.class],
level: charData.level,
online: charData.online === 1,
};
}
private getRealm(realm: string): IRealmConfig {
return this.armory.config.realms.find(r => r.name.toLowerCase() === realm.toLowerCase());
}
private async getCharacterData(realm: string, charName: string): Promise<ICharacterData> {
const [rows, fields] = await this.armory.getCharactersDb(realm).query(`
SELECT guid, name, race, class, gender, level, skin, face, hairStyle, hairColor, facialStyle, online
@ -468,4 +537,77 @@ export class CharacterController {
return options;
}
private async getTalents(realm: string, character: number): Promise<number[][]> {
const [rows, fields] = await this.armory.getCharactersDb(realm).query(`
SELECT spell, specMask
FROM character_talent
WHERE guid = ?
`, [character]);
const talents: number[][] = [[], []];
for (const row of rows as RowDataPacket[]) {
if (row.specMask === 1 || row.specMask === 3) {
talents[0].push(row.spell);
}
if (row.specMask === 2 || row.specMask === 3) {
talents[1].push(row.spell);
}
}
return talents;
}
private async getTalentTrees(classId: number) {
const items = await this.armory.dbc.talentTab()
.filter(tab => tab.classMask === Math.pow(2, classId - 1))
.map(async tab => {
const icon = await this.armory.dbc.spellIcon().find(icon => icon.id === tab.spellIconId);
const spells = await this.armory.dbc.talent()
.filter(row => row.tabId === tab.id)
.map(async row => {
const spell = await this.armory.dbc.spell().find(spell => spell.id === row.spellRank0);
const icon = await this.armory.dbc.spellIcon().find(icon => icon.id === spell?.spellIconId);
return { ...row, icon: this.processSpellIconTexture(icon?.textureFilename ?? ""), };
})
.toArray();
return {
name: tab.nameLang0,
icon: this.processSpellIconTexture(icon.textureFilename),
spells: await Promise.all(spells),
};
})
.toArray();
return await Promise.all(items);
}
private processSpellIconTexture(texturePath: string): string {
return texturePath
.toLowerCase()
.replace("interface\\icons\\", "")
.replace("interface\\spellbook\\", "")
.replace(/\.$/, "");
}
private async getGlyphs(realm: string, character: number): Promise<any[][]> {
const [rows, fields] = await this.armory.getCharactersDb(realm).query(`
SELECT guid, talentGroup, glyph1, glyph2, glyph3, glyph4, glyph5, glyph6
FROM character_glyphs
WHERE guid = ?
`, [character]);
const glyphs = [[], []];
for (const row of rows as RowDataPacket[]) {
const glyphIds = [row.glyph1, row.glyph2, row.glyph3, row.glyph4, row.glyph5, row.glyph6].filter(id => id !== 0);
for (const glyphId of glyphIds) {
const glyph = await this.armory.dbc.glyphProperties().find(g => g.id === glyphId);
if (glyph === undefined) {
continue;
}
glyphs[row.talentGroup].push(glyph.spellId);
}
}
return glyphs;
}
}