/// /// /// class Action { public static check = new Rulebook("Check any Action"); public static carry = new Rulebook("Carry out any Action"); public extraChecks : Array> = []; public extraCarries : Array> = []; public _actor : Thing; public nouns : Array = []; public say : Say = new Say(); public actingAgressively = false; public actingSubmissively = false; public requiresTurn = true; public requiresNoun = true; public requiresVisibility = true; // First noun must be visible and in the same room public constructor (actor : Thing, ...nouns : Array) { this.actor = actor; nouns.forEach((value, index, array) => { this.setNoun(index, value); }); } public async execute () : Promise { this.say = new Say(); let checkRulebooks = []; let carryRulebooks = []; let cClass = this.constructor; while (cClass != Action) { if (( cClass).check != undefined) { checkRulebooks.push(( cClass).check); } if (( cClass).carry != undefined) { carryRulebooks.push(( cClass).carry); } cClass = Object.getPrototypeOf(cClass); } /** * Check if action goes through */ let result = await Action.check.execute({ noun : this }, ...checkRulebooks); // There are now multiple results! A false result means a fail Check! But it can also return a new action! if (result == false) { return; } else if(result instanceof Action) { console.debug(Rulebook.getIndentation() + "[ACTION] Instead of..."); let originalNouns = this.nouns; await result.execute(); this.say.add(result.say); this.nouns = result.nouns; // Reset to initial state this.nouns = originalNouns; return; } /** * Carry Out */ await Action.carry.execute({ noun : this }, ...carryRulebooks); return this.say; } get actor(): Thing { return this._actor; } set actor(value: Thing) { this._actor = value; } public getNoun (n : number) : any { if (this.nouns.length > n) { return this.nouns[n]; } return undefined; } public setNoun (n : number, noun : any) { while (this.nouns.length < n) { this.nouns.push(undefined); } this.nouns[n] = noun; } /** * Needs to return a string explaining what the player will do if he does this action. * For instance, ActionTaking should return something like return "take " + this.nouns[0].getName(), * which would read as "take thing". * remember that things implement PRINTABLE interface, so you can get their names. * @returns {string} */ public getCommandText () { return "do"; } /** * If an action is stopped, it means it failed so spectacularly that it didn't even begin. * Which means if the player is doing it, it'll not take a turn. */ public stop () { this.requiresTurn = false; } } // Action.addCarryRule(new Rule({ // name : "Testing say in actions rule", // priority : Rule.PRIORITY_LOWEST, // firstPriority : Rule.PRIORITY_LOWEST, // code : (rule, rulebook) => { // let action = rulebook.noun; // action.say.add("You do nothing all turn. What was the point, really?"); // } // })) Action.check.addRule( new Rule({ name : "Check any Action - Requires Noun", firstPriority : Rule.PRIORITY_HIGHEST, code : (rulebook : RulebookRunner) => { let action = rulebook.noun; if (action.getNoun(0) == undefined) { return false; } }, conditions : runner => { return runner.noun.requiresNoun; } }) ); Action.check.addRule( new Rule({ name : "Check any Action - Requires Visibility", code : (rulebook : RulebookRunner) => { let action = rulebook.noun; let actor = action.actor; if (!action.getNoun(0).isVisibleTo(actor)) { return false; } }, conditions : runner => { return runner.noun.requiresVisibility; } }) );