Action.ts 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  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 constructor (actor : Thing, ...nouns : Array<any>) {
  21. this.actor = actor;
  22. nouns.forEach((value, index, array) => {
  23. this.setNoun(index, value);
  24. });
  25. }
  26. public async execute () : Promise<Say> {
  27. this.say = new Say();
  28. let checkRulebooks = [];
  29. let carryRulebooks = [];
  30. let cClass = this.constructor;
  31. while (cClass != Action) {
  32. if ((<typeof Action> cClass).check != undefined) {
  33. checkRulebooks.push((<typeof Action> cClass).check);
  34. }
  35. if ((<typeof Action> cClass).carry != undefined) {
  36. carryRulebooks.push((<typeof Action> cClass).carry);
  37. }
  38. cClass = Object.getPrototypeOf(cClass);
  39. }
  40. /**
  41. * Check if action goes through
  42. */
  43. let result = await Action.check.execute({
  44. noun : this
  45. }, ...checkRulebooks);
  46. // There are now multiple results! A false result means a fail Check! But it can also return a new action!
  47. if (result == false) {
  48. return;
  49. } else if(result instanceof Action) {
  50. console.debug(Rulebook.getIndentation() + "[ACTION] Instead of...");
  51. let originalNouns = this.nouns;
  52. await result.execute();
  53. this.say.add(result.say);
  54. this.nouns = result.nouns;
  55. // Reset to initial state
  56. this.nouns = originalNouns;
  57. return;
  58. }
  59. /**
  60. * Carry Out
  61. */
  62. await Action.carry.execute({
  63. noun : this
  64. }, ...carryRulebooks);
  65. return this.say;
  66. }
  67. get actor(): Thing {
  68. return this._actor;
  69. }
  70. set actor(value: Thing) {
  71. this._actor = value;
  72. }
  73. public getNoun (n : number) : any {
  74. if (this.nouns.length > n) {
  75. return this.nouns[n];
  76. }
  77. return undefined;
  78. }
  79. public setNoun (n : number, noun : any) {
  80. while (this.nouns.length < n) {
  81. this.nouns.push(undefined);
  82. }
  83. this.nouns[n] = noun;
  84. }
  85. /**
  86. * Needs to return a string explaining what the player will do if he does this action.
  87. * For instance, ActionTaking should return something like return "take " + this.nouns[0].getName(),
  88. * which would read as "take thing".
  89. * remember that things implement PRINTABLE interface, so you can get their names.
  90. * @returns {string}
  91. */
  92. public getCommandText () {
  93. return "do";
  94. }
  95. /**
  96. * If an action is stopped, it means it failed so spectacularly that it didn't even begin.
  97. * Which means if the player is doing it, it'll not take a turn.
  98. */
  99. public stop () {
  100. this.requiresTurn = false;
  101. }
  102. }
  103. // Action.addCarryRule(new Rule({
  104. // name : "Testing say in actions rule",
  105. // priority : Rule.PRIORITY_LOWEST,
  106. // firstPriority : Rule.PRIORITY_LOWEST,
  107. // code : (rule, rulebook) => {
  108. // let action = <Action> rulebook.noun;
  109. // action.say.add("You do nothing all turn. What was the point, really?");
  110. // }
  111. // }))
  112. Action.check.addRule(
  113. new Rule({
  114. name : "Check any Action - Requires Noun",
  115. firstPriority : Rule.PRIORITY_HIGHEST,
  116. code : (rulebook : RulebookRunner<Action>) => {
  117. let action = <Action> rulebook.noun;
  118. if (action.getNoun(0) == undefined) {
  119. return false;
  120. }
  121. },
  122. conditions : runner => {
  123. return runner.noun.requiresNoun;
  124. }
  125. })
  126. );
  127. Action.check.addRule(
  128. new Rule({
  129. name : "Check any Action - Stance",
  130. firstPriority : Rule.PRIORITY_HIGHEST,
  131. code : (rulebook : RulebookRunner<Action>) => {
  132. let action = <Action> rulebook.noun;
  133. let actor = action.actor;
  134. if (actor instanceof Person) {
  135. if (action.allowedStances.indexOf(actor.stance) == -1) {
  136. if (action.actor == WorldState.player) {
  137. action.say = new Say("You can't do that while ", PersonStanceNames[actor.stance], ", you need to be ", PersonStanceNames[actor.stance], ".");
  138. }
  139. return false;
  140. action.stop();
  141. }
  142. }
  143. }
  144. })
  145. );
  146. Action.check.addRule(
  147. new Rule({
  148. name : "Check any Action - Requires Visibility",
  149. code : (rulebook : RulebookRunner<Action>) => {
  150. let action = <Action> rulebook.noun;
  151. let actor = action.actor;
  152. if (!action.getNoun(0).isVisibleTo(actor)) {
  153. return false;
  154. }
  155. },
  156. conditions : runner => {
  157. return runner.noun.requiresVisibility;
  158. }
  159. })
  160. );
  161. // TODO: Pass everything on here directly to the AI so that IT can handle this.
  162. Action.carry.addRule(
  163. new Rule({
  164. name : "Check any Action - Angery",
  165. firstPriority : Rule.PRIORITY_LOWEST,
  166. priority: Rule.PRIORITY_LOWEST,
  167. code : (rulebook : RulebookRunner<Action>) => {
  168. let action = <Action> rulebook.noun;
  169. let target = action.getNoun(0);
  170. let tai = (<Person> target).AI;
  171. tai.anger += action.aggressivenessRating * tai.grudgeRate;
  172. let cu = new CombatPokeUnit().setTarget(target);
  173. if (tai.anger < 10) {
  174. cu.addMarker(CombatPokeResult.NOHEAT);
  175. } else if (tai.anger < 100) {
  176. cu.addMarker(CombatPokeResult.ANNOYED);
  177. } else {
  178. cu.addMarker(CombatPokeResult.AGGROED);
  179. (<Person>target).AI.hostileTo.push(action.actor);
  180. }
  181. action.say.add(Say.PARAGRAPH_BREAK, ...CombatPokeDescription.getDescription(new ContentGroup(cu)));
  182. },
  183. conditions : (rulebook : RulebookRunner<Action>) => {
  184. let action = <Action> rulebook.noun;
  185. return action.actor == WorldState.player && action.actingAgressively && action.getNoun(0) instanceof Person && !action.getNoun(0).isHostileTo(action.actor) && action.getNoun(0).getHealthOnScale() > 0;
  186. }
  187. })
  188. );