Say.ts 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. /// <reference path="../../World/Classes/Rulebook.ts" />
  2. /// <reference path="../../World/Classes/Rule.ts" />
  3. /// <reference path="Say/OneOf.ts" />
  4. /// <reference path="Say/SayImage.ts" />
  5. interface Printable {
  6. getPrintedName () : string;
  7. }
  8. interface PrintableElement {
  9. getPrintedElement () : Array<Element>;
  10. }
  11. interface SayNoun {
  12. say : Say;
  13. noun : any;
  14. }
  15. class SayableObject {}
  16. class Say {
  17. // TODO: Separate own sequence from processing queue. This way a Say with functions/other says can be reutilized with fresh values.
  18. public sequence : Array <Object> = [];
  19. public skipbreaks : boolean = false;
  20. public static LINE_BREAK : Object = new SayableObject();
  21. public static PARAGRAPH_BREAK : Object = new SayableObject();
  22. public static RUN_PARAGRAPH : Object = new SayableObject();
  23. public static RUN_PARAGRAPH_OFF : Object = new SayableObject();
  24. public static CENTERED : Object = new SayableObject();
  25. public static b : Object = new SayableObject();
  26. public static DO_PARAGRAPH_BREAK = new SayableObject();
  27. public static DO_LINE_BREAK = new SayableObject();
  28. public static COCK = new SayableObject();
  29. public static PUSSY = new SayableObject();
  30. private centered : boolean = false;
  31. public constructor (...objs) {
  32. this.add(...objs);
  33. }
  34. public add (...objs : Array<Say | OneOf | Object | Printable | string | number | String | ((say : Say) => string)>) {
  35. this.sequence.push(...objs);
  36. }
  37. public remove (...objs) {
  38. for (let i = 0; i < objs.length; i++) {
  39. let index = this.sequence.indexOf(objs[i]);
  40. if (index >= 0) {
  41. this.sequence.splice(index, 1);
  42. }
  43. }
  44. }
  45. public isEmpty () {
  46. return this.sequence.length < 1;
  47. }
  48. public paragraphs : Array<Array<Element | Text>>;
  49. public currentParagraph : Array<Element | Text>;
  50. public sequenceRunner : number;
  51. // TODO: Create a single function to get the element of anything
  52. public async getTextOf (index : number, seq : any) : Promise<string> {
  53. let elements = await this.getElementFor(index, seq);
  54. let div = document.createElement("div");
  55. for (let i = 0; i < elements.length; i++) {
  56. if (typeof elements[i] != "number") {
  57. div.appendChild(elements[i]);
  58. }
  59. }
  60. return div.innerText;
  61. }
  62. public doLineBreak () {
  63. if (this.currentParagraph.length > 0 && !this.skipbreaks) {
  64. let br = document.createElement("br");
  65. br.classList.add("linebreak");
  66. let ti = document.createElement("span");
  67. ti.classList.add("textIndenter");
  68. this.currentParagraph.push(br, ti);
  69. }
  70. }
  71. public doParagraphBreak () {
  72. if (this.currentParagraph.length > 0 && !this.skipbreaks) {
  73. this.paragraphs.push(this.currentParagraph);
  74. this.currentParagraph = [];
  75. }
  76. }
  77. public async getParagraphs () : Promise<Array<Array<Element|Text>>> {
  78. this.paragraphs = [];
  79. this.currentParagraph = [];
  80. this.skipbreaks = false;
  81. for (this.sequenceRunner = 0; this.sequenceRunner < this.sequence.length; this.sequenceRunner++) {
  82. let seq = this.sequence[this.sequenceRunner];
  83. if (seq instanceof OneOf) {
  84. seq = seq.getOne();
  85. }
  86. if (seq == Say.CENTERED) {
  87. this.setCentered(true);
  88. } else if (seq == Say.b) {
  89. let boldObjects = [];
  90. for (let i = this.sequenceRunner + 1; i < this.sequence.length; i++) {
  91. let candidate = this.sequenceRunner[i];
  92. if (candidate == Say.b) {
  93. this.sequence.splice(i, 1);
  94. break;
  95. } else {
  96. boldObjects.push(this.sequence.splice(i, 1));
  97. }
  98. }
  99. if (boldObjects.length > 0) {
  100. let bold = new SayBold(...boldObjects);
  101. this.sequence.splice(this.sequenceRunner + 1, 0, bold);
  102. }
  103. } else if (seq == Say.COCK) {
  104. if (HumanoidPenis != undefined) {
  105. let cock = HumanoidPenis.getSynonym();
  106. this.currentParagraph.push(document.createTextNode(cock))
  107. }
  108. } else if (seq == Say.PUSSY) {
  109. if (HumanoidVagina != undefined) {
  110. let vagina = HumanoidVagina.getSynonym();
  111. this.currentParagraph.push(document.createTextNode(vagina))
  112. }
  113. } else if (seq == Say.LINE_BREAK) {
  114. this.doLineBreak();
  115. } else if (seq == Say.PARAGRAPH_BREAK) {
  116. this.doParagraphBreak();
  117. } else if (seq == Say.RUN_PARAGRAPH) {
  118. this.skipbreaks = true;
  119. } else if (seq == Say.RUN_PARAGRAPH_OFF) {
  120. this.skipbreaks = false;
  121. } else if (typeof seq == "function") {
  122. let fObj = (<(s: Say) => any> seq)(this);
  123. if (Array.isArray(fObj)) {
  124. for (let k = fObj.length - 1; k >= 0; k--) {
  125. this.sequence.splice(this.sequenceRunner + 1, 0, fObj[k]);
  126. }
  127. } else if (fObj != undefined) {
  128. this.sequence.splice(this.sequenceRunner + 1, 0, fObj);
  129. }
  130. this.sequence.splice(this.sequenceRunner, 1);
  131. this.sequenceRunner--;
  132. } else if (seq.constructor == this.constructor) {
  133. for (let k = (<Say> seq).sequence.length - 1; k >= 0; k--) {
  134. this.sequence.splice(this.sequenceRunner + 1, 0, (<Say> seq).sequence[k]);
  135. }
  136. this.sequence.splice(this.sequenceRunner, 1);
  137. this.sequenceRunner--;
  138. } else if (seq != undefined) {
  139. let elements = await this.getElementFor(this.sequenceRunner, seq);
  140. for (let i = 0; i < elements.length; i++) {
  141. if (elements[i] === Say.DO_LINE_BREAK) {
  142. this.doLineBreak();
  143. } else if (elements[i] === Say.DO_PARAGRAPH_BREAK) {
  144. this.doParagraphBreak();
  145. } else {
  146. this.currentParagraph.push(elements[i]);
  147. }
  148. }
  149. }
  150. }
  151. // TODO: Remove line break + text indenter if they are the last in the say
  152. if (this.currentParagraph.length > 0) {
  153. this.paragraphs.push(this.currentParagraph);
  154. }
  155. return this.paragraphs;
  156. }
  157. /**
  158. * Lord Have mercy, I wish to never have to debug this piece of god.
  159. * @param {number} index
  160. * @param {Say | OneOf | Object | Printable | string | number | ((say: Say) => string) | ((say: Say) => Promise<string>) | Element | Text} obj
  161. * @returns {Promise<Array<Element | Text>>}
  162. */
  163. public async getElementFor (index : number, obj : Say | OneOf | Object | Printable | string | number | String | ((say : Say) => string) | ((say : Say) => Promise<string>) | Element | Text) : Promise<Array<Element|Text>> {
  164. if (obj instanceof Promise) {
  165. obj = await obj;
  166. }
  167. if (typeof obj == "string" || obj instanceof String) {
  168. return [document.createTextNode(<string> obj)];
  169. } else if (typeof obj == "number" || obj instanceof Number) {
  170. return [document.createTextNode((parseFloat((<number> obj).toFixed(2))/1).toString())];
  171. } else if (typeof obj == "function") {
  172. let elements = await this.getElementFor(-1, (<any> obj)(this));
  173. return elements;
  174. } else if (obj instanceof SayImage) {
  175. return [obj.getImageElement()];
  176. } else if (obj instanceof SayLeftRight) {
  177. return (await obj.getPureElements());
  178. } else if (obj instanceof Say) {
  179. let elements = await obj.getPureElements(this);
  180. return elements;
  181. } else if (this.isProperElement(obj)) {
  182. return [<Element> obj];
  183. } else if (obj instanceof Object) {
  184. let elements = await this.printName(obj);
  185. return elements;
  186. }
  187. }
  188. public async getPureElements (say? : Say) : Promise<Array<Element | Text>> {
  189. let paragraphs = await this.getParagraphs();
  190. return paragraphs.length == 1 ? paragraphs[0] : Array.prototype.concat.apply([], paragraphs);
  191. }
  192. public setCentered (bool : boolean) {
  193. this.centered = bool;
  194. }
  195. public async getHTML (tagName : string, classList : Array<string>, singleParagraph? : boolean) : Promise<Array<HTMLElement>> {
  196. let paragraphs = await this.getParagraphs();
  197. // Reduce to single paragraph
  198. if (singleParagraph == true && paragraphs.length > 1) {
  199. paragraphs = [Array.prototype.concat.apply([], paragraphs)];
  200. }
  201. let elements = [];
  202. for (let i = 0, paragraph = paragraphs[i]; paragraph != undefined; paragraph = paragraphs[++i]) {
  203. let parent = <HTMLElement> document.createElement(tagName);
  204. if (classList.length > 0) {
  205. parent.classList.add(...classList);
  206. }
  207. for (let k = 0, ele = paragraph[k]; ele!= undefined; ele = paragraph[++k]) {
  208. parent.appendChild(ele);
  209. }
  210. elements.push(parent);
  211. if (this.centered) {
  212. parent.classList.add("centered");
  213. }
  214. }
  215. return elements;
  216. }
  217. public getHTMLContent () : Promise<Array<HTMLElement>> {
  218. return this.getHTML("p", ["content"]);
  219. }
  220. public isProperElement (o) : boolean {
  221. return (
  222. typeof Node === "object" ? o instanceof Node :
  223. o && typeof o === "object" && typeof o.nodeType === "number" && typeof o.nodeName==="string"
  224. ) || (
  225. typeof HTMLElement === "object" ? o instanceof HTMLElement : //DOM2
  226. o && typeof o === "object" && o !== null && o.nodeType === 1 && typeof o.nodeName==="string"
  227. );
  228. }
  229. public static beforePrinting = new Rulebook<any>("Before printing the name of something");
  230. public static printing = new Rulebook<any>("Printing the name of something");
  231. public static afterPrinting = new Rulebook<any>("After printing the name of something");
  232. public currentNoun : any;
  233. public currentNounElements : Array<Element | Text>;
  234. public async printName (thing : any) : Promise<Array<Element | Text>> {
  235. this.currentNoun = thing;
  236. this.currentNounElements = [];
  237. let before = Say.beforePrinting.execute({noun : this});
  238. await before;
  239. let print = Say.printing.execute({noun : this});
  240. await print;
  241. let after = Say.afterPrinting.execute({noun : this});
  242. await after;
  243. return this.currentNounElements;
  244. }
  245. public static hisHersIts (target : Thing, startOfSentence? : boolean) {
  246. return new SayHisHersIts(target);
  247. // let result : String;
  248. // if (target == WorldState.player) {
  249. // result = "your";
  250. // } else if (target instanceof Person) {
  251. // // TODO: Figure out whether target is male or female
  252. // result = "his";
  253. // } else {
  254. // result = "its";
  255. // }
  256. // if (startOfSentence == true) {
  257. // result = result.charAt(0).toUpperCase() + result.substr(1, result.length -1);
  258. // }
  259. // return result;
  260. }
  261. }
  262. Say.printing.addRule(new Rule({
  263. name : "Printing the name of a Printable Element",
  264. firstPriority : Rule.PRIORITY_LOW,
  265. code : (rulebook : RulebookRunner<any>) => {
  266. let say = <Say> rulebook.noun;
  267. if ((<any> say.currentNoun).getPrintedElement) {
  268. say.currentNounElements.push(...(<PrintableElement> say.currentNoun).getPrintedElement());
  269. return true; // We only want to print something once, so return true to stop others from running
  270. }
  271. }
  272. }));
  273. Say.printing.addRule(new Rule({
  274. name : "Printing the name of a Printable",
  275. firstPriority : Rule.PRIORITY_LOW,
  276. code : (rulebook : RulebookRunner<any>) => {
  277. let say = <Say> rulebook.noun;
  278. if ((<any> say.currentNoun).getPrintedName) {
  279. let thingEle = document.createTextNode(
  280. (<Printable> say.currentNoun).getPrintedName()
  281. );
  282. say.currentNounElements.push(thingEle);
  283. return true; // We only want to print something once, so return true to stop others from running
  284. }
  285. }
  286. }));
  287. Say.printing.addRule(new Rule({
  288. name : "Printing the name of an unknown object",
  289. firstPriority : Rule.PRIORITY_LOWEST,
  290. priority : Rule.PRIORITY_LOWEST,
  291. code : (rulebook : RulebookRunner<any>) => {
  292. let say = <Say> rulebook.noun;
  293. if ((<any> say.currentNoun).getPrintedName) {
  294. say.currentNounElements.push(
  295. (document.createTextNode((<Object> say.currentNoun).toString()))
  296. );
  297. return true; // We only want to print something once, so return true to stop others from running
  298. }
  299. }
  300. }));
  301. // var msg = new Say ("Hello! Welcome to The Obelisk! This is a game with ", johnTheOgre, " so you must be careful!");
  302. //
  303. // var otherSay = new Say (msg, "Will have to learn how to handle dots.");