feat(character): add transmog module compatibility (#21)

This commit is contained in:
Axel Cocat 2022-05-14 16:39:31 +02:00 committed by GitHub
parent 8b1d172690
commit 7cd2748d33
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 61 additions and 11 deletions

View file

@ -6,6 +6,7 @@ ACORE_ARMORY_IFRAME_MODE__ENABLED=0
ACORE_ARMORY_IFRAME_MODE__URL="https://mywebsite.com/armory" ACORE_ARMORY_IFRAME_MODE__URL="https://mywebsite.com/armory"
ACORE_ARMORY_LOAD_DBCS=1 ACORE_ARMORY_LOAD_DBCS=1
ACORE_ARMORY_HIDE_GAME_MASTERS=1 ACORE_ARMORY_HIDE_GAME_MASTERS=1
ACORE_ARMORY_TRANSMOG_MODULE=0
ACORE_ARMORY_REALMS__0__NAME="AzerothCore" ACORE_ARMORY_REALMS__0__NAME="AzerothCore"
ACORE_ARMORY_REALMS__0__REALM_ID=1 ACORE_ARMORY_REALMS__0__REALM_ID=1
ACORE_ARMORY_REALMS__0__AUTH_DATABASE="acore_auth" ACORE_ARMORY_REALMS__0__AUTH_DATABASE="acore_auth"

View file

@ -142,6 +142,7 @@ I also noticed that such a tool was frequently requested in the AzerothCore Disc
| `iframeMode.url` | `ACORE_ARMORY_IFRAME_MODE__URL` | String | `"https://mywebsite.com/armory"` | Set this to the URL of the page that hosts the `iframe` | | `iframeMode.url` | `ACORE_ARMORY_IFRAME_MODE__URL` | String | `"https://mywebsite.com/armory"` | Set this to the URL of the page that hosts the `iframe` |
| `loadDbcs` | `ACORE_ARMORY_LOAD_DBCS` | Boolean | `true` | Loads the DBC data from the `data` directory into memory when starting up. It is highly recommended to set this to `true`. Only use `false` to keep memory usage low, on a test server for example | | `loadDbcs` | `ACORE_ARMORY_LOAD_DBCS` | Boolean | `true` | Loads the DBC data from the `data` directory into memory when starting up. It is highly recommended to set this to `true`. Only use `false` to keep memory usage low, on a test server for example |
| `hideGameMasters` | `ACORE_ARMORY_HIDE_GAME_MASTERS` | Boolean | `true` | Hides Game Master characters if set to `true`. They will not be found in the search page, and their character pages will show a 404 error | | `hideGameMasters` | `ACORE_ARMORY_HIDE_GAME_MASTERS` | Boolean | `true` | Hides Game Master characters if set to `true`. They will not be found in the search page, and their character pages will show a 404 error |
| `transmogModule` | `ACORE_ARMORY_TRANSMOG_MODULE` | Boolean | `false` | Set this to `true` if your server uses the [transmogrification module](https://github.com/azerothcore/mod-transmog) and you want to display the transmogrified items on the 3D model |
| `realms` | `ACORE_ARMORY_REALMS__`... | Array of objects | | An array of realm configurations | | `realms` | `ACORE_ARMORY_REALMS__`... | Array of objects | | An array of realm configurations |
| `realms[0].name` | `ACORE_ARMORY_REALMS__0__NAME` | String | `"AzerothCore"` | The name of the realm. Will be used in the URLs, shown on the character pages and in the search page if you have multiple realms | | `realms[0].name` | `ACORE_ARMORY_REALMS__0__NAME` | String | `"AzerothCore"` | The name of the realm. Will be used in the URLs, shown on the character pages and in the search page if you have multiple realms |
| `realms[0].realmId` | `ACORE_ARMORY_REALMS__0__REALM_ID` | Number | `1` | The realm's ID, this must match the `id` column of the `realmlist` table in the auth database | | `realms[0].realmId` | `ACORE_ARMORY_REALMS__0__REALM_ID` | Number | `1` | The realm's ID, this must match the `id` column of the `realmlist` table in the auth database |
@ -182,8 +183,8 @@ Open a web browser and navigate to http://localhost:48733
Other useful npm scripts: Other useful npm scripts:
* `npm run clean`: cleans the build directory * `npm run clean`: cleans the build directory
* `npm run watch`: watches for changes and rebuilds automatically, useful for development * `npm run watch`: watches for changes and rebuilds automatically, useful for development
* `npm run fetchdata`: downloads the data needed by the 3d model viewer * `npm run fetchdata`: downloads the data needed by the 3D model viewer
* `npm run cleardata`: clears the data downloaded for the 3d model viewer * `npm run cleardata`: clears the data downloaded for the 3D model viewer
### With Docker ### With Docker
@ -259,7 +260,7 @@ This repository is used in production over at [ChromieCraft](https://www.chromie
- [X] Character page - [X] Character page
- [X] Online/offline status - [X] Online/offline status
- [X] Equipment with tooltips - [X] Equipment with tooltips
- [X] 3d model, including mounts - [X] 3D model, including mounts and [transmogrifications](https://github.com/azerothcore/mod-transmog)
- [X] Talent trees, including glyphs and dual spec support - [X] Talent trees, including glyphs and dual spec support
- [X] Achievements - [X] Achievements
- [X] PvP statistics, including arena teams - [X] PvP statistics, including arena teams

View file

@ -9,6 +9,7 @@
}, },
"loadDbcs": true, "loadDbcs": true,
"hideGameMasters": true, "hideGameMasters": true,
"transmogModule": false,
"realms": [ "realms": [
{ {
"name": "AzerothCore", "name": "AzerothCore",

View file

@ -31,6 +31,7 @@ export class Config {
public iframeMode: IIframeModeConfig; public iframeMode: IIframeModeConfig;
public loadDbcs: boolean; public loadDbcs: boolean;
public hideGameMasters: boolean; public hideGameMasters: boolean;
public transmogModule: boolean;
public realms: IRealmConfig[]; public realms: IRealmConfig[];
public worldDatabase: IDatabaseConfig; public worldDatabase: IDatabaseConfig;
public dbQueryTimeout: number; public dbQueryTimeout: number;

View file

@ -32,6 +32,7 @@ interface IEquipmentData {
classId: number; classId: number;
subclassId: number; subclassId: number;
quality: number; quality: number;
transmog?: number;
} }
interface ICustomizationOption { interface ICustomizationOption {
@ -120,7 +121,7 @@ export class CharacterController {
} }
this.itemSocketBonuses = {}; this.itemSocketBonuses = {};
let [rows, fields] = await this.armory.worldDb.query({ const [rows, fields] = await this.armory.worldDb.query({
sql: "SELECT entry, socketBonus FROM item_template WHERE socketBonus <> 0", sql: "SELECT entry, socketBonus FROM item_template WHERE socketBonus <> 0",
timeout: this.armory.config.dbQueryTimeout, timeout: this.armory.config.dbQueryTimeout,
}); });
@ -180,6 +181,8 @@ export class CharacterController {
return row; return row;
}); });
const mounts = await this.getMounts(realmName, charData.guid); const mounts = await this.getMounts(realmName, charData.guid);
const transmogs: number[][] = this.armory.config.transmogModule ? [] : undefined;
const characterModelItems = await this.getModelViewerItems(equipmentData, charData.class, transmogs);
res.render("character.hbs", { res.render("character.hbs", {
title: `Armory - ${charData.name}`, title: `Armory - ${charData.name}`,
@ -189,7 +192,8 @@ export class CharacterController {
gender: charData.gender, gender: charData.gender,
class: charData.class, class: charData.class,
flags: charData.playerFlags, flags: charData.playerFlags,
characterModelItems: await this.getModelViewerItems(equipmentData, charData.class), characterModelItems,
characterModelTransmogs: transmogs,
customizationOptions: customization, customizationOptions: customization,
equipment, equipment,
mounts, mounts,
@ -332,11 +336,16 @@ export class CharacterController {
} }
private async getEquipmentData(realm: string, charGuid: number): Promise<IEquipmentData[]> { private async getEquipmentData(realm: string, charGuid: number): Promise<IEquipmentData[]> {
const transmogSelect = this.armory.config.transmogModule ? ", custom_transmogrification.FakeEntry AS transmog" : "";
const transmogJoin = this.armory.config.transmogModule ? "LEFT JOIN custom_transmogrification ON custom_transmogrification.GUID = item_instance.guid" : "";
let [rows, fields] = await this.armory.getCharactersDb(realm).query({ let [rows, fields] = await this.armory.getCharactersDb(realm).query({
sql: ` sql: `
SELECT character_inventory.slot, item_instance.itemEntry, item_instance.flags, item_instance.enchantments, item_instance.randomPropertyId SELECT
character_inventory.slot, item_instance.itemEntry, item_instance.flags, item_instance.enchantments, item_instance.randomPropertyId
${transmogSelect}
FROM character_inventory FROM character_inventory
JOIN item_instance ON item_instance.guid = character_inventory.item JOIN item_instance ON item_instance.guid = character_inventory.item
${transmogJoin}
WHERE character_inventory.guid = ? AND character_inventory.bag = 0 AND character_inventory.slot IN (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18) WHERE character_inventory.guid = ? AND character_inventory.bag = 0 AND character_inventory.slot IN (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18)
`, `,
values: [charGuid], values: [charGuid],
@ -378,7 +387,7 @@ export class CharacterController {
return (rows as RowDataPacket[]).map((row) => this.mountBySpellId[row.spell]).filter((m) => m !== undefined); return (rows as RowDataPacket[]).map((row) => this.mountBySpellId[row.spell]).filter((m) => m !== undefined);
} }
private async getModelViewerItems(equipmentData: IEquipmentData[], charClass: number): Promise<number[][]> { private async getModelViewerItems(equipmentData: IEquipmentData[], charClass: number, transmogOut?: number[][]): Promise<number[][]> {
if (charClass !== 3) { if (charClass !== 3) {
// Keep ranged weapon only if the character is a hunter // Keep ranged weapon only if the character is a hunter
equipmentData = equipmentData.filter((row) => row.slot !== 17); equipmentData = equipmentData.filter((row) => row.slot !== 17);
@ -395,12 +404,27 @@ export class CharacterController {
if (modifiedAppearance === undefined) { if (modifiedAppearance === undefined) {
continue; continue;
} }
const appearance = await this.armory.dbc.itemAppearance().find((row) => row.id === modifiedAppearance.itemAppearanceId); let appearance = await this.armory.dbc.itemAppearance().find((row) => row.id === modifiedAppearance.itemAppearanceId);
if (appearance === undefined) { if (appearance === undefined) {
continue; continue;
} }
items.push([this.itemInventoryTypes[equipment.itemEntry], appearance.itemDisplayInfoId]); let invType = this.itemInventoryTypes[equipment.itemEntry];
items.push([invType, appearance.itemDisplayInfoId]);
if (transmogOut !== undefined) {
if (equipment.transmog !== undefined) {
const modifiedAppearance = await this.armory.dbc.itemModifiedAppearance().find((row) => row.itemId === equipment.transmog);
if (modifiedAppearance !== undefined) {
const tmogAppearance = await this.armory.dbc.itemAppearance().find((row) => row.id === modifiedAppearance.itemAppearanceId);
if (tmogAppearance !== undefined) {
appearance = tmogAppearance;
invType = this.itemInventoryTypes[equipment.transmog];
}
}
}
transmogOut.push([invType, appearance.itemDisplayInfoId]);
}
} }
return items; return items;

View file

@ -43,6 +43,12 @@
<input id="cb-hide-tabard" type="checkbox"> <input id="cb-hide-tabard" type="checkbox">
Hide tabard Hide tabard
</label> </label>
<br>
<label class="checkbox">
<input id="cb-hide-transmogs" type="checkbox">
Hide transmogrifications
</label>
</div> </div>
<div id="mounts-container" class="column"> <div id="mounts-container" class="column">
@ -125,6 +131,20 @@
const hide = $("#cb-hide-tabard").prop("checked"); const hide = $("#cb-hide-tabard").prop("checked");
setSlotVisible(19, !hide); setSlotVisible(19, !hide);
}); });
if (charData.characterModelTransmogs === undefined) {
$("#cb-hide-transmogs").parent().hide();
}
$("#cb-hide-transmogs").change(() => {
const hide = $("#cb-hide-transmogs").prop("checked");
const items = hide ? charData.characterModelItems : charData.characterModelTransmogs;
for (const slot of (items.map((item) => item[0]))) {
clearSlots([slot]);
}
const hideHelm = $("#cb-hide-helmet").prop("checked");
const hideCloak = $("#cb-hide-cloak").prop("checked");
setItems(items.filter(item => !((item[0] === 1 && hideHelm) || (item[0] === 16 && hideCloak))));
});
let viewer; let viewer;
function createViewer() { function createViewer() {
@ -138,7 +158,9 @@
function setSlotVisible(slot, visible) { function setSlotVisible(slot, visible) {
if (visible) { if (visible) {
const item = charData.characterModelItems.find(([sl, appearance]) => sl === slot); const transmogged = !$("#cb-hide-transmogs").prop("checked");
const items = transmogged ? charData.characterModelTransmogs : charData.characterModelItems;
const item = items.find(([sl, appearance]) => sl === slot);
if (item !== undefined) { if (item !== undefined) {
setItems([item]); setItems([item]);
} }
@ -343,7 +365,7 @@
sheathOff: -1, sheathOff: -1,
}, },
cls: charData.class, cls: charData.class,
items: charData.characterModelItems.filter(item => !((item[0] === 1 && hideHelm) || (item[0] === 16 && hideCloak))), items: (charData.characterModelTransmogs ?? charData.characterModelItems).filter(item => !((item[0] === 1 && hideHelm) || (item[0] === 16 && hideCloak))),
models: { models: {
type: ZamModelViewer.Wow.Types.CHARACTER, type: ZamModelViewer.Wow.Types.CHARACTER,
id: `${races[charData.race]}${genders[charData.gender]}`, id: `${races[charData.race]}${genders[charData.gender]}`,