Say.ts 16 KB

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