1
1

Action.ts 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. /// <reference path="Rulebook.ts" />
  2. /// <reference path="Rule.ts" />
  3. /// <reference path="Things/Person.ts" />
  4. /// <reference path="../../Elements/Classes/Say.ts" />
  5. class Action {
  6. public static check = new Rulebook<Action>("Check any Action");
  7. public static carry = new Rulebook<Action>("Carry out any Action");
  8. public extraChecks : Array<Rulebook<Action>> = [];
  9. public extraCarries : Array<Rulebook<Action>> = [];
  10. public _actor : Thing;
  11. public nouns : Array<Thing> = [];
  12. public say : Say = new Say();
  13. public actingAgressively = false;
  14. public aggressivenessRating = 1;
  15. public actingSubmissively = false;
  16. public requiresTurn = true;
  17. public requiresNoun = true;
  18. public requiresVisibility = true; // First noun must be visible and in the same room
  19. public allowedStances = [PersonStance.ALLFOURS, PersonStance.STANDING];
  20. public finalSay : Say;
  21. public finalSayOnEnd = true;
  22. public constructor (actor : Thing, ...nouns : Array<any>) {
  23. this.actor = actor;
  24. nouns.forEach((value, index, array) => {
  25. this.setNoun(index, value);
  26. });
  27. }
  28. public async execute () : Promise<Say> {
  29. this.say = new Say();
  30. let checkRulebooks = [];
  31. let carryRulebooks = [];
  32. let cClass = this.constructor;
  33. while (cClass != Action) {
  34. if ((<typeof Action> cClass).check != undefined) {
  35. checkRulebooks.push((<typeof Action> cClass).check);
  36. }
  37. if ((<typeof Action> cClass).carry != undefined) {
  38. carryRulebooks.push((<typeof Action> cClass).carry);
  39. }
  40. cClass = Object.getPrototypeOf(cClass);
  41. }
  42. /**
  43. * Check if action goes through
  44. */
  45. let result = await Action.check.execute({
  46. noun : this
  47. }, ...checkRulebooks);
  48. // There are now multiple results! A false result means a fail Check! But it can also return a new action!
  49. if (result == false) {
  50. if (this.requiresTurn) {
  51. /**
  52. * Add to the list of actions performed in turn.
  53. * Notice the position of this thing: If the action failed, but an attempt was made, it still gets added to the board.
  54. * The importance of ths is, if you do something to annoy an NPC and you get stopped midway, they still get to react.
  55. */
  56. TurnSequence.addAction(this);
  57. }
  58. return;
  59. } else if(result instanceof Action) {
  60. console.debug(Rulebook.getIndentation() + "[ACTION] Instead of...");
  61. let originalNouns = this.nouns;
  62. await result.execute();
  63. this.say.add(result.say);
  64. this.nouns = result.nouns;
  65. // Reset to initial state
  66. this.nouns = originalNouns;
  67. return;
  68. }
  69. /**
  70. * Carry Out
  71. */
  72. await Action.carry.execute({
  73. noun : this
  74. }, ...carryRulebooks);
  75. /**
  76. * Add to the list of actions performed in turn.
  77. * Notice the position of this thing: if an action was replaced by another, it will instead add that action.
  78. */
  79. TurnSequence.addAction(this);
  80. if (this.finalSay != undefined) {
  81. if (this.finalSayOnEnd) {
  82. this.say.add(...this.finalSay.sequence);
  83. } else {
  84. this.say = new Say(...this.finalSay.sequence, ...this.say.sequence);
  85. }
  86. }
  87. return this.say;
  88. }
  89. get actor(): Thing {
  90. return this._actor;
  91. }
  92. set actor(value: Thing) {
  93. this._actor = value;
  94. }
  95. // TODO: The if shouldn't be necessary right? I don't wanna touch this right now.
  96. public getNoun (n : number) : any {
  97. if (this.nouns.length > n) {
  98. return this.nouns[n];
  99. }
  100. return undefined;
  101. }
  102. public setNoun (n : number, noun : any) {
  103. while (this.nouns.length < n) {
  104. this.nouns.push(undefined);
  105. }
  106. this.nouns[n] = noun;
  107. }
  108. /**
  109. * Needs to return a string explaining what the player will do if he does this action.
  110. * For instance, ActionTaking should return something like return "take " + this.nouns[0].getName(),
  111. * which would read as "take thing".
  112. * remember that things implement PRINTABLE interface, so you can get their names.
  113. * @returns {string}
  114. */
  115. public getCommandText () {
  116. return "do";
  117. }
  118. /**
  119. * If an action is stopped, it means it failed so spectacularly that it didn't even begin.
  120. * Which means if the player is doing it, it'll not take a turn.
  121. */
  122. public stop () {
  123. this.requiresTurn = false;
  124. }
  125. }
  126. // Action.addCarryRule(new Rule({
  127. // name : "Testing say in actions rule",
  128. // priority : Rule.PRIORITY_LOWEST,
  129. // firstPriority : Rule.PRIORITY_LOWEST,
  130. // code : (rule, rulebook) => {
  131. // let action = <Action> rulebook.noun;
  132. // action.say.add("You do nothing all turn. What was the point, really?");
  133. // }
  134. // }))
  135. Action.check.addRule(
  136. new Rule({
  137. name : "Check any Action - Requires Noun",
  138. firstPriority : Rule.PRIORITY_HIGHEST,
  139. code : (rulebook : RulebookRunner<Action>) => {
  140. let action = <Action> rulebook.noun;
  141. if (action.getNoun(0) == undefined) {
  142. return false;
  143. }
  144. },
  145. conditions : runner => {
  146. return runner.noun.requiresNoun;
  147. }
  148. })
  149. );
  150. Action.check.addRule(
  151. new Rule({
  152. name : "Check any Action - Stance",
  153. firstPriority : Rule.PRIORITY_HIGHEST,
  154. code : (rulebook : RulebookRunner<Action>) => {
  155. let action = <Action> rulebook.noun;
  156. let actor = action.actor;
  157. if (actor instanceof Person) {
  158. if (action.allowedStances.indexOf(actor.stance) == -1) {
  159. if (action.actor == WorldState.player) {
  160. action.say = new Say("You can't do that while ", PersonStanceNames[actor.stance], ", you need to be ", PersonStanceNames[action.allowedStances[0]], ".");
  161. }
  162. action.stop();
  163. return false;
  164. }
  165. }
  166. }
  167. })
  168. );
  169. Action.check.addRule(
  170. new Rule({
  171. name : "Check any Action - Requires Visibility",
  172. code : (rulebook : RulebookRunner<Action>) => {
  173. let action = <Action> rulebook.noun;
  174. let actor = action.actor;
  175. if (!action.getNoun(0).isVisibleTo(actor)) {
  176. return false;
  177. }
  178. },
  179. conditions : runner => {
  180. return runner.noun.requiresVisibility;
  181. }
  182. })
  183. );
  184. // TODO: Pass everything on here directly to the AI so that IT can handle this.
  185. Action.carry.addRule(
  186. new Rule({
  187. name : "Check any Action - Angery",
  188. firstPriority : Rule.PRIORITY_LOWEST,
  189. priority: Rule.PRIORITY_LOWEST,
  190. code : async (rulebook : RulebookRunner<Action>) => {
  191. let action = <Action> rulebook.noun;
  192. let target = action.getNoun(0);
  193. let tai = (<Person> target).AI;
  194. await tai.getPoked(action);
  195. },
  196. conditions : (rulebook : RulebookRunner<Action>) => {
  197. let action = <Action> rulebook.noun;
  198. return action.actingAgressively && action.getNoun(0) instanceof Person;
  199. }
  200. })
  201. );