uccser/cs-field-guide

View on GitHub
csfieldguide/static/interactives/packet-attack/js/information.js

Summary

Maintainability
B
5 hrs
Test Coverage
/**
 * Packet Attack
 * 
 * Game Information class
 */

require('phaser');

var CONFIG = require('./config.js');
var BUTTON = require('./phaserButton.js');

// Internationalisation text //

var TXT_START = gettext("START");

var TXT_TITLE = CONFIG.TITLE;
var TXT_INFORMATION = gettext("Information");

var TXT_FAIL = gettext("You let the message get through!") + '\n' + gettext("Try again.");

var TXT_PASS = gettext("Awesome!") + '\n' + gettext("You stopped the message being delivered.");
var TXT_REPEAT_CUSTOM = gettext("Click 'start' to repeat this custom level.")
var TXT_NEXT_LEVEL = gettext("Click 'start' to move to the next level.");

var TXT_WELCOME_TO_CUSTOM = gettext("Welcome to this custom level!");
var TXT_WELCOME_TO_LEVEL = gettext("Welcome to level");
var TXT_IN_THIS_LEVEL = gettext("In this level:");
var TXT_MULTIPACKET = gettext("The left pipe is using many creatures to communicate");
var TXT_SHIELDED = gettext("The creatures have shields to protect from corruption");
var TXT_NUMBERED = gettext("The creatures have numbers to protect packet order");
var TXT_ACKSNACKS = gettext("The right pipe sends creatures back with results, green is good, red is bad");
var TXT_ATTACK_ACKSNACKS = gettext("But the responses are also vulnerable to your attack!");
var TXT_TIMEOUTS = gettext("If the left pipe doesn't get a response, it will resend a creature");
var TXT_NO_DEFENCE = gettext("There are no defenses!");

/**
 * To allow custom fonts to be loaded, the Introduction screen has been moved
 * outside the game itself.
 * 
 * The underlying code remains in case it is needed again in future.
 * Just uncomment the part below and set the Information Scene to launch at
 * InfoPaneType.START
 */
var TXT_INTRODUCTION = "";
// var TXT_INTRODUCTION = gettext("Welcome to Packet Attack.")
//               + '\n' + gettext("In this game, your job is to attack the packet creatures and stop messages being delivered.")
//               + '\n' + gettext("You pass a level if the received message is any different from the one sent.")
//               + '\n' + gettext("While a creature is in the danger zone (indicated by the yellow and grey area) you may attack by clicking the command buttons.")
//               + '\n' + gettext("Good luck!");

var TXT_IMPOSSIBLE = gettext("This level is actually impossible to beat")
            + '\n' + gettext("The system can handle anything you throw at it!")
            + '\n' + gettext("Thank you for playing!")
            + '\n\n\n\n'
            + '\n' + gettext("Click 'start' if you want to try this level again")
            + '\n' + gettext("Or refresh the page if you want to start from the beginning");

// End //

var InfoPaneType = {
  FAIL: 0,
  PROCEED: 1,
  BEFORE_LEVEL: 2,
  START: 3,
  END: 4
}

/**
 * Game Information.
 * This is the scene that displays at the very beginning of the game,
 * and before every level with a description of what's to come
 */
class Information extends Phaser.Scene {

  constructor(config) {
    super({ key: 'Information' });

    this.paneType = config.paneType;
    this.urlMode = false;
    this.level = null;
  }

  /**
   * Loads all required base images
   */
  preload() {
    console.log('Loading base Info images');
    this.load.image('blurred-bg', base + 'interactives/packet-attack/assets/blurredBackground.png');
    this.load.image('startButton', base + 'interactives/packet-attack/assets/startButton.png');
    this.load.image('replayButton', base + 'interactives/packet-attack/assets/replayButton.png');
  }

  /**
   * Initialises relevant registry values
   */
  init() {
    this.registry.set('level', null);
  }

  /**
   * Loads the Information Scene on screen
   */
  create() {
    console.log('creating Info screen');
    this.gameScene = this.scene.get('GameScene');
    this.registry.set('level', this.level.levelNumber);

    var buttonConfig = {
      scene: this,
      x: 400,
      y: 500,
      imageKey: 'startButton',
      buttonSize: [203, 122],
      text: TXT_START,
      textConfig: {
          font: '40px Open Sans',
          fill: '#007f0d',
          align: 'center',
        }
      }

    this.add.image(400, 300, 'blurred-bg');
    this.button = new BUTTON.PhaserButton(buttonConfig);
    this.replayButton = this.add.image(100, 500, 'replayButton').setAlpha(0);
    this.button.setInteractive({ useHandCursor: true });
    this.createText();

    this.button.on('pointerdown', this.startLevel);
    this.replayButton.on('pointerdown', this.replayLevel);
  }

  /**
   * Initialises the text to be displayed in the Scene, including title, subtitle
   * and description text
   */
  createText() {
    var config = {
      font: '40px Open Sans',
      fill: '#000000',
      wordWrap: { width: 790 },
      align: 'center',
    }

    var titleText = this.add.text(400, 0, TXT_TITLE, config);
    titleText.setOrigin(0.5, 0);

    config.font = '28px Open Sans';


    var subTitleText = this.add.text(400, 50, TXT_INFORMATION, config);
    subTitleText.setOrigin(0.5, 0);

    this.information = this.add.text(400, 100, this.generateFeedback(), config);
    this.information.setOrigin(0.5, 0);
  }

