import { Misc } from "../misc.js"; import { TMRConstants, tmrTokenZIndex } from "../tmr-constants.js"; import { TMRUtility } from "../tmr-utility.js"; import { EffetsDraconiques } from "./effets-draconiques.js"; export class PixiTMR { static textures = [] static getImgFromCode(code) { return PixiTMR.textures[code] } static register(name, img) { PixiTMR.textures[name] = img; } static async init() { await Promise.all( Object.values(PixiTMR.textures) .filter(img => img != undefined && !PIXI.utils.TextureCache[img]) .map(async img => PIXI.Sprite.from(await PIXI.Assets.load(img)))) } constructor(tmrDialog, displaySize) { this.tmrDialog = tmrDialog; this.callbacksOnAnimate = []; this.sizes = new TMRConstants({ size: displaySize }) console.info(`Creation d'Application PIXI pour les TMR de ${tmrDialog.actor.name}`) this.pixiApp = new PIXI.Application(PixiTMR.computeTMRSize(this.sizes)); this.pixiApp.eventMode = 'static'; this.pixiApp.stage.sortableChildren = true; this.tooltipStyle = new PIXI.TextStyle({ fontFamily: 'CaslonAntique', fontSize: 16, fill: '#FFFFFF', stroke: '#000000', strokeThickness: 4 }); this.tooltip = new PIXI.Text('', this.tooltipStyle); this.tooltip.zIndex = 1000 this.pixiApp.stage.addChild(this.tooltip); } close() { console.info(`Destruction d'Application PIXI pour les TMR de ${this.tmrDialog.actor.name}`) this.pixiApp.destroy(); this.pixiApp = undefined } static computeTMRSize(sizeConstants) { return { width: sizeConstants.cellw * 13 + sizeConstants.marginx, height: sizeConstants.cellh / 2 + sizeConstants.cellh * 15 + sizeConstants.marginy } } resizeTMR(displaySize) { this.sizes = new TMRConstants({ size: displaySize }) const appSize = PixiTMR.computeTMRSize(this.sizes) this.pixiApp.renderer.resize(appSize.width, appSize.height) this.tooltipStyle.fontSize = Math.max(this.sizes.size / 3, 16) } get view() { return this.pixiApp.view } setup() { this.carteTMR = EffetsDraconiques.carteTmr.createSprite(this); this.pixiApp.stage.addChild(this.carteTMR); this.carteTMR.isOver = false; this.carteTMR.eventMode = 'static'; this.carteTMR .on('pointermove', event => this.onPointerMove(event)) .on('pointerdown', event => this.onClickBackground(event)) .on('pointerover', event => this.onShowTooltip(event)) .on('pointerout', event => this.onHideTooltip(event)); } async loadAnimations() { for (let onAnimate of this.callbacksOnAnimate) { onAnimate(); } this.pixiApp.ticker.start(); } animate(animation = pixiApp => { }) { this.callbacksOnAnimate.push(() => animation(this.pixiApp)); } addMarkTMR(coordTMR) { const rect = this.getCaseRectangle(TMRUtility.coordTMRToOddq(coordTMR)) const markTMR = new PIXI.Graphics(); markTMR.beginFill(0xffff00, 0.3); // set the line style to have a width of 5 and set the color to red markTMR.lineStyle(5, 0xff0000); // draw a rectangle markTMR.drawRect(rect.x, rect.y, rect.w, rect.h); this.pixiApp.stage.addChild(markTMR); return markTMR } removeGraphic(graphic) { this.pixiApp.stage.removeChild(graphic); } sprite(code, options = {}) { let img = PixiTMR.getImgFromCode(code) let texture = PIXI.utils.TextureCache[img] if (!texture) { // TODO: charger la texture console.error("Texture manquante", code, PIXI.utils.TextureCache) return; } let sprite = new PIXI.Sprite(texture); sprite.taille = options.taille ?? (() => this.sizes.half) sprite.width = sprite.taille() sprite.height = sprite.taille() sprite.anchor.set(0.5) if (options.tint) { sprite.tint = options.tint } sprite.zIndex = options.zIndex ?? tmrTokenZIndex.casehumide + 1 sprite.alpha = options.alpha ?? 1 sprite.decallage = options.decallage ?? this.sizes.center this.pixiApp.stage.addChild(sprite) return sprite } circle(code, options = {}) { let sprite = new PIXI.Graphics() sprite.taille = options.taille ?? (() => this.sizes.half) sprite.decallage = options.decallage ?? this.sizes.topLeft sprite.beginFill(options.tint, options.opacity) sprite.drawCircle(0, 0, sprite.taille()) sprite.endFill() this.pixiApp.stage.addChild(sprite) return sprite } square(code, options = {}) { let sprite = new PIXI.Graphics(); sprite.taille = options.taille ?? (() => this.sizes.half) sprite.decallage = options.decallage ?? this.sizes.topLeft sprite.beginFill(options.tint, options.opacity) const size = sprite.taille(); sprite.drawRect(0, 0, size, size) sprite.endFill() this.pixiApp.stage.addChild(sprite) return sprite } onClickBackground(event) { if (!this.viewOnly) { this.tmrDialog.onClickTMR(event) } } onPointerMove(event) { if (this.carteTMR.isOver) { this.setTooltipPosition(event); this.tooltip.text = this.computeTooltip(event); } } onShowTooltip(event) { if (!this.carteTMR.isOver) { this.setTooltipPosition(event); this.pixiApp.stage.addChild(this.tooltip); this.tooltip.text = this.computeTooltip(event); } this.carteTMR.isOver = true; } onHideTooltip(event) { if (this.carteTMR.isOver) { this.pixiApp.stage.removeChild(this.tooltip); } this.carteTMR.isOver = false; } computeTooltip(event) { const oddq = this.sizes.computeEventOddq(event); const coordTMR = TMRUtility.oddqToCoordTMR(oddq); const tmr = TMRUtility.getTMR(coordTMR) if (tmr) { const tmrTooltip = `${coordTMR}: ${TMRUtility.getTMRLabel(coordTMR)}`; const tokenTooltips = this.tmrDialog.allTokens .filter(token => token.coordTMR() == coordTMR) .map(token => token.tooltip); return [tmrTooltip, ...tokenTooltips].reduce(Misc.joining('\n')) } } computeEventOddq(event) { return this.sizes.computeEventOddq(event) } setTooltipPosition(event) { const oddq = this.sizes.computeEventOddq(event); this.tooltip.x = oddq.x + (oddq.col > 7 ? -2.5 * this.sizes.full : this.sizes.quarter); this.tooltip.y = oddq.y + (oddq.row > 10 ? -this.sizes.size : 0); } positionToken(token) { if (token.sprite) { const sprite = token.sprite; const oddq = TMRUtility.coordTMRToOddq(token.coordTMR()); const decallagePairImpair = (oddq.col % 2 == 0) ? this.sizes.col1_y : this.sizes.col2_y; const dx = sprite.decallage?.x ?? 0 const dy = sprite.decallage?.y ?? 0 sprite.x = this.sizes.gridx + (oddq.col * this.sizes.cellw) + dx; sprite.y = this.sizes.gridy + (oddq.row * this.sizes.cellh) + dy + decallagePairImpair; } } removeToken(token) { if (token.sprite) { this.pixiApp.stage.removeChild(token.sprite) } } getCaseRectangle(oddq) { const decallagePairImpair = (oddq.col % 2 == 0) ? this.sizes.col1_y : this.sizes.col2_y; const x = this.sizes.gridx + (oddq.col * this.sizes.cellw) - (this.sizes.cellw / 2); const y = this.sizes.gridy + (oddq.row * this.sizes.cellh) - (this.sizes.cellh / 2) + decallagePairImpair; return { x, y, w: this.sizes.cellw, h: this.sizes.cellh }; } }