| | |
| | | import * as TWEEDLE from 'tweedle.js'; |
| | | import * as Tool from './tool'; |
| | | import star from '/img/map/star.png'; |
| | | import { DEVICE_TYPE, MAP_MIRROR, MAP_TONES } from './constants'; |
| | | import { DEVICE_TYPE, MAP_ANGLE_OFFSET_VAL, MAP_MIRROR, MAP_TONES } from './constants'; |
| | | |
| | | const getMapTone = (mode) => mode === 'dark' ? MAP_TONES.dark : MAP_TONES.light; |
| | | const normalizeDegrees = (degrees) => ((degrees % 360) + 360) % 360; |
| | | const FULL_ROTATION = Math.PI * 2; |
| | | const QUARTER_ROTATION = Math.PI / 2; |
| | | const normalizeRadians = (radians) => ((radians % FULL_ROTATION) + FULL_ROTATION) % FULL_ROTATION; |
| | | const snapToQuarterRotation = (radians) => Math.round(radians / QUARTER_ROTATION) * QUARTER_ROTATION; |
| | | |
| | | export default class Player { |
| | | |
| | |
| | | this.mapContainer.position.set(this.app.renderer.width / 2, this.app.renderer.height / 2); |
| | | |
| | | const rotationOffset = Number(value) || 0; |
| | | const newRotation = this.mapContainer.rotation + rotationOffset; |
| | | const baseRotation = Number.isFinite(this.mapRotationTarget) |
| | | ? this.mapRotationTarget |
| | | : snapToQuarterRotation(this.mapContainer.rotation); |
| | | const newRotation = snapToQuarterRotation(baseRotation + rotationOffset); |
| | | |
| | | new TWEEDLE.Tween(this.mapContainer) |
| | | if (this.mapRotationTween) { |
| | | this.mapRotationTween.stop(); |
| | | } |
| | | this.mapRotationTarget = newRotation; |
| | | |
| | | this.mapRotationTween = new TWEEDLE.Tween(this.mapContainer) |
| | | .to({ rotation: newRotation }, 200) |
| | | .easing(TWEEDLE.Easing.Quadratic.Out) |
| | | .onUpdate(() => { |
| | | this.updateRotationHud(this.mapContainer.rotation); |
| | | }) |
| | | .onComplete(() => { |
| | | this.mapContainer.rotation = newRotation; |
| | | this.mapRotationTarget = newRotation; |
| | | this.mapRotationTween = null; |
| | | this.updateRotationHud(this.mapContainer.rotation); |
| | | localStorage.setItem('mapRotation', newRotation % (Math.PI * 2)); |
| | | localStorage.setItem('mapRotation', normalizeRadians(newRotation)); |
| | | }) |
| | | .start(); |
| | | } |
| | |
| | | getDirectionHudTone = () => { |
| | | if (this.themeMode === 'dark') { |
| | | return { |
| | | panelFill: 0x11161f, |
| | | panelAlpha: 0.78, |
| | | panelStroke: 0x4c5a6d, |
| | | ring: 0x7f8ea3, |
| | | axis: 0x5d6980, |
| | | accent: 0x49a6ff, |
| | |
| | | }; |
| | | } |
| | | return { |
| | | panelFill: 0xffffff, |
| | | panelAlpha: 0.86, |
| | | panelStroke: 0xcfd6df, |
| | | ring: 0xaab4c2, |
| | | axis: 0xd6dde7, |
| | | accent: 0x2f68ac, |
| | |
| | | const hudTone = this.getDirectionHudTone(); |
| | | this.directionHud = new PIXI.Container(); |
| | | this.directionHud.name = 'directionHud'; |
| | | this.directionHud.position.set(16, 68); |
| | | this.directionHud.position.set(10, 62); |
| | | this.app.stage.addChild(this.directionHud); |
| | | |
| | | this.directionHudPanel = new PIXI.Graphics(); |
| | | this.directionHud.addChild(this.directionHudPanel); |
| | | |
| | | this.directionHudDial = new PIXI.Container(); |
| | | this.directionHudDial.position.set(52, 42); |
| | | this.directionHudDial.position.set(48, 50); |
| | | this.directionHud.addChild(this.directionHudDial); |
| | | |
| | | this.directionHudDialGraphics = new PIXI.Graphics(); |
| | |
| | | [0, 90, 180, 270].forEach((degree) => { |
| | | const label = new PIXI.Text(`${degree}°`, { |
| | | fill: tone.textPrimary, |
| | | fontSize: 13, |
| | | fontSize: degree === 0 ? 11 : 10, |
| | | fontFamily: 'Microsoft YaHei', |
| | | fontWeight: 'bold', |
| | | stroke: hudTone.textStroke, |
| | | strokeThickness: 3, |
| | | }); |
| | | label.anchor.set(0.5); |
| | | this.directionHudDial.addChild(label); |
| | |
| | | } |
| | | |
| | | const radius = 28; |
| | | const centerLabelRadius = 48; |
| | | const labelRadius = 37; |
| | | |
| | | [0, 90, 180, 270].forEach((degree) => { |
| | | const screenAngle = normalizeDegrees(degree + rotationDegrees); |
| | | const screenAngle = normalizeDegrees(90 - degree + MAP_ANGLE_OFFSET_VAL + rotationDegrees); |
| | | const angle = screenAngle * Math.PI / 180; |
| | | const x = Math.cos(angle) * centerLabelRadius; |
| | | const y = Math.sin(angle) * centerLabelRadius; |
| | | const x = Math.cos(angle) * labelRadius; |
| | | const y = Math.sin(angle) * labelRadius; |
| | | const label = this.directionHudLabels[degree]; |
| | | if (label) { |
| | | label.position.set(x, y); |
| | |
| | | if (this.directionHudDialGraphics) { |
| | | const hudTone = this.getDirectionHudTone(); |
| | | this.directionHudDialGraphics.clear(); |
| | | |
| | | this.directionHudDialGraphics.lineStyle(1.5, hudTone.axis, 0.85); |
| | | this.directionHudDialGraphics.moveTo(-radius, 0); |
| | | this.directionHudDialGraphics.lineTo(radius, 0); |
| | |
| | | this.directionHudDialGraphics.lineTo(0, radius); |
| | | this.directionHudDialGraphics.lineStyle(2, hudTone.ring, 0.95); |
| | | this.directionHudDialGraphics.drawCircle(0, 0, radius); |
| | | |
| | | this.directionHudDialGraphics.beginFill(hudTone.accent, 1); |
| | | this.directionHudDialGraphics.moveTo(0, -radius - 10); |
| | | this.directionHudDialGraphics.lineTo(-6, -radius + 2); |
| | |
| | | const tone = getMapTone(this.themeMode); |
| | | const hudTone = this.getDirectionHudTone(); |
| | | |
| | | if (this.directionHudPanel) { |
| | | this.directionHudPanel.clear(); |
| | | this.directionHudPanel.lineStyle(1, hudTone.panelStroke, 0.95); |
| | | this.directionHudPanel.beginFill(hudTone.panelFill, hudTone.panelAlpha); |
| | | this.directionHudPanel.drawRoundedRect(0, 0, 96, 96, 16); |
| | | this.directionHudPanel.endFill(); |
| | | } |
| | | if (this.directionHudLabels) { |
| | | Object.values(this.directionHudLabels).forEach((label) => { |
| | | label.style.fill = tone.textPrimary; |
| | | label.style.stroke = hudTone.textStroke; |
| | | }); |
| | | } |
| | | } |