Pre-release
AdventureJS Docs Downloads Bundler Devlog Score: 0 Moves: 0
// doVerbAction.js
(function () {
  /* global AdventureJS A */
  var p = AdventureJS.Assets.Asset.prototype;
  /**
   * Check if this asset has a general verb action or reaction
   * for the specified event, or if the asset has an action for
   * the specific asset. If either form is found, pass it to
   * getStringArrayFunction and return the results. If the
   * result returns false or null it will terminate the calling
   * verb's default behavior.
   * <br><br>
   * We look for actions in two forms:<br>
   * <li>Function properties such as <code>asset.doThisToThat()</code>
   * are called. If a function returns null or false, that effectively
   * ends the turn.</li>
   * <li>String properties such as <code>asset.do_this_to_that</code>
   * override the turn's default output.</li>
   * @memberOf AdventureJS.Assets.Asset
   * @method AdventureJS.Assets.Asset#doVerbAction
   * @param {string} action The name of this action.
   * @param {string} target Target is relative. Depending on whether
   * "this" is a direct or indirect object, the target may be an
   * indirect or direct object. The target is the other object in the
   * sentence, regardless of where in the sentence it lies. We pass
   * asset.name here instead of asset.id – though ids are also
   * accepted – because this is an author facing system and we've
   * trained authors to work with names rather than IDs. Hooks will
   * only be defined by authors, so we let them use asset.name as
   * their identifier. We do however make an effort to see
   * if an id has been passed instead of a name, because Ivan.
   * @param {string} target2 An optional second target. In a sentence
   * with three nouns, this would be the last noun. For example,
   * in "throw rock at troll with slingshot", the slingshot would
   * be target2.
   * @param {object} params Arbitrary parameter object.
   * @return {Boolean}
   */
  p.doVerbAction = function Asset_doVerbAction({
    action,
    params = {},
    type = "Hook",
    target,
    target2,
  } = {}) {
    // if we didn't receive an action, exit
    if (!action) return;

    let target_key = "";
    let target2_key = "";

    // in addition to checking for camel cased method properties,
    // we also look for alternative snake cased string properties
    // const _action_ = A.FX.camelToSnake(action);

    // if this hasn't got function or string, exit
    // if (!this[action] && !this[_action_]) return;
    if ("undefined" === typeof this[action] || null === this[action]) return;

    const input = this.game.getInput();

    if (target) {
      // if an asset was passed, disregard assets retrieved from input
      target = target.class ? target : this.game.getAsset(target);
      if (target2) {
        target2 = target2.class ? target2 : this.game.getAsset(target2);
      }
    } else {
      target = input.getAsset(2);
      target2 = input.getAsset(3);
      if (!target) target = this.game.getPlayer();
    }

    let target_is_player = target && target.id === this.game.world._player;

    //

    if (target2) {
      this.game.log(
        "L1459",
        "log",
        "high",
        `[doVerbAction.js] TRY ${type}: ${this.id}.${action}(${target.id ? target.id : target}, ${target2.id ? target2.id : target2})`,
        type
      );

      this.game.debug(
        `D1442`,
        `${type}`,
        `<span class="ajs-tryfound">HOOK</span> <span class="ajs-actor">${this.id}</span>.<span class="ajs-action">${action}(</span><span class="ajs-target">"${target.name ? target.name : target}", "${target2.name ? target2.name : target2}"</span><span class="ajs-action">)</span>`
      );
    } else if (target) {
      this.game.log(
        "L1456",
        "log",
        "high",
        `[doVerbAction.js] TRY ${type}: ${this.id}.${action}(${target.id ? target.id : target})`,
        type
      );

      this.game.debug(
        `D1420`,
        `${type}`,
        `<span class="ajs-tryfound">HOOK</span> <span class="ajs-actor">${this.id}</span>.<span class="ajs-action">${action}(</span><span class="ajs-target">"${target.name ? target.name : target}"</span><span class="ajs-action">)</span>`
      );
    } else {
      this.game.log(
        "L1476",
        "log",
        "high",
        `[doVerbAction.js] TRY ${type}: ${this.id}.${action}()`,
        type
      );

      this.game.debug(
        `D1415`,
        `${type}`,
        `<span class="ajs-tryfound">HOOK</span> <span class="ajs-actor">${this.id}</span>.<span class="ajs-action">${action}(</span><span class="ajs-target">${""}</span><span class="ajs-action">)</span>`
      );
    }
    //

    let _a = action;
    let _o = "(";
    let _c = ")";

    this.game.log(
      "L1394",
      "log",
      "high",
      `[doVerbAction.js] ↳ FOUND ${type}: ${this.id}.${_a}${_o}${target ? target.id : ""}${target && target2 ? ", " : ""}${target2 ? target2.id : ""}${_c}`,
      `${type}`
    );

    this.game.debug(
      `D1383`,
      `${type}`,
      `<span class="ajs-tryfound">↳ FOUND</span> <span class="ajs-actor">${this.id}</span>.<span class="ajs-action">${_a}${_o}</span><span class="ajs-target">${target ? '"' + target.name + '"' : ""}${target && target2 ? ", " : ""}${target2 ? '"' + target2.name + '"' : ""}</span><span class="ajs-action">${_c}</span>`
    );

    // is there a method at this[action]?
    if (this[action]) {
      const value = this[action];
      if (typeof value === "function") {
        return value.call(this, {
          action,
          target,
          target2,
          params,
          type,
          self: this,
        });
      } else if (A.FX.isSAF(value)) {
        return A.FX.getSAF.call(this.game, value);
      }
    }

    // --------------------------------------------------
    // is there a method at this[action][target]?
    // --------------------------------------------------
    if (target) {
      // look for key with name or id
      target_key = [target.name, target.id].find((key) => {
        const value = this[action]?.[key];
        // look for string array function under name or id
        return A.FX.isSAF(value);
      });
    }
    if (target_key) {
      const value = this[action][target_key];
      if (typeof value === "function") {
        return value.call(this, {
          action,
          target,
          target2,
          params,
          type,
          self: this,
        });
      } else {
        return A.FX.getSAF.call(this.game, value);
      }
    }

    // --------------------------------------------------
    // @TODO testing out using "player" as an identifier
    // have not carried this through remaining blocks
    // --------------------------------------------------
    if (target_is_player && this[action]?.player) {
      const value = this[action].player;
      if (typeof value === "function") {
        return value.call(this, {
          action,
          target,
          target2,
          params,
          type,
          self: this,
        });
      } else {
        return A.FX.getSAF.call(this.game, value);
      }
    }

    // --------------------------------------------------
    // is there a method at this[action][target][target2]?
    // --------------------------------------------------
    if (target && target2) {
      target_key = [target.name, target.id].find(
        (key) => "undefined" !== typeof this[action]?.[key]
      );
      if (target_key) {
        target2_key = [target2.name, target2.id].find((key) => {
          const value = this[action][target_key]?.[key];
          // look for string array function under name or id
          return A.FX.isSAF(value);
        });
      }
    }
    if (target_key && target2_key) {
      const value = this[action][target.name][target2.name];
      if (typeof value === "function") {
        return value.call(this, {
          action,
          target,
          target2,
          params,
          type,
          self: this,
        });
      } else {
        return A.FX.getSAF.call(this.game, value);
      }
    }

    return;
  };
})();