soundDirector.ts 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. import { type GameEffect } from '../core/gameResult'
  2. type SoundKey = 'session-start' | 'start-complete' | 'control-complete' | 'finish-complete' | 'warning'
  3. const SOUND_SRC: Record<SoundKey, string> = {
  4. 'session-start': '/assets/sounds/session-start.wav',
  5. 'start-complete': '/assets/sounds/start-complete.wav',
  6. 'control-complete': '/assets/sounds/control-complete.wav',
  7. 'finish-complete': '/assets/sounds/finish-complete.wav',
  8. warning: '/assets/sounds/warning.wav',
  9. }
  10. export class SoundDirector {
  11. enabled: boolean
  12. contexts: Partial<Record<SoundKey, WechatMiniprogram.InnerAudioContext>>
  13. constructor() {
  14. this.enabled = true
  15. this.contexts = {}
  16. }
  17. setEnabled(enabled: boolean): void {
  18. this.enabled = enabled
  19. }
  20. destroy(): void {
  21. const keys = Object.keys(this.contexts) as SoundKey[]
  22. for (const key of keys) {
  23. const context = this.contexts[key]
  24. if (!context) {
  25. continue
  26. }
  27. context.stop()
  28. context.destroy()
  29. }
  30. this.contexts = {}
  31. }
  32. handleEffects(effects: GameEffect[]): void {
  33. if (!this.enabled || !effects.length) {
  34. return
  35. }
  36. const hasFinishCompletion = effects.some((effect) => effect.type === 'control_completed' && effect.controlKind === 'finish')
  37. for (const effect of effects) {
  38. if (effect.type === 'session_started') {
  39. this.play('session-start')
  40. continue
  41. }
  42. if (effect.type === 'punch_feedback' && effect.tone === 'warning') {
  43. this.play('warning')
  44. continue
  45. }
  46. if (effect.type === 'control_completed') {
  47. if (effect.controlKind === 'start') {
  48. this.play('start-complete')
  49. continue
  50. }
  51. if (effect.controlKind === 'finish') {
  52. this.play('finish-complete')
  53. continue
  54. }
  55. this.play('control-complete')
  56. continue
  57. }
  58. if (effect.type === 'session_finished' && !hasFinishCompletion) {
  59. this.play('finish-complete')
  60. }
  61. }
  62. }
  63. play(key: SoundKey): void {
  64. const context = this.getContext(key)
  65. context.stop()
  66. context.seek(0)
  67. context.play()
  68. }
  69. getContext(key: SoundKey): WechatMiniprogram.InnerAudioContext {
  70. const existing = this.contexts[key]
  71. if (existing) {
  72. return existing
  73. }
  74. const context = wx.createInnerAudioContext()
  75. context.src = SOUND_SRC[key]
  76. context.autoplay = false
  77. context.loop = false
  78. context.obeyMuteSwitch = true
  79. context.volume = 1
  80. this.contexts[key] = context
  81. return context
  82. }
  83. }