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_LOAD_DBCS=1
ACORE_ARMORY_HIDE_GAME_MASTERS=1
ACORE_ARMORY_TRANSMOG_MODULE=0
ACORE_ARMORY_REALMS__0__NAME="AzerothCore"
ACORE_ARMORY_REALMS__0__REALM_ID=1
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` |
| `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 |
| `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[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 |
@ -182,8 +183,8 @@ Open a web browser and navigate to http://localhost:48733
Other useful npm scripts:
* `npm run clean`: cleans the build directory
* `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 cleardata`: clears the data downloaded for 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
### With Docker
@ -259,7 +260,7 @@ This repository is used in production over at [ChromieCraft](https://www.chromie
- [X] Character page
- [X] Online/offline status
- [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] Achievements
- [X] PvP statistics, including arena teams

View file

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

View file

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

View file

@ -32,6 +32,7 @@ interface IEquipmentData {
classId: number;
subclassId: number;
quality: number;
transmog?: number;
}
interface ICustomizationOption {
@ -120,7 +121,7 @@ export class CharacterController {
}
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",
timeout: this.armory.config.dbQueryTimeout,
});
@ -180,6 +181,8 @@ export class CharacterController {
return row;
});
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", {
title: `Armory - ${charData.name}`,
@ -189,7 +192,8 @@ export class CharacterController {
gender: charData.gender,
class: charData.class,
flags: charData.playerFlags,
characterModelItems: await this.getModelViewerItems(equipmentData, charData.class),
characterModelItems,
characterModelTransmogs: transmogs,
customizationOptions: customization,
equipment,
mounts,
@ -332,11 +336,16 @@ export class CharacterController {
}
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({
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
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)
`,
values: [charGuid],
@ -378,7 +387,7 @@ export class CharacterController {
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) {
// Keep ranged weapon only if the character is a hunter
equipmentData = equipmentData.filter((row) => row.slot !== 17);
@ -395,12 +404,27 @@ export class CharacterController {
if (modifiedAppearance === undefined) {
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) {
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;

View file

@ -43,6 +43,12 @@
<input id="cb-hide-tabard" type="checkbox">
Hide tabard
</label>
<br>
<label class="checkbox">
<input id="cb-hide-transmogs" type="checkbox">
Hide transmogrifications
</label>
</div>
<div id="mounts-container" class="column">
@ -125,6 +131,20 @@
const hide = $("#cb-hide-tabard").prop("checked");
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;
function createViewer() {
@ -138,7 +158,9 @@
function setSlotVisible(slot, 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) {
setItems([item]);
}
@ -343,7 +365,7 @@
sheathOff: -1,
},
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: {
type: ZamModelViewer.Wow.Types.CHARACTER,
id: `${races[charData.race]}${genders[charData.gender]}`,