diff --git a/src/nitro/session/badge/BadgeImageManager.ts b/src/nitro/session/badge/BadgeImageManager.ts new file mode 100644 index 00000000..c95b5c36 --- /dev/null +++ b/src/nitro/session/badge/BadgeImageManager.ts @@ -0,0 +1,275 @@ +import { Resource, Texture } from '@pixi/core'; +import { BadgeTypeEnum, GroupBadge } from '.'; +import { IAssetManager } from '../../../core/asset/IAssetManager'; +import { IMessageEvent } from '../../../core/communication/messages/IMessageEvent'; +import { NitroSprite } from '../../../core/utils/proxy/NitroSprite'; +import { GroupBadgePartsEvent } from '../../communication/messages/incoming/group/GroupBadgePartsEvent'; +import { GroupBadgePartsComposer } from '../../communication/messages/outgoing/group/GroupBadgePartsComposer'; +import { Nitro } from '../../Nitro'; +import { BadgeImageReadyEvent } from '../events/BadgeImageReadyEvent'; +import { ISessionDataManager } from '../ISessionDataManager'; +import { TextureUtils } from './../../../room/utils/TextureUtils'; +import { SessionDataManager } from './../SessionDataManager'; +import { BadgeInfo } from './BadgeInfo'; +import { GroupBadgePart } from './GroupBadgePart'; +import { IBadgeImageManager } from './IBadgeImageManager'; + +export class BadgeImageManager implements IBadgeImageManager +{ + private _assets: IAssetManager; + private _sessionDataManager: SessionDataManager; + private _messages: IMessageEvent[]; + + private _groupBases: Map; + private _groupSymbols: Map; + private _groupPartColors: Map; + + private _requestedBadges: Map; + private _groupBadgesQueue: Map; + + private _readyToGenerateGroupBadges: boolean; + + constructor(assetManager: IAssetManager, sessionDataManager: ISessionDataManager) + { + this._assets = assetManager; + this._sessionDataManager = sessionDataManager; + + this._groupBases = new Map(); + this._groupSymbols = new Map(); + this._groupPartColors = new Map(); + + this._requestedBadges = new Map(); + + this._readyToGenerateGroupBadges = false; + } + + public init(): void + { + if(this._sessionDataManager && this._sessionDataManager.communication) + { + this._messages = [ + new GroupBadgePartsEvent(this.onGroupBadgePartsEvent.bind(this)) + ]; + + for(const message of this._messages) this._sessionDataManager.communication.registerMessageEvent(message); + + this._sessionDataManager.send(new GroupBadgePartsComposer()); + } + } + + public dispose(): void + { + if(this._messages && this._messages.length) + { + for(const message of this._messages) this._sessionDataManager.communication.removeMessageEvent(message); + + this._messages = null; + } + + this._sessionDataManager = null; + } + + public getBadgeImage(badgeName: string, type: string = BadgeTypeEnum.NORMAL_BADGE, load: boolean = true): Texture + { + let badge = this.getBadgeTexture(badgeName, type); + + if(!badge && load) badge = this.getBadgePlaceholder(); + + return badge; + } + + public getBadgeInfo(k: string): BadgeInfo + { + const badge = this.getBadgeTexture(k); + + return (badge) ? new BadgeInfo(badge, false) : new BadgeInfo(this.getBadgePlaceholder(), true); + } + + public loadBadgeImage(badgeName: string, type: string = BadgeTypeEnum.NORMAL_BADGE): string + { + if(this._assets.getTexture(this.getBadgeUrl(badgeName, type))) return badgeName; + + this.getBadgeTexture(badgeName, type); + + return null; + } + + private getBadgeTexture(badgeName: string, type: string = BadgeTypeEnum.NORMAL_BADGE): Texture + { + const url = this.getBadgeUrl(badgeName, type); + + const existing = this._assets.getTexture(url); + + if(existing) return existing.clone(); + + if(this._requestedBadges.get(badgeName)) return null; + + if(url) + { + this._requestedBadges.set(badgeName, true); + + if(type === BadgeTypeEnum.NORMAL_BADGE) + { + this._assets.downloadAsset(url, (flag: boolean) => + { + if(flag) + { + this._requestedBadges.delete(badgeName); + + const texture = this._assets.getTexture(url); + + if(texture && this._sessionDataManager) this._sessionDataManager.events.dispatchEvent(new BadgeImageReadyEvent(badgeName, texture.clone())); + } + }); + } + else + { + if(!this._readyToGenerateGroupBadges) + { + if(this._groupBadgesQueue.get(badgeName)) this._groupBadgesQueue.set(badgeName, true); + } + else + { + this.loadGroupBadge(badgeName); + } + } + } + + return null; + } + + private getBadgePlaceholder(): Texture + { + const url = (Nitro.instance.getConfiguration('images.url') + '/loading_icon.png'); + const existing = this._assets.getTexture(url); + + if(!existing) return null; + + return existing.clone(); + } + + public getBadgeUrl(badge: string, type: string = BadgeTypeEnum.NORMAL_BADGE): string + { + let url = null; + + switch(type) + { + case BadgeTypeEnum.NORMAL_BADGE: + url = (Nitro.instance.getConfiguration('badge.asset.url')).replace('%badgename%', badge); + break; + case BadgeTypeEnum.GROUP_BADGE: + //url = (Nitro.instance.getConfiguration('badge.asset.group.url')).replace('%badgedata%', badge); + url = badge; + break; + } + + return url; + } + + private loadGroupBadge(badgeCode: string): void + { + const groupBadge = new GroupBadge(badgeCode); + const imagePath = Nitro.instance.getConfiguration('badge.asset.grouparts.url'); + + const urlsToLoad: string[] = []; + + for(let i = 0; i < badgeCode.length; i += 6) + { + const partType = badgeCode.slice(i, i + 1); + + let partId = parseInt(badgeCode.slice(i + 1, i + 3)); + const partColor = parseInt(badgeCode.slice(i + 3, i + 5)); + const partPosition = parseInt(badgeCode.slice(i + 5, i + 6)); + + if(partType === 't') partId += 100; + + const part = new GroupBadgePart(partType, partId, partColor, partPosition); + groupBadge.parts.push(part); + + const isBase = (partType === 'b'); + + const requiredAssets = isBase ? this._groupBases.get(partId) : this._groupSymbols.get(partId); + + for(const requiredAsset of requiredAssets) + { + if(requiredAsset.length > 0) + { + const url = imagePath.replace('%part%', requiredAsset); + part.urls.push(url); + + if(!this._assets.getAsset(requiredAsset)) urlsToLoad.push(url); + } + } + } + + if(urlsToLoad.length === 0) return this.renderGroupBadge(groupBadge); + + this._assets.downloadAssets(urlsToLoad, (flag: boolean) => + { + this.renderGroupBadge(groupBadge); + }); + } + + private renderGroupBadge(groupBadge: GroupBadge): void + { + for(const part of groupBadge.parts) + { + for(const partUrl of part.urls) + { + const texture = this._assets.getTexture(partUrl); + + if(!texture) continue; //Generate with what we got + + const pos = part.calculatePosition(texture); + + const sprite = new NitroSprite(texture); + sprite.x = pos.x; + sprite.y = pos.y; + sprite.tint = parseInt(this._groupPartColors.get(part.color), 16); + + groupBadge.container.addChild(sprite); + } + } + + this._requestedBadges.delete(groupBadge.code); + this._groupBadgesQueue.delete(groupBadge.code); + + const texture = TextureUtils.generateTexture(groupBadge.container); + + this._assets.setTexture(groupBadge.code, texture); + + if(this._sessionDataManager) this._sessionDataManager.events.dispatchEvent(new BadgeImageReadyEvent(groupBadge.code, texture)); + } + + private onGroupBadgePartsEvent(event: GroupBadgePartsEvent): void + { + if(!event) return; + + const data = event.getParser(); + + if(!data) return; + + data.bases.forEach( (names, id) => + { + this._groupBases.set(id, names.map( val => val.replace('.png', '').replace('.gif', ''))); + }); + + data.symbols.forEach( (names, id) => + { + this._groupSymbols.set(id, names.map( val => val.replace('.png', '').replace('.gif', ''))); + }); + + this._groupPartColors = data.partColors; + this._readyToGenerateGroupBadges = true; + + this._groupBadgesQueue.forEach((_, badgeCode) => + { + this.loadGroupBadge(badgeCode); + }); + } + + public get disposed(): boolean + { + return !!this._sessionDataManager; + } +} diff --git a/src/nitro/session/badge/GroupBadge.ts b/src/nitro/session/badge/GroupBadge.ts new file mode 100644 index 00000000..b9064693 --- /dev/null +++ b/src/nitro/session/badge/GroupBadge.ts @@ -0,0 +1,31 @@ +import { NitroContainer } from '../../..'; +import { GroupBadgePart } from './GroupBadgePart'; + +export class GroupBadge +{ + private _code: string; + private _parts: GroupBadgePart[]; + private _container: NitroContainer; + + constructor(code: string) + { + this._code = code; + this._parts = []; + this._container = new NitroContainer(); + } + + public get code(): string + { + return this._code; + } + + public get parts(): GroupBadgePart[] + { + return this._parts; + } + + public get container(): NitroContainer + { + return this._container; + } +} diff --git a/src/nitro/session/badge/GroupBadgePart.ts b/src/nitro/session/badge/GroupBadgePart.ts new file mode 100644 index 00000000..4b545d90 --- /dev/null +++ b/src/nitro/session/badge/GroupBadgePart.ts @@ -0,0 +1,69 @@ +import { NitroPoint, NitroTexture } from '../../..'; + +export class GroupBadgePart +{ + public static BASE: string = 'b'; + public static SYMBOL: string = 's'; + public static SYMBOL_ALT: string = 't'; + + public static BASE_PART: number = 0; + public static LAYER_PART: number = 1; + public static IMAGE_WIDTH: number = 39; + public static IMAGE_HEIGHT: number = 39; + public static CELL_WIDTH: number = 13; + public static CELL_HEIGHT: number = 13; + + public type: string; + public key: number; + public color: number; + public position: number; + public urls: string[]; + + constructor(type: string, key?: number, color?: number, position?: number) + { + this.type = type; + this.key = key ? key : 0; + this.color = color ? color : 0; + this.position = position ? position : 4; + this.urls = []; + } + + public get code(): string + { + if(this.key === 0) return null; + + return GroupBadgePart.getCode(this.type, this.key, this.color, this.position); + } + + public static getCode(type: string, key: number, color: number, position: number): string + { + return (type === GroupBadgePart.BASE ? type : key >= 100 ? GroupBadgePart.SYMBOL_ALT : GroupBadgePart.SYMBOL) + (key < 10 ? '0' : '') + (type === GroupBadgePart.BASE ? key : key >= 100 ? key - 100 : key) + (color < 10 ? '0' : '') + color + position; + } + + public calculatePosition(asset: NitroTexture) : NitroPoint + { + const gridPos = this.calculateGridPos(this.position); + + let x: number = (((GroupBadgePart.CELL_WIDTH * gridPos.x) + (GroupBadgePart.CELL_WIDTH / 2)) - (asset.width / 2)); + let y: number = (((GroupBadgePart.CELL_HEIGHT * gridPos.y) + (GroupBadgePart.CELL_HEIGHT / 2)) - (asset.height / 2)); + + if(x < 0) x = 0; + + if((x + asset.width) > GroupBadgePart.IMAGE_WIDTH) x = (GroupBadgePart.IMAGE_WIDTH - asset.width); + + if(y < 0) y = 0; + + if((y + asset.height) > GroupBadgePart.IMAGE_HEIGHT) y = (GroupBadgePart.IMAGE_HEIGHT - asset.height); + + return new NitroPoint(Math.floor(x), Math.floor(y)); + } + + private calculateGridPos(gridVal: number): NitroPoint + { + const point = new NitroPoint(); + point.x = Math.floor((gridVal % 3)); + point.y = Math.floor((gridVal / 3)); + + return point; + } +} diff --git a/src/nitro/session/badge/index.ts b/src/nitro/session/badge/index.ts new file mode 100644 index 00000000..fa31d966 --- /dev/null +++ b/src/nitro/session/badge/index.ts @@ -0,0 +1,2 @@ +export * from './BadgeImageManager'; +export * from './GroupBadgePart';