Action.ts 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  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. if (this.requiresTurn) {
  49. /**
  50. * Add to the list of actions performed in turn.
  51. * Notice the position of this thing: If the action failed, but an attempt was made, it still gets added to the board.
  52. * The importance of ths is, if you do something to annoy an NPC and you get stopped midway, they still get to react.
  53. */
  54. TurnSequence.addAction(this);
  55. }
  56. return;
  57. } else if(result instanceof Action) {
  58. console.debug(Rulebook.getIndentation() + "[ACTION] Instead of...");
  59. let originalNouns = this.nouns;
  60. await result.execute();
  61. this.say.add(result.say);
  62. this.nouns = result.nouns;
  63. // Reset to initial state
  64. this.nouns = originalNouns;
  65. return;
  66. }
  67. /**
  68. * Carry Out
  69. */
  70. await Action.carry.execute({
  71. noun : this
  72. }, ...carryRulebooks);
  73. /**
  74. * Add to the list of actions performed in turn.
  75. * Notice the position of this thing: if an action was replaced by another, it will instead add that action.
  76. */
  77. TurnSequence.addAction(this);
  78. return this.say;
  79. }
  80. get actor(): Thing {
  81. return this._actor;
  82. }
  83. set actor(value: Thing) {
  84. this._actor = value;
  85. }
  86. // TODO: The if shouldn't be necessary right? I don't wanna touch this right now.
  87. public getNoun (n : number) : any {
  88. if (this.nouns.length > n) {
  89. return this.nouns[n];
  90. }
  91. return undefined;
  92. }
  93. public setNoun (n : number, noun : any) {
  94. while (this.nouns.length < n) {
  95. this.nouns.push(undefined);
  96. }
  97. this.nouns[n] = noun;
  98. }
  99. /**
  100. * Needs to return a string explaining what the player will do if he does this action.
  101. * For instance, ActionTaking should return something like return "take " + this.nouns[0].getName(),
  102. * which would read as "take thing".
  103. * remember that things implement PRINTABLE interface, so you can get their names.
  104. * @returns {string}
  105. */
  106. public getCommandText () {
  107. return "do";
  108. }
  109. /**
  110. * If an action is stopped, it means it failed so spectacularly that it didn't even begin.
  111. * Which means if the player is doing it, it'll not take a turn.
  112. */
  113. public stop () {
  114. this.requiresTurn = false;
  115. }
  116. }
  117. // Action.addCarryRule(new Rule({
  118. // name : "Testing say in actions rule",
  119. // priority : Rule.PRIORITY_LOWEST,
  120. // firstPriority : Rule.PRIORITY_LOWEST,
  121. // code : (rule, rulebook) => {
  122. // let action = <Action> rulebook.noun;
  123. // action.say.add("You do nothing all turn. What was the point, really?");
  124. // }
  125. // }))
  126. Action.check.addRule(
  127. new Rule({
  128. name : "Check any Action - Requires Noun",
  129. firstPriority : Rule.PRIORITY_HIGHEST,
  130. code : (rulebook : RulebookRunner<Action>) => {
  131. let action = <Action> rulebook.noun;
  132. if (action.getNoun(0) == undefined) {
  133. return false;
  134. }
  135. },
  136. conditions : runner => {
  137. return runner.noun.requiresNoun;
  138. }
  139. })
  140. );
  141. Action.check.addRule(
  142. new Rule({
  143. name : "Check any Action - Stance",
  144. firstPriority : Rule.PRIORITY_HIGHEST,
  145. code : (rulebook : RulebookRunner<Action>) => {
  146. let action = <Action> rulebook.noun;
  147. let actor = action.actor;
  148. if (actor instanceof Person) {
  149. if (action.allowedStances.indexOf(actor.stance) == -1) {
  150. if (action.actor == WorldState.player) {
  151. action.say = new Say("You can't do that while ", PersonStanceNames[actor.stance], ", you need to be ", PersonStanceNames[action.allowedStances[0]], ".");
  152. }
  153. action.stop();
  154. return false;
  155. }
  156. }
  157. }
  158. })
  159. );
  160. Action.check.addRule(
  161. new Rule({
  162. name : "Check any Action - Requires Visibility",
  163. code : (rulebook : RulebookRunner<Action>) => {
  164. let action = <Action> rulebook.noun;
  165. let actor = action.actor;
  166. if (!action.getNoun(0).isVisibleTo(actor)) {
  167. return false;
  168. }
  169. },
  170. conditions : runner => {
  171. return runner.noun.requiresVisibility;
  172. }
  173. })
  174. );
  175. // TODO: Pass everything on here directly to the AI so that IT can handle this.
  176. // Action.carry.addRule(
  177. // new Rule({
  178. // name : "Check any Action - Angery",
  179. // firstPriority : Rule.PRIORITY_LOWEST,
  180. // priority: Rule.PRIORITY_LOWEST,
  181. // code : (rulebook : RulebookRunner<Action>) => {
  182. // let action = <Action> rulebook.noun;
  183. // let target = action.getNoun(0);
  184. // let tai = (<Person> target).AI;
  185. // tai.anger += action.aggressivenessRating * tai.grudgeRate;
  186. // let cu = new CombatPokeUnit().setTarget(target);
  187. // if (tai.anger < 10) {
  188. // cu.addMarker(CombatPokeResult.NOHEAT);
  189. // } else if (tai.anger < 100) {
  190. // cu.addMarker(CombatPokeResult.ANNOYED);
  191. // } else {
  192. // cu.addMarker(CombatPokeResult.AGGROED);
  193. // (<Person>target).AI.hostileTo.push(action.actor);
  194. // }
  195. //
  196. //
  197. // action.say.add(Say.PARAGRAPH_BREAK, ...CombatPokeDescription.getDescription(new ContentGroup(cu)));
  198. // },
  199. // conditions : (rulebook : RulebookRunner<Action>) => {
  200. // let action = <Action> rulebook.noun;
  201. // return action.actor == WorldState.player && action.actingAgressively && action.getNoun(0) instanceof Person && !action.getNoun(0).isHostileTo(action.actor) && action.getNoun(0).getHealthOnScale() > 0;
  202. // }
  203. // })
  204. // );