All files / src/model field.ts

100% Statements 40/40
100% Branches 24/24
100% Functions 13/13
100% Lines 38/38

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187                                                                                                                                                                  22x     167x       167x       79x 1x     78x 1x       77x 77x 68x     77x 11x         123x 1x       122x 122x 52x     122x 72x         87x 33x         43x 18x         9x 17x         1x       78x 78x 64x 62x   2x     78x 5x 4x   1x           7x                     300x       129x      
import { Spot } from './spot'
import { Broadcaster } from './broadcaster'
 
/**
 * 放送者(プレイヤー) ({@link Broadcaster}) とスポット ({@link Spot}) が配置され、ゲームがプレイされる舞台.
 *
 * {@link FieldBuilder} を使ってインスタンスを作成してください.
 */
export interface Field {
  /**
   * マップの領域座標.
   *
   * {@link container} に値が登録されているとき値を返します.
   */
  readonly area?: Readonly<g.CommonArea>
 
  /**
   * Broadcaster, Spot を描画するエンティティ.
   *
   * 登録されている Broadcaster, Spot は本エンティティの子として描画されます
   */
  container?: g.E
 
  /**
   * マップ上に存在する Broadcaster を取得します
   */
  readonly broadcaster?: Broadcaster
 
  /**
   * マップ上に存在する Spot 一覧を取得します
   */
  readonly spots: readonly Spot[]
 
  /**
   * ライブラリ利用者が自由に使えるフィールドです.
   */
  vars?: unknown
 
  /**
   * 指定した Broadcaster をマップに配置します
   *
   * 配置すると Broadcaster ーはマップ上を移動可能になります.
   * 放送者(プレイヤー)は一人のみ配置可能です.
   *
   * @param broadcaster 配置する Broadcaster
   */
  addBroadcaster(broadcaster: Broadcaster): void
 
  /**
   * 指定した Spot をマップに配置します.
   *
   * 配置すると Broadcaster は Spot を目的地として選択できるようになります.
   *
   * @param spot 配置する Spot
   */
  addSpot(spot: Spot): void
 
  /**
   * 指定した Spot 以外を Broadcaster が目的地に選択できないようにします.
   *
   * @param spot 目的地に設定する Spot
   */
  disableSpotExcept(spot: Spot): void
 
  /**
   * 指定した Spot 以外を Broadcaster が目的地として選択できるようにします.
   */
  enableSpotExcept(spot: Spot): void
 
  /**
   * 指定した Spot を攻略済みとしてマークし、他の Spot を解放します.
   *
   * @param spot 攻略済みとする Spot
   */
  unlock(spot: Spot): void
}
 
export interface FieldOptions {
  vars: unknown
}
 
export class FieldImpl implements Field {
  vars?: unknown
  private _container?: g.E
  private readonly _spots: Set<Spot> = new Set<Spot>()
  private _broadcaster?: Broadcaster
 
  constructor ({ vars }: FieldOptions) {
    this.vars = vars
  }
 
  addBroadcaster (broadcaster: Broadcaster): void {
    if (this._broadcaster && this._broadcaster !== broadcaster) {
      throw new Error('このfieldにはすでに異なるbroadcasterが配置されているので、指定のbroadcasterを配置できません.' +
        ' broadcasterはただ一人である必要があり、fieldには複数のbroadcasterを配置できません')
    }
    if (broadcaster.field && broadcaster.field !== this) {
      throw new Error('指定のbroadcasterはすでに異なるfieldに配置されているので、このfieldに配置できません.' +
        ' broadcasterはただ一人である必要があり、broadcasterは複数のfieldに配置できません')
    }
 
    this._broadcaster = broadcaster
    if (this._container) {
      this._container.append(broadcaster.view)
    }
 
    if (!broadcaster.field) {
      broadcaster.standOn(this)
    }
  }
 
  addSpot (spot: Spot): void {
    if (spot.field && spot.field !== this) {
      throw new Error('指定のspotはすでに異なるfieldに配置されているので、このfieldに配置できません.' +
        ' spotは複数のfieldに配置できないので、fieldごとにspotを作成してください')
    }
 
    this._spots.add(spot)
    if (this._container) {
      this._container.insertBefore(spot.view, this._broadcaster?.view)
    }
 
    if (!spot.field) {
      spot.deployOn(this)
    }
  }
 
  disableSpotExcept (spot: Spot): void {
    for (const s of [...this._spots].filter(sp => sp !== spot)) {
      s.disable()
    }
  }
 
  enableSpotExcept (spot: Spot): void {
    for (const s of [...this._spots].filter(sp => sp !== spot)) {
      s.enable()
    }
  }
 
  unlock (spot: Spot): void {
    for (const s of this._spots) {
      s.unlock(spot)
    }
  }
 
  get container (): g.E | undefined {
    return this._container
  }
 
  set container (view: g.E | undefined) {
    this._container = view
    for (const s of this._spots) {
      if (this._container) {
        this._container.append(s.view)
      } else {
        s.view.remove()
      }
    }
    if (this._broadcaster) {
      if (this._container) {
        this._container.append(this._broadcaster.view)
      } else {
        this._broadcaster.view.remove()
      }
    }
  }
 
  get area (): Readonly<g.CommonArea> | undefined {
    return this._container
      ? {
          x: this._container.x,
          y: this._container.y,
          width: this._container.width,
          height: this._container.height
        }
      : undefined
  }
 
  get broadcaster (): Broadcaster | undefined {
    return this._broadcaster
  }
 
  get spots (): readonly Spot[] {
    return [...this._spots]
  }
}