IntegerMan/angularIF

View on GitHub
src/app/engine/verbs/hang-handler.ts

Summary

Maintainability
D
2 days
Test Coverage
import {VerbHandler} from './verb-handler';
import {VerbType} from './verb-type.enum';
import {Command} from '../parser/command';
import {CommandContext} from '../command-context';
import {CommandResult} from '../command-result';
import {CommandToken} from '../parser/command-token';
import {TokenClassification} from '../parser/token-classification.enum';
import {WorldEntity} from '../entities/world-entity';

export class HangHandler extends VerbHandler {

  get verbType(): VerbType {
    return VerbType.manipulate;
  }

  handleCommand(command: Command, context: CommandContext): CommandResult {

    // Figure out what we're talking about.
    const subject: WorldEntity = this.assertHasObjectWithEntity(command, context);
    if (!subject) {
      return CommandResult.BuildParseFailedResult();
    }

    // If it's alive, shame the player.
    if (!this.assertEntityIsNotAlive(subject, context)) {
      return CommandResult.BuildActionFailedResult();
    }

    // Ensure we HAVE it first
    if (!this.assertHasEntity(context, subject)) {
      return CommandResult.BuildParseFailedResult();
    }

    // Next, let's see if we can determine what we're trying to hang the item on.
    const prep: CommandToken = this.getSuspender(command, context, subject);
    if (!prep) {
      return CommandResult.BuildParseFailedResult();
    }

    return HangHandler.attemptHang(context, subject, prep);
  }

  static attemptHang(context: CommandContext, subject: WorldEntity, prep: CommandToken): CommandResult {

    const suspender = prep.modifies;

    if (!suspender || suspender.classification === TokenClassification.Verb) {
      context.outputService.displayParserError(`I don't understand what you want to hang ${subject.that} ${prep.name}.`,
        `Try saying 'Hang ${subject.that} on [object name]'.`);
      return CommandResult.BuildParseFailedResult();
    }

    // Ensure the hook  / hang target is present
    const hook: WorldEntity = suspender.entity;
    if (!hook) {
      context.outputService.displayStory(`You can't hang anything ${prep.name} ${suspender.entity.that}.`);
      return CommandResult.BuildActionFailedResult();
    }

    if (hook.allowItemHanged(context, subject)) {

      this.hangEntityFromSuspender(context, subject, hook);
      return CommandResult.BuildActionSuccessResult();

    } else {

      return CommandResult.BuildActionFailedResult();

    }

  }

  private static hangEntityFromSuspender(context: CommandContext, entityToHang: WorldEntity, suspender: WorldEntity): void {

    // TODO: Probably should have a direct reference to the container
    context.player.removeFromInventory(entityToHang);
    suspender.addObject(entityToHang);
    suspender.onItemHanged(context, entityToHang);
    entityToHang.onHung(context, suspender);

  }

  private getSuspender(command: Command, context: CommandContext, entityToHang: WorldEntity): CommandToken {

    // TODO: With/using may need to be special as in "Hang target from branch with rope"
    const hangerPrep: CommandToken = command.getPrepositionWithFallbacks(['on', 'using', 'with', 'under', 'below']);

    // We couldn't figure out what we were trying to hang the object on
    if (!hangerPrep) {

      // Check to see if the user was trying a hang ... in X
      if (command.getProposition('in')) {
        context.outputService.displayStory(`You can't hang something inside of something else!`);
      } else {
        context.outputService.displayParserError(`I don't understand what you're trying to hang ${entityToHang.that} on.`,
          `Try saying 'Hang ${entityToHang.that} on [object name]'.`);
      }

      return null;
    }
    return hangerPrep;
  }

  private assertEntityIsNotAlive(entityToHang: WorldEntity, context: CommandContext): boolean {

    if (!entityToHang.isAlive) {
      return true;
    }

    if (entityToHang === context.player) {
      context.outputService.displayStory(`That's awfully morbid. Killing yourself never solved anything.`);
    } else {
      context.outputService.displayStory(`You think about hanging ${entityToHang.that} but then remember that you're not a sociopath.`);
    }

    return false;
  }

  private assertHasObjectWithEntity(command: Command, context: CommandContext): WorldEntity {

    if (command.objects.length < 1) {

      context.outputService.displayParserError('I don\'t understand what you\'re trying to hang.',
        'Try saying \'Hang [object name] on [target name]\'.');

      return null;
    }

    const itemToHang: CommandToken = command.objects[0];
    const entityToHang: WorldEntity = itemToHang.entity;

    // Verify that we're talking about is present
    if (!entityToHang) {
      context.outputService.displayParserError(`You don't see ${itemToHang.userInput} here.`);
      return null;
    }

    return entityToHang;
  }
}