feat(guild): add guild page
15
package-lock.json
generated
|
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "azerothcore-armory",
|
"name": "azerothcore-armory",
|
||||||
"version": "0.8.0",
|
"version": "0.9.0",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "azerothcore-armory",
|
"name": "azerothcore-armory",
|
||||||
"version": "0.8.0",
|
"version": "0.9.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@supercharge/promise-pool": "^2.1.0",
|
"@supercharge/promise-pool": "^2.1.0",
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
"glob": "^7.2.0",
|
"glob": "^7.2.0",
|
||||||
"glob-promise": "^4.2.2",
|
"glob-promise": "^4.2.2",
|
||||||
"handlebars-helpers": "^0.9.8",
|
"handlebars-helpers": "^0.9.8",
|
||||||
|
"html-entities": "^2.3.2",
|
||||||
"mkdirp": "^1.0.4",
|
"mkdirp": "^1.0.4",
|
||||||
"morgan": "^1.10.0",
|
"morgan": "^1.10.0",
|
||||||
"mysql2": "^2.3.3",
|
"mysql2": "^2.3.3",
|
||||||
|
|
@ -2379,6 +2380,11 @@
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/html-entities": {
|
||||||
|
"version": "2.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.2.tgz",
|
||||||
|
"integrity": "sha512-c3Ab/url5ksaT0WyleslpBEthOzWhrjQbg75y7XUsfSzi3Dgzt0l8w5e7DylRn15MTlMMD58dTfzddNS2kcAjQ=="
|
||||||
|
},
|
||||||
"node_modules/html-tag": {
|
"node_modules/html-tag": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/html-tag/-/html-tag-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/html-tag/-/html-tag-1.0.0.tgz",
|
||||||
|
|
@ -7004,6 +7010,11 @@
|
||||||
"remarkable": "^1.6.2"
|
"remarkable": "^1.6.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"html-entities": {
|
||||||
|
"version": "2.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.2.tgz",
|
||||||
|
"integrity": "sha512-c3Ab/url5ksaT0WyleslpBEthOzWhrjQbg75y7XUsfSzi3Dgzt0l8w5e7DylRn15MTlMMD58dTfzddNS2kcAjQ=="
|
||||||
|
},
|
||||||
"html-tag": {
|
"html-tag": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/html-tag/-/html-tag-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/html-tag/-/html-tag-1.0.0.tgz",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "azerothcore-armory",
|
"name": "azerothcore-armory",
|
||||||
"version": "0.8.0",
|
"version": "0.9.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsc -p tsconfig.json",
|
"build": "tsc -p tsconfig.json",
|
||||||
|
|
@ -37,6 +37,7 @@
|
||||||
"glob": "^7.2.0",
|
"glob": "^7.2.0",
|
||||||
"glob-promise": "^4.2.2",
|
"glob-promise": "^4.2.2",
|
||||||
"handlebars-helpers": "^0.9.8",
|
"handlebars-helpers": "^0.9.8",
|
||||||
|
"html-entities": "^2.3.2",
|
||||||
"mkdirp": "^1.0.4",
|
"mkdirp": "^1.0.4",
|
||||||
"morgan": "^1.10.0",
|
"morgan": "^1.10.0",
|
||||||
"mysql2": "^2.3.3",
|
"mysql2": "^2.3.3",
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import { DbcManager } from "./data/DbcReader";
|
||||||
import { CharacterCustomization } from "./data/CharacterCustomization";
|
import { CharacterCustomization } from "./data/CharacterCustomization";
|
||||||
import { IndexController } from "./controllers/IndexController";
|
import { IndexController } from "./controllers/IndexController";
|
||||||
import { CharacterController } from "./controllers/CharacterController";
|
import { CharacterController } from "./controllers/CharacterController";
|
||||||
|
import { GuildController } from "./controllers/GuildController";
|
||||||
|
|
||||||
export class Armory {
|
export class Armory {
|
||||||
public characterCustomization: CharacterCustomization;
|
public characterCustomization: CharacterCustomization;
|
||||||
|
|
@ -19,6 +20,7 @@ export class Armory {
|
||||||
public config: Config;
|
public config: Config;
|
||||||
public worldDb: Pool;
|
public worldDb: Pool;
|
||||||
public logger: winston.Logger;
|
public logger: winston.Logger;
|
||||||
|
public charsetCache: { [key: string]: string };
|
||||||
|
|
||||||
private charsDbs: { [key: string]: Pool };
|
private charsDbs: { [key: string]: Pool };
|
||||||
private errorNames: { [key: number]: string };
|
private errorNames: { [key: number]: string };
|
||||||
|
|
@ -40,6 +42,7 @@ export class Armory {
|
||||||
new winston.transports.File({ filename: "armory.combined.log", level: "http" }),
|
new winston.transports.File({ filename: "armory.combined.log", level: "http" }),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
this.charsetCache = {};
|
||||||
|
|
||||||
this.errorNames = {
|
this.errorNames = {
|
||||||
400: "Bad Request",
|
400: "Bad Request",
|
||||||
|
|
@ -100,7 +103,17 @@ export class Armory {
|
||||||
morgan.token("id", (req: express.Request) => {
|
morgan.token("id", (req: express.Request) => {
|
||||||
return req.id;
|
return req.id;
|
||||||
});
|
});
|
||||||
app.use(morgan(":method :url :status - ID :id - :remote-addr - :response-time ms", {
|
morgan.token("ip", (req: express.Request) => {
|
||||||
|
const forwardedFor = req.headers["x-forwarded-for"];
|
||||||
|
if (forwardedFor) {
|
||||||
|
if (typeof forwardedFor === "string") {
|
||||||
|
return forwardedFor;
|
||||||
|
}
|
||||||
|
return forwardedFor.join(", ");
|
||||||
|
}
|
||||||
|
return req.socket.remoteAddress;
|
||||||
|
});
|
||||||
|
app.use(morgan(":method :url :status - ID :id - IP :ip - :response-time ms", {
|
||||||
stream: {
|
stream: {
|
||||||
write: (msg) => this.logger.http(msg.trim()),
|
write: (msg) => this.logger.http(msg.trim()),
|
||||||
},
|
},
|
||||||
|
|
@ -126,6 +139,10 @@ export class Armory {
|
||||||
app.get("/character/:realm/:name/achievements", this.wrapRoute(charsController.achievements.bind(charsController)));
|
app.get("/character/:realm/:name/achievements", this.wrapRoute(charsController.achievements.bind(charsController)));
|
||||||
app.get("/character/:realm/:character/achievements/data", this.wrapRoute(charsController.achievementsData.bind(charsController)));
|
app.get("/character/:realm/:character/achievements/data", this.wrapRoute(charsController.achievementsData.bind(charsController)));
|
||||||
|
|
||||||
|
const guildsController = new GuildController(this);
|
||||||
|
app.get("/guild/:realm/:name", this.wrapRoute(guildsController.guild.bind(guildsController)));
|
||||||
|
app.get("/guild/:realm/:name/members", this.wrapRoute(guildsController.members.bind(guildsController)));
|
||||||
|
|
||||||
app.use((err, req: express.Request, res: express.Response, next: express.NextFunction) => {
|
app.use((err, req: express.Request, res: express.Response, next: express.NextFunction) => {
|
||||||
// Error handler
|
// Error handler
|
||||||
|
|
||||||
|
|
@ -174,6 +191,25 @@ export class Armory {
|
||||||
return this.config.realms.find(r => r.name.toLowerCase() === realm.toLowerCase());
|
return this.config.realms.find(r => r.name.toLowerCase() === realm.toLowerCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async getDatabaseCharset(realm: string): Promise<string> {
|
||||||
|
const db = this.getCharactersDb(realm);
|
||||||
|
|
||||||
|
if (!(realm in this.charsetCache)) {
|
||||||
|
const [rows, fields] = await db.query({
|
||||||
|
sql: `
|
||||||
|
SELECT CCSA.character_set_name FROM information_schema.\`TABLES\` T,
|
||||||
|
information_schema.\`COLLATION_CHARACTER_SET_APPLICABILITY\` CCSA
|
||||||
|
WHERE CCSA.collation_name = T.table_collation
|
||||||
|
AND T.table_schema = "${(await db.getConnection()).config.database}"
|
||||||
|
AND T.table_name = "characters"
|
||||||
|
`,
|
||||||
|
timeout: this.config.dbQueryTimeout,
|
||||||
|
});
|
||||||
|
this.charsetCache[realm] = rows[0].character_set_name;
|
||||||
|
}
|
||||||
|
return this.charsetCache[realm];
|
||||||
|
}
|
||||||
|
|
||||||
public gc(): void {
|
public gc(): void {
|
||||||
if (this.config.loadDbcs) {
|
if (this.config.loadDbcs) {
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
35
src/armory/Utils.ts
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
export enum EFaction {
|
||||||
|
Horde = 0,
|
||||||
|
Alliance = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Utils {
|
||||||
|
public static raceNames = {
|
||||||
|
1: "human",
|
||||||
|
2: "orc",
|
||||||
|
3: "dwarf",
|
||||||
|
4: "nightelf",
|
||||||
|
5: "scourge",
|
||||||
|
6: "tauren",
|
||||||
|
7: "gnome",
|
||||||
|
8: "troll",
|
||||||
|
10: "bloodelf",
|
||||||
|
11: "draenei",
|
||||||
|
};
|
||||||
|
public static classNames = {
|
||||||
|
1: "warrior",
|
||||||
|
2: "paladin",
|
||||||
|
3: "hunter",
|
||||||
|
4: "rogue",
|
||||||
|
5: "priest",
|
||||||
|
6: "deathknight",
|
||||||
|
7: "shaman",
|
||||||
|
8: "mage",
|
||||||
|
9: "warlock",
|
||||||
|
11: "druid",
|
||||||
|
};
|
||||||
|
|
||||||
|
public static getFactionFromRaceId(race: number): EFaction {
|
||||||
|
return [1, 3, 4, 7, 11].includes(race) ? EFaction.Alliance : EFaction.Horde;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import * as express from "express";
|
import * as express from "express";
|
||||||
import { RowDataPacket } from "mysql2/promise";
|
import { RowDataPacket } from "mysql2/promise";
|
||||||
|
|
||||||
|
import { Utils } from "../Utils";
|
||||||
import { Armory } from "../Armory";
|
import { Armory } from "../Armory";
|
||||||
import { IRealmConfig } from "../Config";
|
import { IRealmConfig } from "../Config";
|
||||||
import { IAchievement } from "../data/DbcReader";
|
import { IAchievement } from "../data/DbcReader";
|
||||||
|
|
@ -732,10 +733,8 @@ export class CharacterController {
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getAchievements(realm: string, charData: ICharacterData): Promise<{ achievements: any[]; earned: { [key: number]: any }; }> {
|
private async getAchievements(realm: string, charData: ICharacterData): Promise<{ achievements: any[]; earned: { [key: number]: any }; }> {
|
||||||
const faction = [1, 3, 4, 7, 11].includes(charData.race) ? 1 : 0;
|
|
||||||
|
|
||||||
const promises = await this.armory.dbc.achievement()
|
const promises = await this.armory.dbc.achievement()
|
||||||
.filter(ach => ach.faction === -1 || ach.faction === faction)
|
.filter(ach => ach.faction === -1 || ach.faction === Utils.getFactionFromRaceId(charData.race))
|
||||||
.map(async (ach) => {
|
.map(async (ach) => {
|
||||||
const icon = await this.armory.dbc.spellIcon().find(icon => icon.id === ach.iconId);
|
const icon = await this.armory.dbc.spellIcon().find(icon => icon.id === ach.iconId);
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
177
src/armory/controllers/GuildController.ts
Normal file
|
|
@ -0,0 +1,177 @@
|
||||||
|
import * as express from "express";
|
||||||
|
import { encode } from "html-entities";
|
||||||
|
import { RowDataPacket } from "mysql2/promise";
|
||||||
|
|
||||||
|
import { Utils } from "../Utils";
|
||||||
|
import { Armory } from "../Armory";
|
||||||
|
import { IRealmConfig } from "../Config";
|
||||||
|
import { DataTablesSsp } from "../DataTablesSsp";
|
||||||
|
|
||||||
|
interface IGuildRank {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class GuildController {
|
||||||
|
private armory: Armory;
|
||||||
|
|
||||||
|
public constructor(armory: Armory) {
|
||||||
|
this.armory = armory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async guild(req: express.Request, res: express.Response, next: express.NextFunction): Promise<void> {
|
||||||
|
const realmName = req.params.realm;
|
||||||
|
const guildName = req.params.name;
|
||||||
|
|
||||||
|
const realm = this.armory.getRealm(realmName);
|
||||||
|
if (realm === undefined) {
|
||||||
|
// Could not find realm
|
||||||
|
return next(404);
|
||||||
|
}
|
||||||
|
|
||||||
|
const guildData = await this.getGuildData(realm, guildName);
|
||||||
|
if (guildData === null) {
|
||||||
|
// Could not find guild
|
||||||
|
return next(404);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.render("guild.hbs", {
|
||||||
|
title: `Armory - ${guildName}`,
|
||||||
|
realm: realm.name,
|
||||||
|
...guildData,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async members(req: express.Request, res: express.Response, next: express.NextFunction): Promise<void> {
|
||||||
|
const realmName = req.params.realm;
|
||||||
|
const guildName = req.params.name;
|
||||||
|
|
||||||
|
const realm = this.armory.getRealm(realmName);
|
||||||
|
if (realm === undefined) {
|
||||||
|
// Could not find realm
|
||||||
|
return next(404);
|
||||||
|
}
|
||||||
|
|
||||||
|
const guildId = await this.getGuildId(realm, guildName);
|
||||||
|
if (guildId === null) {
|
||||||
|
// Could not find guild
|
||||||
|
return next(404);
|
||||||
|
}
|
||||||
|
|
||||||
|
const db = this.armory.getCharactersDb(realm.name);
|
||||||
|
const charSet = await this.armory.getDatabaseCharset(realm.name);
|
||||||
|
|
||||||
|
let ssp = new DataTablesSsp(req.query, db, "guild_member", "guid", [
|
||||||
|
{ name: "name", table: "characters", collation: `${charSet}_general_ci` },
|
||||||
|
{ name: "rank" },
|
||||||
|
{ name: "level", table: "characters" },
|
||||||
|
{ name: "class", table: "characters", formatter: cls => Utils.classNames[cls] },
|
||||||
|
{ name: "race", table: "characters", formatter: (race, row) => `${Utils.raceNames[race]}_${row[6] === 0 ? "male" : "female"}` },
|
||||||
|
{ name: "online", table: "characters", formatter: online => online === 1 },
|
||||||
|
]);
|
||||||
|
ssp.joins = [
|
||||||
|
{ table1: "guild_member", column1: "guid", table2: "characters", column2: "guid", kind: "LEFT" },
|
||||||
|
];
|
||||||
|
ssp.extraDataColumns = ["`characters`.`gender`"];
|
||||||
|
|
||||||
|
if (this.armory.config.hideGameMasters) {
|
||||||
|
ssp.joins.push({ table1: "characters", column1: "account", table2: "account_access", column2: "id", database2: realm.authDatabase, kind: "LEFT" });
|
||||||
|
ssp = ssp.where(`\`account_access\`.\`id\` IS NULL OR \`account_access\`.\`RealmID\` NOT IN (-1, ${realm.realmId}) OR \`account_access\`.\`gmlevel\` = 0`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await ssp
|
||||||
|
.where("`guildid` = ?", guildId)
|
||||||
|
.where("`deleteInfos_Account` IS NULL")
|
||||||
|
.run(this.armory.config.dbQueryTimeout);
|
||||||
|
|
||||||
|
const ranks = await this.getGuildRanks(realm, guildId);
|
||||||
|
(result as any).ranks = {};
|
||||||
|
for (const rank of ranks) {
|
||||||
|
(result as any).ranks[rank.id] = encode(rank.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getGuildData(realm: IRealmConfig, name: string): Promise<any> {
|
||||||
|
const db = this.armory.getCharactersDb(realm.name);
|
||||||
|
let [rows, fields] = await db.query({
|
||||||
|
sql: `
|
||||||
|
SELECT guildid, name, leaderguid, EmblemStyle AS emblemStyle, EmblemColor AS emblemColor, BorderStyle AS borderStyle, BorderColor AS borderColor, BackgroundColor AS background
|
||||||
|
FROM guild WHERE name = ?
|
||||||
|
`,
|
||||||
|
values: [name],
|
||||||
|
timeout: this.armory.config.dbQueryTimeout,
|
||||||
|
});
|
||||||
|
if ((rows as RowDataPacket[]).length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const guild = rows[0];
|
||||||
|
|
||||||
|
[rows, fields] = await db.query({
|
||||||
|
sql: `
|
||||||
|
SELECT name, race FROM characters
|
||||||
|
WHERE guid = ?
|
||||||
|
`,
|
||||||
|
values: [guild.leaderguid],
|
||||||
|
timeout: this.armory.config.dbQueryTimeout,
|
||||||
|
});
|
||||||
|
const leader = rows[0];
|
||||||
|
|
||||||
|
[rows, fields] = await db.query({
|
||||||
|
sql: `
|
||||||
|
SELECT COUNT(guid) AS \`count\` FROM guild_member
|
||||||
|
WHERE guildid = ?
|
||||||
|
`,
|
||||||
|
values: [guild.guildid],
|
||||||
|
timeout: this.armory.config.dbQueryTimeout,
|
||||||
|
});
|
||||||
|
const membersCount = rows[0].count;
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: guild.guildid,
|
||||||
|
name: guild.name,
|
||||||
|
leader: leader.name,
|
||||||
|
faction: Utils.getFactionFromRaceId(leader.race),
|
||||||
|
emblem: {
|
||||||
|
icon: guild.emblemStyle.toString().padStart(2, "0"),
|
||||||
|
iconColor: guild.emblemColor.toString().padStart(2, "0"),
|
||||||
|
border: guild.borderStyle.toString().padStart(2, "0"),
|
||||||
|
borderColor: guild.borderColor.toString().padStart(2, "0"),
|
||||||
|
background: guild.background.toString().padStart(2, "0"),
|
||||||
|
},
|
||||||
|
membersCount,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getGuildId(realm: IRealmConfig, name: string): Promise<number> {
|
||||||
|
const db = this.armory.getCharactersDb(realm.name);
|
||||||
|
const [rows, fields] = await db.query({
|
||||||
|
sql: `
|
||||||
|
SELECT guildid
|
||||||
|
FROM guild WHERE name = ?
|
||||||
|
`,
|
||||||
|
values: [name],
|
||||||
|
timeout: this.armory.config.dbQueryTimeout,
|
||||||
|
});
|
||||||
|
if ((rows as RowDataPacket[]).length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rows[0].guildid;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getGuildRanks(realm: IRealmConfig, id: number): Promise<IGuildRank[]> {
|
||||||
|
const db = this.armory.getCharactersDb(realm.name);
|
||||||
|
const [rows, fields] = await db.query({
|
||||||
|
sql: `
|
||||||
|
SELECT rid AS id, rname AS name
|
||||||
|
FROM guild_rank WHERE guildid = ?
|
||||||
|
`,
|
||||||
|
values: [id],
|
||||||
|
timeout: this.armory.config.dbQueryTimeout,
|
||||||
|
});
|
||||||
|
|
||||||
|
return rows as IGuildRank[];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,40 +1,14 @@
|
||||||
import * as express from "express";
|
import * as express from "express";
|
||||||
|
|
||||||
|
import { Utils } from "../Utils";
|
||||||
import { Armory } from "../Armory";
|
import { Armory } from "../Armory";
|
||||||
import { DataTablesSsp } from "../DataTablesSsp";
|
import { DataTablesSsp } from "../DataTablesSsp";
|
||||||
|
|
||||||
const raceFiles = {
|
|
||||||
1: "human",
|
|
||||||
2: "orc",
|
|
||||||
3: "dwarf",
|
|
||||||
4: "nightelf",
|
|
||||||
5: "scourge",
|
|
||||||
6: "tauren",
|
|
||||||
7: "gnome",
|
|
||||||
8: "troll",
|
|
||||||
10: "bloodelf",
|
|
||||||
11: "draenei",
|
|
||||||
};
|
|
||||||
const classFiles = {
|
|
||||||
1: "warrior",
|
|
||||||
2: "paladin",
|
|
||||||
3: "hunter",
|
|
||||||
4: "rogue",
|
|
||||||
5: "priest",
|
|
||||||
6: "deathknight",
|
|
||||||
7: "shaman",
|
|
||||||
8: "mage",
|
|
||||||
9: "warlock",
|
|
||||||
11: "druid",
|
|
||||||
};
|
|
||||||
|
|
||||||
export class IndexController {
|
export class IndexController {
|
||||||
private armory: Armory;
|
private armory: Armory;
|
||||||
private charsetCache: { [key: string]: string };
|
|
||||||
|
|
||||||
public constructor(armory: Armory) {
|
public constructor(armory: Armory) {
|
||||||
this.armory = armory;
|
this.armory = armory;
|
||||||
this.charsetCache = {};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async index(req: express.Request, res: express.Response): Promise<void> {
|
public async index(req: express.Request, res: express.Response): Promise<void> {
|
||||||
|
|
@ -54,28 +28,14 @@ export class IndexController {
|
||||||
}
|
}
|
||||||
|
|
||||||
const db = this.armory.getCharactersDb(realm.name);
|
const db = this.armory.getCharactersDb(realm.name);
|
||||||
|
const charSet = await this.armory.getDatabaseCharset(realm.name);
|
||||||
if (!(realm.name in this.charsetCache)) {
|
|
||||||
let [rows, fields] = await db.query({
|
|
||||||
sql: `
|
|
||||||
SELECT CCSA.character_set_name FROM information_schema.\`TABLES\` T,
|
|
||||||
information_schema.\`COLLATION_CHARACTER_SET_APPLICABILITY\` CCSA
|
|
||||||
WHERE CCSA.collation_name = T.table_collation
|
|
||||||
AND T.table_schema = "${(await db.getConnection()).config.database}"
|
|
||||||
AND T.table_name = "characters"
|
|
||||||
`,
|
|
||||||
timeout: this.armory.config.dbQueryTimeout,
|
|
||||||
});
|
|
||||||
this.charsetCache[realm.name] = rows[0].character_set_name;
|
|
||||||
}
|
|
||||||
const charSet = this.charsetCache[realm.name];
|
|
||||||
|
|
||||||
let ssp = new DataTablesSsp(req.query, db, "characters", "guid", [
|
let ssp = new DataTablesSsp(req.query, db, "characters", "guid", [
|
||||||
{ name: "name", collation: `${charSet}_general_ci` },
|
{ name: "name", collation: `${charSet}_general_ci` },
|
||||||
{ table: "guild", name: "name" },
|
{ table: "guild", name: "name" },
|
||||||
{ name: "level" },
|
{ name: "level" },
|
||||||
{ name: "class", formatter: cls => classFiles[cls] },
|
{ name: "class", formatter: cls => Utils.classNames[cls] },
|
||||||
{ name: "race", formatter: (race, row) => `${raceFiles[race]}_${row[6] === 0 ? "male" : "female"}` },
|
{ name: "race", formatter: (race, row) => `${Utils.raceNames[race]}_${row[6] === 0 ? "male" : "female"}` },
|
||||||
{ name: "online", formatter: online => online === 1 },
|
{ name: "online", formatter: online => online === 1 },
|
||||||
]);
|
]);
|
||||||
ssp.joins = [
|
ssp.joins = [
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,6 @@
|
||||||
column-gap: 10px;
|
column-gap: 10px;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.talent-tree .cols {
|
.talent-tree .cols {
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,12 @@
|
||||||
|
.dataTable {
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dataTable td {
|
||||||
|
text-align: center !important;
|
||||||
|
vertical-align: middle !important;
|
||||||
|
}
|
||||||
|
|
||||||
.dataTables_wrapper .dataTables_info,
|
.dataTables_wrapper .dataTables_info,
|
||||||
.dataTables_wrapper .dataTables_filter,
|
.dataTables_wrapper .dataTables_filter,
|
||||||
.dataTables_wrapper .dataTables_length {
|
.dataTables_wrapper .dataTables_length {
|
||||||
|
|
|
||||||
48
static/css/guild.css
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
#guild-header {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
#guild-header .emblem {
|
||||||
|
margin-right: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#guild-header .info {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.faction {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.faction .logo {
|
||||||
|
height: 32px;
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#emblem-images {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#emblem {
|
||||||
|
background: url("/img/guild-emblems/circle.png") no-repeat center;
|
||||||
|
display: flex;
|
||||||
|
flex-shrink: 0;
|
||||||
|
height: 128px;
|
||||||
|
width: 128px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#emblem canvas {
|
||||||
|
height: 96px;
|
||||||
|
width: 96px;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.circle {
|
||||||
|
clip-path: circle(50% at 50% 50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
#members_processing {
|
||||||
|
height: 70px;
|
||||||
|
}
|
||||||
|
|
@ -2,15 +2,6 @@
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#results {
|
|
||||||
width: 100% !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
#results td {
|
|
||||||
text-align: center;
|
|
||||||
vertical-align: middle;
|
|
||||||
}
|
|
||||||
|
|
||||||
#results_processing {
|
#results_processing {
|
||||||
height: 70px;
|
height: 70px;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
154
static/guild.hbs
Normal file
|
|
@ -0,0 +1,154 @@
|
||||||
|
<link rel="stylesheet" type="text/css" href="/css/guild.css">
|
||||||
|
{{> datatables}}
|
||||||
|
|
||||||
|
<a href="/">Back to Armory</a>
|
||||||
|
<br><br>
|
||||||
|
|
||||||
|
<div id="guild-header">
|
||||||
|
<div class="emblem">
|
||||||
|
<div id="emblem" class="shape-outer circle">
|
||||||
|
<canvas class="shape-inner circle" width="128" height="96"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="info">
|
||||||
|
<div class="char-name title is-size-3"><{{name}}></div>
|
||||||
|
<div class="is-size-5">
|
||||||
|
<div>
|
||||||
|
Guild Master: <a href="/character/{{realm}}/{{leader}}">{{leader}}</a>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{{membersCount}} members
|
||||||
|
</div>
|
||||||
|
{{#eq faction 1}}
|
||||||
|
<div class="faction">
|
||||||
|
<img class="logo" src="/img/PlusManz-Alliance.png">
|
||||||
|
<span>Alliance</span>
|
||||||
|
</div>
|
||||||
|
{{else}}
|
||||||
|
<div class="faction">
|
||||||
|
<img class="logo" src="/img/PlusManz-Horde.png">
|
||||||
|
<span>Horde</span>
|
||||||
|
</div>
|
||||||
|
{{/eq}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
<div id="emblem-images">
|
||||||
|
<img id="emblem-bg-upper" src="/img/guild-emblems/Background_{{emblem.background}}_TU_U.PNG">
|
||||||
|
<img id="emblem-bg-lower" src="/img/guild-emblems/Background_{{emblem.background}}_TL_U.PNG">
|
||||||
|
<img id="emblem-icon-upper" src="/img/guild-emblems/Emblem_{{emblem.icon}}_{{emblem.iconColor}}_TU_U.PNG">
|
||||||
|
<img id="emblem-icon-lower" src="/img/guild-emblems/Emblem_{{emblem.icon}}_{{emblem.iconColor}}_TL_U.PNG">
|
||||||
|
<img id="emblem-border-upper" src="/img/guild-emblems/Border_{{emblem.border}}_{{emblem.borderColor}}_TU_U.PNG">
|
||||||
|
<img id="emblem-border-lower" src="/img/guild-emblems/Border_{{emblem.border}}_{{emblem.borderColor}}_TL_U.PNG">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table id="members" class="stripe hover row-border">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Rank</th>
|
||||||
|
<th>Level</th>
|
||||||
|
<th>Class</th>
|
||||||
|
<th>Race</th>
|
||||||
|
<th>Online</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody></tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<script type="application/javascript">
|
||||||
|
function createEmblem() {
|
||||||
|
const canvas = $("#emblem canvas")[0];
|
||||||
|
const ctx = canvas.getContext("2d");
|
||||||
|
|
||||||
|
const mask = $("#emblem-mask")[0];
|
||||||
|
const bgUpper = $("#emblem-bg-upper")[0];
|
||||||
|
const bgLower = $("#emblem-bg-lower")[0];
|
||||||
|
const iconUpper = $("#emblem-icon-upper")[0];
|
||||||
|
const iconLower = $("#emblem-icon-lower")[0];
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(0, 0);
|
||||||
|
ctx.lineTo(26, 0);
|
||||||
|
ctx.lineTo(64, 28);
|
||||||
|
ctx.lineTo(102, 0);
|
||||||
|
ctx.lineTo(128, 0);
|
||||||
|
ctx.lineTo(128, 96);
|
||||||
|
ctx.lineTo(0, 96);
|
||||||
|
ctx.closePath();
|
||||||
|
ctx.clip();
|
||||||
|
drawEmblemLayer(canvas, ctx, "bg");
|
||||||
|
drawEmblemLayer(canvas, ctx, "icon");
|
||||||
|
drawEmblemLayer(canvas, ctx, "border");
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawEmblemLayer(canvas, ctx, layer) {
|
||||||
|
const upper = $(`#emblem-${layer}-upper`)[0];
|
||||||
|
const lower = $(`#emblem-${layer}-lower`)[0];
|
||||||
|
|
||||||
|
const w = upper.width / 2;
|
||||||
|
const uh = upper.height;
|
||||||
|
const lh = lower.height;
|
||||||
|
|
||||||
|
ctx.drawImage(upper, 0, 0, w, uh, w, 0, w, uh);
|
||||||
|
ctx.drawImage(lower, 0, 0, w, lh, w, upper.height, w, lh);
|
||||||
|
ctx.save();
|
||||||
|
ctx.scale(-1, 1);
|
||||||
|
ctx.drawImage(upper, 0, 0, w, uh, 0, 0, -w, uh);
|
||||||
|
ctx.drawImage(lower, 0, 0, w, lh, 0, upper.height, -w, lh);
|
||||||
|
ctx.restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
const emblemLoadPromises = $("#emblem-images img").map((idx, img) => {
|
||||||
|
return new Promise((res, rej) => {
|
||||||
|
if (img.complete) {
|
||||||
|
res();
|
||||||
|
} else {
|
||||||
|
img.addEventListener("load", res);
|
||||||
|
img.addEventListener("error", rej);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
Promise.all(emblemLoadPromises).then(createEmblem);
|
||||||
|
|
||||||
|
dt = $("#members").DataTable({
|
||||||
|
processing: true,
|
||||||
|
serverSide: true,
|
||||||
|
searchDelay: 800,
|
||||||
|
ajax: {
|
||||||
|
url: `/guild/{{realm}}/{{name}}/members`,
|
||||||
|
},
|
||||||
|
columnDefs: [
|
||||||
|
{
|
||||||
|
targets: 0,
|
||||||
|
render: (name) => `<a href="/character/{{realm}}/${name}">${name}</a>`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
targets: 1,
|
||||||
|
render: (rank, type, row, meta) => meta.settings.json.ranks[rank],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
searchable: false,
|
||||||
|
targets: 3,
|
||||||
|
render: data => `<img src="{{aowow}}/static/images/wow/icons/medium/class_${data}.jpg">`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
searchable: false,
|
||||||
|
targets: 4,
|
||||||
|
render: data => `<img src="{{aowow}}/static/images/wow/icons/medium/race_${data}.jpg">`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
searchable: false,
|
||||||
|
targets: 5,
|
||||||
|
render: online => online ? "🟢" : "🔴",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
responsive: {
|
||||||
|
details: true,
|
||||||
|
},
|
||||||
|
order: [[1, "asc"]],
|
||||||
|
});
|
||||||
|
</script>
|
||||||
BIN
static/img/PlusManz-Alliance.png
Normal file
|
After Width: | Height: | Size: 4 KiB |
BIN
static/img/PlusManz-Horde.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
static/img/guild-emblems/BACKGROUND_29_TU_U.PNG
Normal file
|
After Width: | Height: | Size: 8.6 KiB |
BIN
static/img/guild-emblems/Background_00_TL_U.PNG
Normal file
|
After Width: | Height: | Size: 5.5 KiB |
BIN
static/img/guild-emblems/Background_00_TU_U.PNG
Normal file
|
After Width: | Height: | Size: 8.9 KiB |
BIN
static/img/guild-emblems/Background_01_TL_U.PNG
Normal file
|
After Width: | Height: | Size: 5.7 KiB |
BIN
static/img/guild-emblems/Background_01_TU_U.PNG
Normal file
|
After Width: | Height: | Size: 8.8 KiB |
BIN
static/img/guild-emblems/Background_02_TL_U.PNG
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
BIN
static/img/guild-emblems/Background_02_TU_U.PNG
Normal file
|
After Width: | Height: | Size: 8.5 KiB |
BIN
static/img/guild-emblems/Background_03_TL_U.PNG
Normal file
|
After Width: | Height: | Size: 5.4 KiB |
BIN
static/img/guild-emblems/Background_03_TU_U.PNG
Normal file
|
After Width: | Height: | Size: 8.6 KiB |
BIN
static/img/guild-emblems/Background_04_TL_U.PNG
Normal file
|
After Width: | Height: | Size: 5.5 KiB |
BIN
static/img/guild-emblems/Background_04_TU_U.PNG
Normal file
|
After Width: | Height: | Size: 8.6 KiB |
BIN
static/img/guild-emblems/Background_05_TL_U.PNG
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
BIN
static/img/guild-emblems/Background_05_TU_U.PNG
Normal file
|
After Width: | Height: | Size: 8 KiB |
BIN
static/img/guild-emblems/Background_06_TL_U.PNG
Normal file
|
After Width: | Height: | Size: 5.6 KiB |
BIN
static/img/guild-emblems/Background_06_TU_U.PNG
Normal file
|
After Width: | Height: | Size: 8.8 KiB |
BIN
static/img/guild-emblems/Background_07_TL_U.PNG
Normal file
|
After Width: | Height: | Size: 5.8 KiB |
BIN
static/img/guild-emblems/Background_07_TU_U.PNG
Normal file
|
After Width: | Height: | Size: 9.1 KiB |
BIN
static/img/guild-emblems/Background_08_TL_U.PNG
Normal file
|
After Width: | Height: | Size: 5.5 KiB |
BIN
static/img/guild-emblems/Background_08_TU_U.PNG
Normal file
|
After Width: | Height: | Size: 8.9 KiB |
BIN
static/img/guild-emblems/Background_09_TL_U.PNG
Normal file
|
After Width: | Height: | Size: 5.6 KiB |
BIN
static/img/guild-emblems/Background_09_TU_U.PNG
Normal file
|
After Width: | Height: | Size: 9 KiB |
BIN
static/img/guild-emblems/Background_10_TL_U.PNG
Normal file
|
After Width: | Height: | Size: 6 KiB |
BIN
static/img/guild-emblems/Background_10_TU_U.PNG
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
static/img/guild-emblems/Background_11_TL_U.PNG
Normal file
|
After Width: | Height: | Size: 5.7 KiB |
BIN
static/img/guild-emblems/Background_11_TU_U.PNG
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
static/img/guild-emblems/Background_12_TL_U.PNG
Normal file
|
After Width: | Height: | Size: 5.8 KiB |
BIN
static/img/guild-emblems/Background_12_TU_U.PNG
Normal file
|
After Width: | Height: | Size: 9.5 KiB |
BIN
static/img/guild-emblems/Background_13_TL_U.PNG
Normal file
|
After Width: | Height: | Size: 6.1 KiB |
BIN
static/img/guild-emblems/Background_13_TU_U.PNG
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
static/img/guild-emblems/Background_14_TL_U.PNG
Normal file
|
After Width: | Height: | Size: 5.6 KiB |
BIN
static/img/guild-emblems/Background_14_TU_U.PNG
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
static/img/guild-emblems/Background_15_TL_U.PNG
Normal file
|
After Width: | Height: | Size: 5.8 KiB |
BIN
static/img/guild-emblems/Background_15_TU_U.PNG
Normal file
|
After Width: | Height: | Size: 8.7 KiB |
BIN
static/img/guild-emblems/Background_16_TL_U.PNG
Normal file
|
After Width: | Height: | Size: 5.8 KiB |
BIN
static/img/guild-emblems/Background_16_TU_U.PNG
Normal file
|
After Width: | Height: | Size: 8.9 KiB |
BIN
static/img/guild-emblems/Background_17_TL_U.PNG
Normal file
|
After Width: | Height: | Size: 5.7 KiB |
BIN
static/img/guild-emblems/Background_17_TU_U.PNG
Normal file
|
After Width: | Height: | Size: 8.9 KiB |
BIN
static/img/guild-emblems/Background_18_TL_U.PNG
Normal file
|
After Width: | Height: | Size: 5.8 KiB |
BIN
static/img/guild-emblems/Background_18_TU_U.PNG
Normal file
|
After Width: | Height: | Size: 8.9 KiB |
BIN
static/img/guild-emblems/Background_19_TL_U.PNG
Normal file
|
After Width: | Height: | Size: 5.7 KiB |
BIN
static/img/guild-emblems/Background_19_TU_U.PNG
Normal file
|
After Width: | Height: | Size: 8.7 KiB |
BIN
static/img/guild-emblems/Background_20_TL_U.PNG
Normal file
|
After Width: | Height: | Size: 5.4 KiB |
BIN
static/img/guild-emblems/Background_20_TU_U.PNG
Normal file
|
After Width: | Height: | Size: 8.4 KiB |
BIN
static/img/guild-emblems/Background_21_TL_U.PNG
Normal file
|
After Width: | Height: | Size: 5.8 KiB |
BIN
static/img/guild-emblems/Background_21_TU_U.PNG
Normal file
|
After Width: | Height: | Size: 8.7 KiB |
BIN
static/img/guild-emblems/Background_22_TL_U.PNG
Normal file
|
After Width: | Height: | Size: 5.6 KiB |
BIN
static/img/guild-emblems/Background_22_TU_U.PNG
Normal file
|
After Width: | Height: | Size: 8.4 KiB |
BIN
static/img/guild-emblems/Background_23_TL_U.PNG
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
BIN
static/img/guild-emblems/Background_23_TU_U.PNG
Normal file
|
After Width: | Height: | Size: 8.2 KiB |
BIN
static/img/guild-emblems/Background_24_TL_U.PNG
Normal file
|
After Width: | Height: | Size: 5.8 KiB |
BIN
static/img/guild-emblems/Background_24_TU_U.PNG
Normal file
|
After Width: | Height: | Size: 8.8 KiB |
BIN
static/img/guild-emblems/Background_25_TL_U.PNG
Normal file
|
After Width: | Height: | Size: 5.7 KiB |
BIN
static/img/guild-emblems/Background_25_TU_U.PNG
Normal file
|
After Width: | Height: | Size: 8.6 KiB |
BIN
static/img/guild-emblems/Background_26_TL_U.PNG
Normal file
|
After Width: | Height: | Size: 5.6 KiB |
BIN
static/img/guild-emblems/Background_26_TU_U.PNG
Normal file
|
After Width: | Height: | Size: 8.7 KiB |
BIN
static/img/guild-emblems/Background_27_TL_U.PNG
Normal file
|
After Width: | Height: | Size: 5.9 KiB |
BIN
static/img/guild-emblems/Background_27_TU_U.PNG
Normal file
|
After Width: | Height: | Size: 9.1 KiB |
BIN
static/img/guild-emblems/Background_28_TL_U.PNG
Normal file
|
After Width: | Height: | Size: 5.7 KiB |
BIN
static/img/guild-emblems/Background_28_TU_U.PNG
Normal file
|
After Width: | Height: | Size: 9.1 KiB |
BIN
static/img/guild-emblems/Background_29_TL_U.PNG
Normal file
|
After Width: | Height: | Size: 5.5 KiB |
BIN
static/img/guild-emblems/Background_30_TL_U.PNG
Normal file
|
After Width: | Height: | Size: 6 KiB |
BIN
static/img/guild-emblems/Background_30_TU_U.PNG
Normal file
|
After Width: | Height: | Size: 9.6 KiB |
BIN
static/img/guild-emblems/Background_31_TL_U.PNG
Normal file
|
After Width: | Height: | Size: 6 KiB |
BIN
static/img/guild-emblems/Background_31_TU_U.PNG
Normal file
|
After Width: | Height: | Size: 9.5 KiB |
BIN
static/img/guild-emblems/Background_32_TL_U.PNG
Normal file
|
After Width: | Height: | Size: 5.5 KiB |
BIN
static/img/guild-emblems/Background_32_TU_U.PNG
Normal file
|
After Width: | Height: | Size: 8.8 KiB |
BIN
static/img/guild-emblems/Background_33_TL_U.PNG
Normal file
|
After Width: | Height: | Size: 6.1 KiB |
BIN
static/img/guild-emblems/Background_33_TU_U.PNG
Normal file
|
After Width: | Height: | Size: 9.1 KiB |
BIN
static/img/guild-emblems/Background_34_TL_U.PNG
Normal file
|
After Width: | Height: | Size: 6 KiB |
BIN
static/img/guild-emblems/Background_34_TU_U.PNG
Normal file
|
After Width: | Height: | Size: 9.2 KiB |
BIN
static/img/guild-emblems/Background_35_TL_U.PNG
Normal file
|
After Width: | Height: | Size: 5.6 KiB |
BIN
static/img/guild-emblems/Background_35_TU_U.PNG
Normal file
|
After Width: | Height: | Size: 8.9 KiB |
BIN
static/img/guild-emblems/Background_36_TL_U.PNG
Normal file
|
After Width: | Height: | Size: 5.5 KiB |
BIN
static/img/guild-emblems/Background_36_TU_U.PNG
Normal file
|
After Width: | Height: | Size: 8.1 KiB |
BIN
static/img/guild-emblems/Background_37_TL_U.PNG
Normal file
|
After Width: | Height: | Size: 5.8 KiB |
BIN
static/img/guild-emblems/Background_37_TU_U.PNG
Normal file
|
After Width: | Height: | Size: 9 KiB |
BIN
static/img/guild-emblems/Background_38_TL_U.PNG
Normal file
|
After Width: | Height: | Size: 5.6 KiB |
BIN
static/img/guild-emblems/Background_38_TU_U.PNG
Normal file
|
After Width: | Height: | Size: 8.8 KiB |
BIN
static/img/guild-emblems/Background_39_TL_U.PNG
Normal file
|
After Width: | Height: | Size: 5.5 KiB |
BIN
static/img/guild-emblems/Background_39_TU_U.PNG
Normal file
|
After Width: | Height: | Size: 9.2 KiB |
BIN
static/img/guild-emblems/Background_40_TL_U.PNG
Normal file
|
After Width: | Height: | Size: 5.7 KiB |
BIN
static/img/guild-emblems/Background_40_TU_U.PNG
Normal file
|
After Width: | Height: | Size: 8.9 KiB |
BIN
static/img/guild-emblems/Background_41_TL_U.PNG
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
static/img/guild-emblems/Background_41_TU_U.PNG
Normal file
|
After Width: | Height: | Size: 8.4 KiB |
BIN
static/img/guild-emblems/Background_42_TL_U.PNG
Normal file
|
After Width: | Height: | Size: 6 KiB |
BIN
static/img/guild-emblems/Background_42_TU_U.PNG
Normal file
|
After Width: | Height: | Size: 9.3 KiB |