Quantum-Game / quantum-game-2

LEGACY open-source version of Quantum Game 2 (Sept 2019 - Oct 2020)
https://quantumgame.io
MIT License
65 stars 16 forks source link

Controls refactor #139

Closed JStrebeyko closed 4 years ago

JStrebeyko commented 4 years ago
JStrebeyko commented 4 years ago

In case the access problem continues, please consider updating the @/components/GameControls.vue with the following. The changes make the style section BEMed and nested, the icon source and opacity mechanism less bloated and the template smooth. And cuts out like 40 lines, too.

<template>
  <div class="controls">
    <!-- SIMULATION CONTROLS -->
    <span class="controls__playback-buttons">
      <button :style="getIconStyle('rewind')" @click="$emit('rewind')" />
      <button :style="getIconStyle('skip_back')" @click="$emit('step-back')" />
      <button id="play" :style="getIconStyle('play')" @click="$emit('play')" />
      <button :style="getIconStyle('skip_forward')" @click="$emit('step-forward')" />
      <button :style="getIconStyle('fast_forward')" @click="$emit('fast-forward')" />
    </span>
    <!-- FRAME INFO -->
    <span class="controls__frame-info">
      <b>STEP {{ nextFrame }} / {{ totalFrames }}</b>
      <span>({{ gameState }})</span>
    </span>
    <!-- LEVEL CONTROLS -->
    <span class="controls__options-buttons">
      <button :style="getIconStyle('sound')" @click="toggleSound" />
      <button :style="getIconStyle('download')" @click="$emit('downloadLevel')" />
      <button :style="getIconStyle('save')" @click="handleSave" />
      <button :style="getIconStyle('map')" @click="handleMap" />
      <button :style="getIconStyle('account')" @click="handleAccount" />
    </span>
  </div>
</template>

<script lang="ts">
import { Vue, Component, Prop } from 'vue-property-decorator'
import { State } from 'vuex-class'
import { GameStateEnum } from '@/engine/interfaces'
import $userStore from '@/store/userStore'

@Component
export default class GameControls extends Vue {
  // FIXME: Can somehow accelerate photon speed by spamming play
  @Prop() readonly frameIndex!: number
  @Prop() readonly totalFrames!: number
  @State('gameState') gameState!: GameStateEnum
  @State('simulationState') simulationState!: boolean
  isSoundOn = true

  saveLevel(): void {
    $userStore.dispatch('SAVE_LEVEL', this.$store.state)
  }

  updateLevel(): void {
    $userStore.dispatch('UPDATE_LEVEL', this.$store.state)
  }

  handleSave(): void {
    if (!this.$route.meta.levelSaved) {
      this.saveLevel()
    } else {
      this.updateLevel()
    }
  }

  handleAccount(): void {
    if (!this.isLoggedIn) {
      this.$router.push('/login')
    } else {
      this.$router.push('/myaccount')
    }
  }

  handleOptions(): void {
    this.$router.push('/options')
  }

  handleMap(): void {
    this.$router.push('/levels')
  }

  toggleSound(): void {
    this.isSoundOn = !this.isSoundOn
  }

  getIconStyle(icon: string): {} {
    let iconName
    /** In cases the iconName does not represent the icon
     *  (for example the button has two possible icons)
     *  we want to overwrite the name depending on
     *  different circumstances.
     */
    if (icon === 'account' && !this.isLoggedIn) {
      iconName = 'account_register'
    } else if (icon === 'sound') {
      if (this.isSoundOn) {
        iconName = 'sound_off'
      } else {
        iconName = 'sound_on'
      }
    } else if (icon === 'play' && this.simulationState) {
      iconName = 'pause'
    } else {
      /** otherwise, the iconName should be taken as is:
       */
      iconName = icon
    }

    let opacity
    /**
     *  Similarly, the opacity assigining mechanics is generally the same,
     *  although the rules of "dimming down" differ slightly in some cases.
     *  We want to override these rules for some of the buttons.
     */
    if (icon === 'save') {
      opacity = this.isLoggedIn && !this.simulationState ? 1 : 0.3
      // play should always is litten up:
    } else if (icon === 'play') {
      opacity = 1
      // these should be additionaly dimmed down in case there are no steps ahead:
    } else if (icon === 'skip_forward' || icon === 'fast_forward') {
      opacity = !this.simulationState && this.stepForwardCanBeMade ? 1 : 0.3
      // these, if there are no steps behind:
    } else if (icon === 'skip_back' || icon === 'rewind') {
      opacity = !this.simulationState && this.stepBackCanBeMade ? 1 : 0.3
    } else {
      opacity = !this.simulationState ? 1 : 0.3
    }
    return {
      backgroundImage: `url(${require(`@/assets/graphics/icons/${iconName}.svg`)})`, //eslint-disable-line
      opacity
    }
  }

  get nextFrame(): number {
    return this.frameIndex + 1
  }

  get stepForwardCanBeMade(): boolean {
    return this.nextFrame < this.totalFrames
  }

  get stepBackCanBeMade(): boolean {
    return this.frameIndex > 0
  }

  get isLoggedIn(): boolean {
    return $userStore.getters.isLoggedIn
  }
}
</script>

<style lang="scss" scoped>
.controls {
  width: 100%;
  border-bottom: 1px solid white;
  display: flex;
  padding: 1rem 0;
  justify-content: space-between;
  align-items: center;
  @media screen and (max-width: 600px) {
    padding: 0.6rem 0;
  }
  & button {
    height: 24px;
    width: 24px;
    margin: 0.2rem 0.4rem;
    background-color: transparent;
    border: none;
    transition: all 0.2s ease-in-out;
    &:hover {
      transform: scale(1.2);
    }
    &#play {
      height: 30px;
      width: 30px;
      margin: 0 0.2rem;
      @media screen and (max-width: 1000px) {
        width: 4.5vw;
        height: 4.5vw;
        margin: 0.2rem 0.4rem;
      }
    }
  }
  & .controls__frame-info {
    display: flex;
    & span {
      font-size: 0.75rem;
      padding-left: 10px;
    }
    @media screen and (max-width: 1000px) {
      flex-direction: column;
      margin: 0 auto;
      & span {
        padding-left: 0;
      }
    }
    @media screen and (max-width: 600px) {
      padding-bottom: 0;
      & b {
        font-size: 0.8rem;
      }
      & span {
        font-size: 0.6rem;
      }
    }
  }
  & .controls__playback-buttons {
    display: flex;
    @media screen and (max-width: 1000px) {
      button {
        background-repeat: no-repeat;
        background-size: contain;
        display: flex;
        width: 4.5vw;
        height: 4.5vw;
      }
    }
  }
  & .controls__options-buttons {
    display: flex;
    align-items: center;
    line-height: 20px;
    @media screen and (max-width: 1000px) {
      display: none;
    }
  }
}
</style>
stared commented 4 years ago

Closed as old, and most likely not up to do date with recent code changes.