Say.ts 14 KB

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