  /**
   * Prepares the canvas for the next game, as appropriate
   */
  startLevel() {
    this.scene.hideReplayButton();
    if (this.scene.paneType == InfoPaneType.FAIL && this.scene.level.levelNumber >= CONFIG.FINAL_LEVEL) {
      this.scene.setPaneType(InfoPaneType.END);
    } else if (this.scene.paneType == InfoPaneType.START) {
      this.scene.setLevel(this.scene.level.levelNumber);
      this.scene.setPaneType(InfoPaneType.BEFORE_LEVEL);
    } else if (this.scene.paneType == InfoPaneType.PROCEED) {
      if (!this.scene.urlMode) {
        var nextLevel = this.scene.level.levelNumber + 1;
        if (nextLevel > CONFIG.FINAL_LEVEL) {
          nextLevel = 1;
        }
        this.scene.setLevel(nextLevel);
      }
      this.scene.setPaneType(InfoPaneType.BEFORE_LEVEL);
    } else {
      this.scene.button.disableInteractive();
      this.scene.sendBackward();
      this.scene.gameScene.play();
    }
  }

  /**
   * Prepares the canvas to replay the same level
   */
  replayLevel() {
    this.scene.hideReplayButton();
    this.scene.button.disableInteractive();
    this.scene.sendBackward();
    this.scene.gameScene.play();
  }

  /**
   * Returns the Information scene to the front and allows interaction with its button(s)
   */
  resumeInfo() {
    this.bringForward();
    if (this.paneType == InfoPaneType.PROCEED && !this.urlMode) {
      this.showReplayButton();
    }
    this.button.setInteractive({ useHandCursor: true });
  }

  /**
   * Brings the info panel to the front, so that it and not the game is rendered
   */
  bringForward() {
    this.scene.bringToTop();
  }

  /**
   * Sends the info panel to the back, so that the game and not it is rendered
   */
  sendBackward() {
    this.scene.sendToBack();
  }

  /**
   * Sets the info screen to display the given pane type
   */
  setPaneType(pane) {
    this.paneType = pane;
    this.updateText();
  }

  /**
   * Sets all scenes to run the given level
   */
  setLevel(level) {
    this.registry.set('level', level);
    this.level = CONFIG.LEVELS[level];
  }

  /**
   * Sets the level to be used in the game, called when the game is first set up
   */
  setStartLevel(level) {
    this.level = CONFIG.LEVELS[level];
  }

  /**
   * Sets the game to be running a custom URL-Parameter-defined level
   */
  setUrlMode() {
    this.urlMode = true;
  }

  /**
   * Displays the replay button, and allows it to be clicked
   */
  showReplayButton() {
    this.replayButton.setAlpha(1);
    this.replayButton.setInteractive({ useHandCursor: true })
  }

  /**
   * Makes the replay button transparent, and disallows it to be clicked
   */
  hideReplayButton() {
    this.replayButton.setAlpha(0);
    this.replayButton.disableInteractive();
  }

  /**
   * Generates feedback text to be displayed, depending on where the user is in the game
   */
  generateFeedback() {
    var text = "";
    switch(this.paneType) {
      case InfoPaneType.FAIL:
        text = TXT_FAIL;
        break;
      case InfoPaneType.PROCEED:
        if (this.urlMode) {
          text = TXT_PASS + '\n' + TXT_REPEAT_CUSTOM;
        } else {
          text = TXT_PASS + '\n' + TXT_NEXT_LEVEL;
        }
        break;
      case InfoPaneType.BEFORE_LEVEL:
        if (this.urlMode) {
          text = TXT_WELCOME_TO_CUSTOM + '\n';
        } else {
          text = TXT_WELCOME_TO_LEVEL + ' ' + this.level.levelNumber + '!\n';
        }
        text += TXT_IN_THIS_LEVEL + '\n'
            + (this.level.message.length > 1 ? TXT_MULTIPACKET + '\n' : '')
            + (this.level.packetsHaveShields ? TXT_SHIELDED + '\n' : '')
            + (this.level.packetsHaveNumbers ? TXT_NUMBERED + '\n' : '')
            + (this.level.acksNacksEnabled ? TXT_ACKSNACKS + '\n' : '')
            + (this.level.canAttackAcksNacks ? TXT_ATTACK_ACKSNACKS + '\n' : '')
            + (this.level.timeoutsEnabled ? TXT_TIMEOUTS + '\n' : '')
            + (this.level.levelNumber == 1 ? TXT_NO_DEFENCE : '');
        break;
      case InfoPaneType.START:
        text = TXT_INTRODUCTION;
        break;
      case InfoPaneType.END:
        text = TXT_IMPOSSIBLE;
        break;
    }
    return text;
  }

  /**
   * Updates the description text as required
   */
  updateText() {
    this.information.setText(this.generateFeedback());
  }
}

module.exports = {
  Information,
  InfoPaneType
};