/// /// /// /// interface Printable { getPrintedName () : string; } interface PrintableElement { getPrintedElement () : Array; } interface SayNoun { say : Say; noun : any; } class SayableObject {} class Say { // TODO: Separate own sequence from processing queue. This way a Say with functions/other says can be reutilized with fresh values. public sequence : Array = []; public skipbreaks : boolean = false; public static LINE_BREAK : Object = new SayableObject(); public static CONDITIONAL_LINE_BREAK : Object = new SayableObject(); public static PARAGRAPH_BREAK : Object = new SayableObject(); public static RUN_PARAGRAPH : Object = new SayableObject(); public static RUN_PARAGRAPH_OFF : Object = new SayableObject(); public static CENTERED : Object = new SayableObject(); public static b : Object = new SayableObject(); public static DO_PARAGRAPH_BREAK = new SayableObject(); public static DO_LINE_BREAK = new SayableObject(); public static COCK = new SayableObject(); public static PUSSY = new SayableObject(); private centered : boolean = false; public static Action (a : any) { return new SayAction(a); } public static Bold (a : any) { return new SayBold(a); } public static Mention (target : Thing, uppercase = true) : Array { if (target == WorldState.player) { return [new SayYou(uppercase)]; } else { return [new SayThe(uppercase), target]; } } public static YouThem (target : Thing, uppercase = true) { return Say.Mention(target, uppercase); } public static YourTheir (target : Thing, uppercase = true) { if (target == WorldState.player) { return [new SayYour(uppercase)]; } else { return [new SayHisHersIts(target, uppercase)]; } } public static Article (target : Thing, uppercase = true) { let arr = []; if (target.isUnique()) { arr.push(new SayThe(uppercase)); } else { arr.push(new SayAn(uppercase)); } arr.push(target); return arr; } /** * He, She, It * @param target * @param uppercase * @constructor */ public static Subject (target : Thing, uppercase = true) { return new SayHeSheIt(target, uppercase); } /** * Him, Her, It * @param target * @param uppercase * @constructor */ public static Object (target : Thing, uppercase = true) { return new SayHimHerIt(target, uppercase); } /** * His, Hers, Its * @param target * @param uppercase * @constructor */ public static Possessive (target : Thing, uppercase = true) { return new SayHisHersIts(target, uppercase); } public static Speak (speaker : Thing | string, ...message : Array) { if (typeof speaker == "string") { return [new SayBold(speaker, ": "), ...message]; } else { return [new SayBold(...Say.YouThem(speaker), ": "), ...message]; } } public constructor (...objs) { this.add(...objs); } public add (...objs : Array string)>) { for (let i = 0; i < objs.length; i++) { if (Array.isArray(objs[i])) { this.sequence.push(...(> objs[i])); } else { this.sequence.push(objs[i]); } } return this; } public remove (...objs) { for (let i = 0; i < objs.length; i++) { let index = this.sequence.indexOf(objs[i]); if (index >= 0) { this.sequence.splice(index, 1); } } } public isEmpty () { return this.sequence.length < 1; } public paragraphs : Array>; public currentParagraph : Array; public sequenceRunner : number; // TODO: Create a single function to get the element of anything public async getTextOf (index : number, seq : any) : Promise { let elements = await this.getElementFor(index, seq); let div = document.createElement("div"); for (let i = 0; i < elements.length; i++) { if (typeof elements[i] != "number") { div.appendChild(elements[i]); } } return div.innerText; } public doLineBreak (noDouble : boolean) { if (this.currentParagraph.length > 0 && !this.skipbreaks) { if (noDouble) { let lastElement = this.currentParagraph[this.currentParagraph.length - 1]; if (lastElement != undefined && lastElement instanceof Element && lastElement.tagName == "BR") { return; } } let br = document.createElement("br"); br.classList.add("linebreak"); let ti = document.createElement("span"); ti.classList.add("textIndenter"); this.currentParagraph.push(br, ti); } } public doParagraphBreak () { if (this.currentParagraph.length > 0 && !this.skipbreaks) { this.paragraphs.push(this.currentParagraph); this.currentParagraph = []; } } public async getParagraphs () : Promise>> { this.paragraphs = []; this.currentParagraph = []; this.skipbreaks = false; for (this.sequenceRunner = 0; this.sequenceRunner < this.sequence.length; this.sequenceRunner++) { let seq = this.sequence[this.sequenceRunner]; if (seq instanceof OneOf) { seq = seq.getOne(); } if (seq == Say.CENTERED) { this.setCentered(true); } else if (seq == Say.b) { let boldObjects = []; for (let i = this.sequenceRunner + 1; i < this.sequence.length; i++) { let candidate = this.sequenceRunner[i]; if (candidate == Say.b) { this.sequence.splice(i, 1); break; } else { boldObjects.push(this.sequence.splice(i, 1)); } } if (boldObjects.length > 0) { let bold = new SayBold(...boldObjects); this.sequence.splice(this.sequenceRunner + 1, 0, bold); } } else if (seq == Say.COCK) { if (HumanoidPenis != undefined) { let cock = HumanoidPenis.getSynonym(); this.currentParagraph.push(document.createTextNode(cock)) } } else if (seq == Say.PUSSY) { if (HumanoidVagina != undefined) { let vagina = HumanoidVagina.getSynonym(); this.currentParagraph.push(document.createTextNode(vagina)) } } else if (seq == Say.LINE_BREAK || seq == Say.CONDITIONAL_LINE_BREAK) { this.doLineBreak(seq == Say.CONDITIONAL_LINE_BREAK); } else if (seq == Say.PARAGRAPH_BREAK) { this.doParagraphBreak(); } else if (seq == Say.RUN_PARAGRAPH) { this.skipbreaks = true; } else if (seq == Say.RUN_PARAGRAPH_OFF) { this.skipbreaks = false; } else if (typeof seq == "function") { // not a function, but a class if (seq.prototype != undefined) { this.currentParagraph.push(document.createTextNode(seq.name)); } else { let fObj = (<(s: Say) => any>seq)(this); if (Array.isArray(fObj)) { for (let k = fObj.length - 1; k >= 0; k--) { this.sequence.splice(this.sequenceRunner + 1, 0, fObj[k]); } } else if (fObj != undefined) { this.sequence.splice(this.sequenceRunner + 1, 0, fObj); } this.sequence.splice(this.sequenceRunner, 1); this.sequenceRunner--; } } else if (seq.constructor == this.constructor) { for (let k = ( seq).sequence.length - 1; k >= 0; k--) { this.sequence.splice(this.sequenceRunner + 1, 0, ( seq).sequence[k]); } this.sequence.splice(this.sequenceRunner, 1); this.sequenceRunner--; } else if (seq != undefined) { let elements = await this.getElementFor(this.sequenceRunner, seq); for (let i = 0; i < elements.length; i++) { if (elements[i] === Say.DO_LINE_BREAK) { this.doLineBreak(false); } else if (elements[i] === Say.DO_PARAGRAPH_BREAK) { this.doParagraphBreak(); } else { this.currentParagraph.push(elements[i]); } } } } // TODO: Remove line break + text indenter if they are the last in the say if (this.currentParagraph.length > 0) { this.paragraphs.push(this.currentParagraph); } return this.paragraphs; } /** * Lord Have mercy, I wish to never have to debug this piece of god. * @param {number} index * @param {Say | OneOf | Object | Printable | string | number | ((say: Say) => string) | ((say: Say) => Promise) | Element | Text} obj * @returns {Promise>} */ public async getElementFor (index : number, obj : Say | OneOf | Object | Printable | string | number | String | ((say : Say) => string) | ((say : Say) => Promise) | Element | Text) : Promise> { if (obj instanceof Promise) { obj = await obj; } if (typeof obj == "string" || obj instanceof String) { return [document.createTextNode( obj)]; } else if (typeof obj == "number" || obj instanceof Number) { return [document.createTextNode((parseFloat(( obj).toFixed(2))/1).toString())]; } else if (typeof obj == "function") { let elements = await this.getElementFor(-1, ( obj)(this)); return elements; } else if (obj instanceof SayImage) { return [obj.getImageElement()]; } else if (obj instanceof SayLeftRight) { return (await obj.getPureElements()); } else if (obj instanceof Say) { let elements = await obj.getPureElements(this); return elements; } else if (this.isProperElement(obj)) { return [ obj]; } else if (obj instanceof Object) { let elements = await this.printName(obj); return elements; } } public async getPureElements (say? : Say) : Promise> { let paragraphs = await this.getParagraphs(); return paragraphs.length == 1 ? paragraphs[0] : Array.prototype.concat.apply([], paragraphs); } public setCentered (bool : boolean) { this.centered = bool; } public async getHTML (tagName : string, classList : Array, singleParagraph? : boolean) : Promise> { let paragraphs = await this.getParagraphs(); // Reduce to single paragraph if (singleParagraph == true && paragraphs.length > 1) { paragraphs = [Array.prototype.concat.apply([], paragraphs)]; } let elements = []; for (let i = 0, paragraph = paragraphs[i]; paragraph != undefined; paragraph = paragraphs[++i]) { let parent = document.createElement(tagName); if (classList.length > 0) { parent.classList.add(...classList); } for (let k = 0, ele = paragraph[k]; ele!= undefined; ele = paragraph[++k]) { parent.appendChild(ele); } elements.push(parent); if (this.centered) { parent.classList.add("centered"); } } return elements; } public getHTMLContent () : Promise> { return this.getHTML("p", ["content"]); } public isProperElement (o) : boolean { return ( typeof Node === "object" ? o instanceof Node : o && typeof o === "object" && typeof o.nodeType === "number" && typeof o.nodeName==="string" ) || ( typeof HTMLElement === "object" ? o instanceof HTMLElement : //DOM2 o && typeof o === "object" && o !== null && o.nodeType === 1 && typeof o.nodeName==="string" ); } public static beforePrinting = new Rulebook("Before printing the name of something"); public static printing = new Rulebook("Printing the name of something"); public static afterPrinting = new Rulebook("After printing the name of something"); public currentNoun : any; public currentNounElements : Array; public async printName (thing : any) : Promise> { this.currentNoun = thing; this.currentNounElements = []; let before = Say.beforePrinting.execute({noun : this}); await before; let print = Say.printing.execute({noun : this}); await print; let after = Say.afterPrinting.execute({noun : this}); await after; return this.currentNounElements; } public static hisHersIts (target : Thing, startOfSentence? : boolean) { return new SayHisHersIts(target); // let result : String; // if (target == WorldState.player) { // result = "your"; // } else if (target instanceof Person) { // // TODO: Figure out whether target is male or female // result = "his"; // } else { // result = "its"; // } // if (startOfSentence == true) { // result = result.charAt(0).toUpperCase() + result.substr(1, result.length -1); // } // return result; } } Say.printing.addRule(new Rule({ name : "Printing the name of a Printable Element", firstPriority : Rule.PRIORITY_LOW, code : (rulebook : RulebookRunner) => { let say = rulebook.noun; if (( say.currentNoun).getPrintedElement) { say.currentNounElements.push(...( say.currentNoun).getPrintedElement()); return true; // We only want to print something once, so return true to stop others from running } } })); Say.printing.addRule(new Rule({ name : "Printing the name of a Printable", firstPriority : Rule.PRIORITY_LOW, code : (rulebook : RulebookRunner) => { let say = rulebook.noun; if (( say.currentNoun).getPrintedName) { let thingEle = document.createTextNode( ( say.currentNoun).getPrintedName() ); say.currentNounElements.push(thingEle); return true; // We only want to print something once, so return true to stop others from running } } })); Say.printing.addRule(new Rule({ name : "Printing the name of an unknown object", firstPriority : Rule.PRIORITY_LOWEST, priority : Rule.PRIORITY_LOWEST, code : (rulebook : RulebookRunner) => { let say = rulebook.noun; if (( say.currentNoun).getPrintedName) { say.currentNounElements.push( (document.createTextNode(( say.currentNoun).toString())) ); return true; // We only want to print something once, so return true to stop others from running } } })); // var msg = new Say ("Hello! Welcome to The Obelisk! This is a game with ", johnTheOgre, " so you must be careful!"); // // var otherSay = new Say (msg, "Will have to learn how to handle dots.");