36 Commit-ok 36537f10be ... b0714751cc

Szerző SHA1 Üzenet Dátum
  Reddo b0714751cc 11 Dialogue Node Types... 5 éve
  Reddo b36a2c0b14 Allow empty variable Branches to just be truth-state forks! 5 éve
  Reddo d91f5b4e71 Remember which rooms were visited forever 5 éve
  Reddo bb08383468 Add TODO 5 éve
  Reddo 1f3ed948ad Also unset the placed! 5 éve
  Reddo 33d217a7ea Fix hyperlinking from keyboard 5 éve
  Reddo c61031f015 Resting Stuff and Resting Action 5 éve
  Reddo 52df39008d Fix name 5 éve
  Reddo ad294bfdc2 Support for postponed possibility additions 5 éve
  Reddo 72a4f4c0c4 more types of objects 5 éve
  Reddo 1de2fa3c53 make sure current room is always remembered 5 éve
  Reddo 94d1822889 Private to protected 5 éve
  Reddo e295eaccfd Fodder options 5 éve
  Reddo 06a0b03758 Higher fodder level limit 5 éve
  Reddo 9ae703cdbc Minor improvements 5 éve
  Reddo 599fa7a26c Fix the thing I didn't know what it was 5 éve
  Reddo 38072110b1 Couldn't get it fixed, so just remade it 5 éve
  Reddo 2b5ba284fa I don't even know 5 éve
  Reddo 8f9527db06 Create random coordinates without letting them conflict 5 éve
  Reddo f45e055f8a RandomRange 5 éve
  Reddo 79b529a10b Turns out remembering all fodder rooms on saved games is a BAD IDEA 5 éve
  Reddo 7d5093a033 Fix references to wordlstate.player 5 éve
  Reddo bb06046155 Fix breaking when Thing no longer exists 5 éve
  Reddo 145dd394e5 Random fixes 5 éve
  Reddo 02733fed44 Shortcut to adding rooms at specific coordinates. 5 éve
  Reddo cd5d662f82 Fuck dots 5 éve
  Reddo 6dd029827c Dooooooooooooooooooor 5 éve
  Reddo 6a5d8ad380 Dooooooooooooooooooor 5 éve
  Reddo be2ea28702 Shortcut 5 éve
  Reddo 00c0d9f3c6 Multiple choices in the same line (good for small choices) 5 éve
  Reddo fae6ab1201 Conditional line breaks? 5 éve
  Reddo a303ba30ed Dont start dialogue if nothing to say. Correctly finish dialogue if something was said. 5 éve
  Reddo 2401d54dd5 Speak helper function 5 éve
  Reddo a200689347 Do not keep going if a dialogue happened 5 éve
  Reddo 307f549c94 Automatically count DialogueTree usage (and save it!) 5 éve
  Reddo ddfb8f7269 Clearer AI Hooks 5 éve

+ 28 - 3
app/Controls/Controls.ts

@@ -9,6 +9,17 @@ module Controls {
         return p;
     }
 
+    export function createSmallButton (text : string, index : number, resolve : (t : number) => void) {
+        let p = document.createElement("p");
+        p.classList.add("choice");
+        p.classList.add("small");
+        p.appendChild(document.createTextNode(text));
+
+        markButton(p, index, resolve);
+
+        return p;
+    }
+
     function markButton (p : HTMLElement, index : number, resolve : (t : number) => void) {
         p.addEventListener("click", () => {
             resolve(index);
@@ -17,8 +28,12 @@ module Controls {
         Controls.KeyHandler.applyCode(p, Controls.KeyHandler.getFirstKeyCode());
     }
 
-    export async function giveChoices (big? : boolean, ...choices : Array<Say | string>) : Promise<Array<any>> {
+    export async function giveChoices (big : boolean, ...choices : Array<Say | string>) : Promise<Array<any>> {
         let buttons;
+        let classList = ["choice"];
+        if (big != true) {
+            classList.push("small");
+        }
         let chosenPromise = <Promise<number>> new Promise((async (resolve) => {
             Controls.KeyHandler.reset();
             let say = new Say();
@@ -26,11 +41,15 @@ module Controls {
             for (let i = 0; i < choices.length; i++) {
                 let choice = choices[i];
                 if (choice instanceof Say) {
-                    let button = (await choice.getHTML("p", ["choice"], true))[0];
+                    let button = (await choice.getHTML("p", classList, true))[0];
                     markButton(button, i, resolve);
                     say.add(button);
                 } else {
-                    say.add(createBigButton(choice, i, resolve))
+                    if (big == true) {
+                        say.add(createBigButton(choice, i, resolve));
+                    } else {
+                        say.add(createSmallButton(choice, i, resolve));
+                    }
                 }
             }
 
@@ -42,4 +61,10 @@ module Controls {
         Elements.CurrentTurnHandler.unprint(...buttons);
         return [choices[chosen], chosen];
     }
+
+    export async function askForConsent () : Promise<boolean> {
+        let choices = ["Yes", "No"];
+        let choice = await giveChoices(false, ...choices);
+        return choice[1] == 0;
+    }
 }

+ 26 - 5
app/Elements/Classes/Say.ts

@@ -24,6 +24,7 @@ class Say {
     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();
@@ -56,12 +57,26 @@ class Say {
         }
     }
 
+    public static Speak (speaker : Thing | string, ...message : Array<any>) {
+        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<Say | OneOf | Object | Printable | string | number | String | ((say : Say) => string)>) {
-        this.sequence.push(...objs);
+        for (let i = 0; i < objs.length; i++) {
+            if (Array.isArray(objs[i])) {
+                this.sequence.push(...(<Array<any>> objs[i]));
+            } else {
+                this.sequence.push(objs[i]);
+            }
+        }
         return this;
     }
 
@@ -95,8 +110,14 @@ class Say {
         return div.innerText;
     }
 
-    public doLineBreak () {
+    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");
@@ -150,8 +171,8 @@ class Say {
                     let vagina = HumanoidVagina.getSynonym();
                     this.currentParagraph.push(document.createTextNode(vagina))
                 }
-            } else if (seq == Say.LINE_BREAK) {
-                this.doLineBreak();
+            } 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) {
@@ -179,7 +200,7 @@ class Say {
                 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();
+                        this.doLineBreak(false);
                     } else if (elements[i] === Say.DO_PARAGRAPH_BREAK) {
                         this.doParagraphBreak();
                     } else {

+ 8 - 1
app/Elements/Classes/Say/OneOf.ts

@@ -12,8 +12,15 @@ class OneOf {
 
     public constructor (randomMode : number, ...poss : Array<any>) {
         this.randomMode = randomMode;
-        this.possibilities = poss;
+        this.addPossibilities(...poss);
+    }
+
+    public addPossibilities (...poss : Array<any>) {
+        this.possibilities.push(...poss);
+        this.updatePossibilities();
+    }
 
+    public updatePossibilities () {
         if (this.randomMode == OneOf.ROTATING_RANDOM) {
             this.availablePossibilites = this.possibilities.slice();
         }

+ 4 - 0
app/Functions.ts

@@ -32,4 +32,8 @@ function arrayShuffleNewArray (a : Array<any>) {
     let nA = a.slice();
     arrayShuffle(nA);
     return nA;
+}
+
+function randomRange (min, max) {
+    return Math.floor(Math.random() * (max - min + 1)) + min;;
 }

+ 6 - 0
app/SaveHandler.ts

@@ -98,6 +98,12 @@ module SaveHandler {
         let item : Thing;
         if (thing.Unique) {
             item = Thing.getUnique(thing.Name);
+            if (item == undefined) {
+                let error = thing.Name + " no longer exists.";
+                console.error("[SaveHandler] " + error);
+                errors.push(error)
+                return undefined;
+            }
         } else {
             let items = Thing.getNonUnique(thing.Name);
             if (items.length > 0) {

+ 1 - 1
app/World/Classes/Action/ActionDropDown.ts

@@ -24,7 +24,7 @@ class ActionDropDown extends Action {
     });
 
     public static carryStand = ActionDropDown.carry.createAndAddRule({
-        name : "Stand - Rise up!",
+        name : "Stand - Go doggy",
         code : (runner : RulebookRunner<ActionDropDown>) => {
             let actor = runner.noun.actor;
             if (actor instanceof Person) {

+ 92 - 0
app/World/Classes/Action/ActionGoThrough.ts

@@ -0,0 +1,92 @@
+/// <reference path="../Action.ts" />
+/// <reference path="../Rule.ts" />
+/// <reference path="../Rulebook.ts" />
+class ActionGoThrough extends Action {
+    public static check = new Rulebook<ActionGoThrough>("Check Going Through");
+    public static carry = new Rulebook<ActionGoThrough>("Carry out Going Through");
+
+    public constructor (actor : Thing, ...nouns : Array<any>) {
+        super(actor, ...nouns);
+        this.requiresNoun = true;
+        this.requiresVisibility = true;
+    }
+
+    public getCommandText () {
+        return "go through " + (<Door> this.getDoor()).getPrintedName();
+    }
+
+    public getDoor () : Door {
+        return this.getNoun(0);
+    }
+
+    public static checkLocked = ActionGoThrough.check.createAndAddRule({
+        firstPriority : Rule.PRIORITY_HIGHEST,
+        priority : Rule.PRIORITY_HIGHEST,
+        name : "Check Going Through - Is it locked?",
+        code : async (runner: RulebookRunner<ActionGoThrough>) => {
+            let actor = runner.noun.actor;
+            let door = <Door> runner.noun.getDoor();
+            let isPlayer = WorldState.isPlayer(actor);
+
+            if (door.locked) {
+                let actorThings = actor.getEnclosed();
+                if (actorThings.includes(door.key)) {
+                    if (isPlayer) {
+                        Elements.CurrentTurnHandler.printAsContent(new Say(Say.Mention(door), " is locked, but you can unlock it with ", Say.Mention(door.key), ". Do you want to unlock it?"));
+                        if (!(await Controls.askForConsent())) {
+                            return false;
+                        }
+                    }
+                    door.locked = false;
+                } else {
+                    if (isPlayer) {
+                        runner.noun.say.add("It's locked, and you don't have the key to unlock it...");
+                    }
+                    // TODO: Reconsider whether taking a turn on failed open checks is good.
+                    return false;
+                }
+            }
+        }
+    });
+
+    public static carryGoThrough = ActionGoThrough.check.createAndAddRule({
+        name: "Carry Going Through - Go Through!",
+        code: async (runner: RulebookRunner<ActionGoThrough>) => {
+            let actor = runner.noun.actor;
+            let door = <Door> runner.noun.getDoor();
+            let isPlayer = WorldState.isPlayer(actor);
+
+            if (isPlayer) {
+                runner.noun.say.add("You go through ", Say.Mention(door), ".");
+
+                if (actor.isPlayer()) {
+                    WorldState.rememberRoom(actor.getRoom(), door.destination);
+                }
+            } else {
+                let oldRoom = actor.getRoom();
+                let playerRoom = WorldState.player.getRoom();
+                if (oldRoom == playerRoom) {
+                    runner.noun.say.add(Say.Mention(actor), " goes through ", Say.Mention(door), ".");
+                } else {
+                    runner.noun.say.add(Say.Mention(actor), " arrives through ", Say.Mention(door), ".");
+                }
+            }
+
+            door.destination.place(actor);
+        }
+    });
+}
+
+Elements.HyperlinkHandler.HyperlinkingRulebook.addRule(new Rule(
+    {
+        name : "Hyperlink - Go through",
+        firstPriority : Rule.PRIORITY_HIGHEST,
+        code : (rulebook : RulebookRunner<Thing>) => {
+            let thing = <Thing> rulebook.noun;
+
+            if (thing instanceof Door && thing.isVisibleTo(WorldState.player)) {
+                Elements.HyperlinkHandler.addAvailableAction("Go through", new ActionGoThrough(WorldState.player, thing));
+            }
+        }
+    }
+));

+ 1 - 0
app/World/Classes/Action/ActionInventory.ts

@@ -87,6 +87,7 @@ class ActionInventory extends Action {
             }));
 
             let chosenThing = await chosenPromise;
+            rulebook.noun.setNoun(0, chosenThing);
             if (chosenThing != undefined) {
                 Elements.CurrentTurnHandler.unprint(...buttons);
                 Controls.KeyHandler.reset();

+ 48 - 0
app/World/Classes/Action/ActionRest.ts

@@ -0,0 +1,48 @@
+/// <reference path="../Action.ts" />
+/// <reference path="../Rule.ts" />
+/// <reference path="../Rulebook.ts" />
+/// <reference path="../Things/Person.ts" />
+class ActionRest extends Action {
+    public static check = new Rulebook<ActionRest>("Check Rest");
+    public static carry = new Rulebook<ActionRest>("Carry out Rest");
+
+    public constructor (actor : Thing, ...nouns : Array<any>) {
+        super(actor, ...nouns);
+        this.requiresNoun = true;
+        this.requiresVisibility = true;
+    }
+
+    public getCommandText () {
+        return "rest on " + (<Thing> this.getNoun(0)).getPrintedName();
+    }
+
+    public static carryRest = ActionRest.carry.createAndAddRule({
+        name : "Rest - Restful Moment",
+        code : (runner : RulebookRunner<ActionRest>) => {
+            let actor = runner.noun.actor;
+            if (actor instanceof Person) {
+                // TODO: Run the Rulebook responsible for healing on the person. Resting = 2x healing.
+                let action = runner.noun;
+                if (WorldState.isPlayer(actor)) {
+                    runner.noun.say.add("You decide to rest for a bit on ", Say.Mention(runner.noun.getNoun(0)), ".");
+                } else {
+                    runner.noun.say.add(Say.Mention(action.actor), " rests on ", Say.Mention(action.getNoun(0)), ".");
+                }
+            }
+        }
+    });
+}
+
+Elements.HyperlinkHandler.HyperlinkingRulebook.addRule(new Rule(
+    {
+        name : "Hyperlink - Rest",
+        firstPriority : Rule.PRIORITY_HIGHEST,
+        code : (rulebook : RulebookRunner<Thing>) => {
+            let thing = <Thing> rulebook.noun;
+
+            if (thing instanceof RestingStuff && thing.isVisibleTo(WorldState.player)) {
+                Elements.HyperlinkHandler.addAvailableAction("Rest", new ActionRest(WorldState.player, thing));
+            }
+        }
+    }
+));

+ 7 - 1
app/World/Classes/Action/ActionTalk.ts

@@ -29,7 +29,8 @@ class ActionTalk extends Action {
                 await thing.AI.answerTo({greeter : action.actor, answerer : thing, options : circumstantialOptions, runAndStop : runAndStop, runFirst : runAndContinue});
 
                 if (runAndStop.length > 0) {
-                    return await runAndStop[0].execute();
+                    await runAndStop[0].execute();
+                    return true; // must stop executing now
                 } else if (runAndContinue.length > 0) {
                     for (let i = 0; i < runAndContinue.length; i++) {
                         await runAndContinue[i].execute();
@@ -39,6 +40,9 @@ class ActionTalk extends Action {
                 let investigativeOptions : Array<DialogueHook> = [];
                 await thing.AI.interrogateTo({greeter : action.actor, answerer : thing, options : investigativeOptions, runAndStop : runAndStop, runFirst : runAndContinue});
 
+                if (investigativeOptions.length == 0 && circumstantialOptions.length == 0) {
+                    return;
+                }
 
                 let choices : Array<Say> = [];
                 let results = [];
@@ -73,6 +77,8 @@ class ActionTalk extends Action {
                 } else if (results[choice[1]] instanceof DialogueTree) {
                     await (results[choice[1]]).execute();
                 }
+
+                return true;
             }
         }
     });

+ 1 - 1
app/World/Classes/Dialogue/DialogueBranch.ts

@@ -1,7 +1,7 @@
 /// <reference path="DialogueNode.ts" />
 class DialogueBranch extends DialogueNode {
     public type = NodeType.Branch;
-    public variable : () => any = () => {return false;}
+    public variable : () => any = () => {return true;}
     public branchIds : Array<string> = [];
     public branchConditions : Array<() => any> = [];
 

+ 16 - 1
app/World/Classes/Dialogue/DialogueTree.ts

@@ -1,12 +1,27 @@
+/// <reference path="../Save/StoredVariable.ts" />
 class DialogueTree {
     public id : string;
     private nodes : {[id : string] : DialogueNode} = {};
     public startNode : DialogueNode = undefined;
     private repeatChoices : boolean = true;
     private lastPrintedChoice : Array<HTMLElement>;
+    private executedCount : StoredVariable<number>;
 
     public constructor (id : string) {
         this.id = id;
+        this.executedCount = new StoredVariable({id: "DialogueTree " + id, value : 0});
+    }
+
+    public hasRan () {
+        return this.executedCount.value > 0;
+    }
+
+    public ranTimes () {
+        return this.executedCount.value;
+    }
+
+    public incrementRanCount () {
+        this.executedCount.value = (this.ranTimes() + 1);
     }
 
     public addNode (node : DialogueNode) {
@@ -37,7 +52,7 @@ class DialogueTree {
     }
 
     public async execute (startId? : string) {
-
+        this.incrementRanCount();
         console.debug(Rulebook.getIndentation() + "[DialogueTree] Running " + this.id);
         Rulebook.increaseIndentation(this);
 

+ 40 - 1
app/World/Classes/Measure.ts

@@ -11,10 +11,16 @@ interface Measurement {
  *
  * If multiple measurements are added, it's treated as area of something simple like rectangles or cubes or whatever, they're just multiplied.
  */
-class Measure implements Measurement {
+enum MeasureUnitType {
+    CORRECT,
+    MURICAN
+}
+class Measure implements Measurement, Printable {
     private units : number;
     private sides : number;
 
+    public static unitType = new StoredMemory("UnitType", MeasureUnitType.MURICAN);
+
     public constructor (...sides : Array<number>) {
         this.units = 1;
         sides.forEach((side) => {
@@ -35,6 +41,35 @@ class Measure implements Measurement {
         }
     }
 
+    public getPrintedName () {
+        let unit = Measure.unitType.getValue();
+        if (unit == MeasureUnitType.CORRECT) {
+            let meters = Math.pow(this.sides, 100);
+            if (this.units > meters) {
+                return (+(this.units / meters).toFixed(2)).toString() + "m" + this.superscript[this.sides];
+            } else {
+                return this.units.toString() + "cm" + this.superscript[this.sides];
+            }
+        } else if (unit == MeasureUnitType.MURICAN) {
+            // Gotta say, although this looks awful, at least I don't have to worry about "when does inch becomes feet".
+            // In fact I liked it so much I'll actually make it the default.
+            let inches = Measure.toInches(this.units);
+            let feet = Math.floor(inches / 12);
+            inches = inches - (feet * 12);
+            inches = Math.floor(inches);
+            let text = [];
+            if (feet > 0) {
+                text.push(feet.toString() + "′");
+            }
+            if (inches > 0) {
+                text.push(inches.toString() + "″");
+            }
+            return text.join(" ");
+        } else {
+            return "INVALID"
+        }
+    }
+
     public getNumber () {
         return this.units;
     }
@@ -47,6 +82,10 @@ class Measure implements Measurement {
         return inches * 2.54;
     }
 
+    public static toInches (cm : number) {
+        return cm / 2.54;
+    }
+
     public static fromFeet (feet : number) {
         return feet * 30.48;
     }

+ 12 - 0
app/World/Classes/RandomDungeons/RandomCoordinateGenerator.ts

@@ -0,0 +1,12 @@
+class RandomCoordinateGenerator {
+    private definedCoordinates = [];
+
+    public generate (minX : number, maxX : number, minY : number, maxY : number) {
+        let coordinates : Array<number>;
+        do {
+            coordinates = [randomRange(minX, maxX), randomRange(minY, maxY)];
+        } while (this.definedCoordinates.includes(coordinates.join(";")));
+        this.definedCoordinates.push(coordinates.join(";"));
+        return coordinates;
+    }
+}

+ 6 - 1
app/World/Classes/RandomDungeons/RegionRandom.ts

@@ -29,6 +29,11 @@ class RegionRandom extends Region {
         }
     }
 
+    public placeAt (room : RoomRandom, x : number, y : number) {
+        this.map.map(room, x, y);
+        this.place(room);
+    }
+
     public static rng : () => number = () => { return Math.random(); };
 
     public static rulebookRandomizeRegion = new Rulebook<Region>("Randomizing Random Region something");
@@ -243,7 +248,7 @@ class RegionRandom extends Region {
                 }
             };
 
-            for (let fodderLevel = 1; fodderLevel < 10; fodderLevel++) {
+            for (let fodderLevel = 1; fodderLevel < 40; fodderLevel++) {
                 roomShuffler.restart();
                 for (let connectableRoom = roomShuffler.getOne(); connectableRoom != undefined; connectableRoom = roomShuffler.getOne()) {
                     let tricky = connectableThroughFodder(fodderLevel, connectableRoom);

+ 2 - 1
app/World/Classes/RandomDungeons/RoomRandom.ts

@@ -28,8 +28,9 @@ class RoomRandom extends Room {
 
     public lastMap : RoomRandomMap;
 
-    public constructor (id? : string, fodder? : boolean) {
+    public constructor (id? : string, fodder? : boolean, forbidden? : boolean) {
         super(id, fodder);
+        this.randomizable = forbidden != true;
     }
 
     public getBackgroundClass () {

+ 38 - 0
app/World/Classes/RandomDungeons/RoomRandomFodder.ts

@@ -4,8 +4,46 @@
  * 1 - It must be created as needed by a RegionRandom as it attempts to place Tricky rooms.
  * 2 - It doesn't count towards a player's maximum remembered rooms
  */
+enum RandomLootType {
+    DROPPED_ITEM,
+    CHEST
+}
+
+interface FodderPossibility {
+    name : string;
+    description : Say | string;
+    objects? : Array<Thing>; // Always has these objects
+    randomLootChance? : number; // 0 to 100, chance of having loot on top of the objects
+    randomLootType? : RandomLootType; // if randomLootChance succeeds, this will be the type of loot in the room
+    chestType? : Array<Thing | typeof Thing>; // if randomLootType is chest, chest will be generated using this
+    maxLootRarity? : number;
+    minLootRarity? : number; // Level of the random loot generated, from 1 to 10
+}
 class RoomRandomFodder extends RoomRandom {
     public constructor (id? : string) {
         super(id, true);
     }
+
+    public cloneInterface (pos : FodderPossibility) {
+        this.name = pos.name;
+        this.description = typeof pos.description == "string" ? new Say(pos.description) : pos.description;
+        if (pos.objects != undefined) {
+            for (let i = 0; i < pos.objects.length; i++) {
+                let object = pos.objects[i];
+                if (object instanceof Thing) {
+                    this.place(object);
+                } else if (object == Thing || (<any>object).prototype instanceof Thing) {
+                    this.place(new (<any>object)());
+                } else if (typeof object == "function") {
+                    object = (<Function> object)();
+                    if (object instanceof Thing) {
+                        this.place(object);
+                    }
+                }
+            }
+        }
+        if (pos.randomLootChance != undefined && (Math.random() * 100) <= pos.randomLootChance) {
+            // TODO: Add the loot.
+        }
+    }
 }

+ 2 - 0
app/World/Classes/RandomDungeons/RoomRandomMap.ts

@@ -133,6 +133,8 @@ class RoomRandomMap {
 
     public unmap (x : number, y : number) {
         if (this.positionTable[x] != undefined && this.positionTable[x][y] != undefined) {
+            let room = this.positionTable[x][y];
+            room.placed = false;
             this.roomMap.delete(this.positionTable[x][y]);
             delete (this.positionTable[x][y]);
             this.rooms--;

+ 2 - 1
app/World/Classes/Room.ts

@@ -49,10 +49,11 @@ var OppositeDirection : {[id : number] : Direction} = (() => {
 })();
 
 class Room implements Printable {
-    private name : string;
+    protected name : string;
     public connections : Array<Room>;
     public description : Say = new Say();
     public fodder : boolean;
+    public visited : boolean = false;
 
     public constructor (id? : string, fodder? : boolean) {
         this.name = id == undefined ? "Room" : id;

+ 40 - 26
app/World/Classes/Thing.ts

@@ -106,39 +106,46 @@ class Thing implements Printable {
 
         this.shiny = options.shiny == true;
 
+        // TODO: Make sure you never bring back a fodder Room - these disappear between games and we don't want to lose Things in them.
         this.addGetAlterations((thing : Thing) => {
-            function getClosestRoom (currentRoom : RoomRandom, rooms : Array<RoomRandom>) {
-                if (currentRoom instanceof RoomRandom && rooms.length > 0) {
-                    rooms.sort((a : RoomRandom, b : RoomRandom) => {
-                        if (!(a instanceof RoomRandom)) return -1;
-                        if (!(b instanceof RoomRandom)) return 1;
-                        let da = a.getDistanceTo(<RoomRandom> currentRoom);
-                        let db = b.getDistanceTo(<RoomRandom> currentRoom);
-                        return da - db;
-                    });
-                    return {
-                        Location : rooms[0].getName()
-                    }
-                }
-            }
-
             if (Thing.EnclosedRelation.getLeft(thing) == thing.getRoom() && thing.getRoom() != undefined) {
                 if (thing.getRoom().fodder) {
                     if (thing.isPlayer()) {
                         // put at closest remembered room
-                        let rooms = WorldState.getRememberedRoomsAsRooms();
-                        let currentRoom = thing.getRoom();
-                        return getClosestRoom(<RoomRandom> currentRoom, <Array<RoomRandom>> rooms);
+                        let rooms = WorldState.getRememberedRoomsAsRooms().filter((room : Room) => { return !(room instanceof RoomRandomFodder);});
+                        let room : Room;
+                        if (rooms.length <= 0) {
+                            // Find Closest visited room
+                            rooms = Region.InRelation.getLeft(thing.getRoom()).getRooms().filter(room => {return room.visited && !room.fodder && room.placed;}).sort((rooma, roomb) => {
+                                if (rooma instanceof RoomRandom && roomb instanceof RoomRandom) {
+                                    let da = rooma.getDistanceTo(<RoomRandom> thing.getRoom());
+                                    let db = roomb.getDistanceTo(<RoomRandom> thing.getRoom());
+                                    return da - db;
+                                }
+                                return 0;
+                            });
+
+                        }
+                        if (rooms.length > 0) {
+                            room = rooms[0]; // freshest room
+                        } else {
+                            room = PlayBegins.startingRoom; // back to square one
+                        }
+                        return {
+                            Location : room.getName()
+                        }
                     } else {
                         // put at closest room
-                        let rooms = thing.getRoom().getConnectedRooms();
-                        let currentRoom = thing.getRoom();
-                        let foundRoom = getClosestRoom(<RoomRandom> currentRoom, <Array<RoomRandom>> rooms);
-                        if (foundRoom != undefined) {
-                            return foundRoom;
-                        } else {
-                            rooms = (<Region> Region.InRelation.getLeft(thing.getRoom())).getRooms();
-                            return getClosestRoom(<RoomRandom> currentRoom, <Array<RoomRandom>> rooms);
+                        let rooms = Region.InRelation.getLeft(thing.getRoom()).getRooms().filter(room => {return !room.fodder && room.placed;}).sort((rooma, roomb) => {
+                            if (rooma instanceof RoomRandom && roomb instanceof RoomRandom) {
+                                let da = rooma.getDistanceTo(<RoomRandom> thing.getRoom());
+                                let db = roomb.getDistanceTo(<RoomRandom> thing.getRoom());
+                                return da - db;
+                            }
+                            return 0;
+                        });
+                        return {
+                            Location : rooms[0].getName()
                         }
                     }
                 } else {
@@ -295,6 +302,13 @@ class Thing implements Printable {
         }
     }
 
+    public getEnclosed (enclosedType? : any) {
+        if (enclosedType != undefined) {
+            return Thing.EnclosedRelation.getAllRightTypes(this, enclosedType);
+        }
+        return Thing.EnclosedRelation.getAllRight(this);
+    }
+
     public getWorns (wornType? : any) {
         if (wornType != undefined) {
             return Thing.WearRelation.getRightType(this, wornType);

+ 23 - 0
app/World/Classes/Things/Door.ts

@@ -0,0 +1,23 @@
+/// <reference path="../Thing.ts" />
+class Door extends Thing {
+    public locked = false;
+    public key : Thing;
+    public destination : Room;
+
+    constructor (name : string, destination : Room, unique = false) {
+        super({name : name, unique : unique == true});
+        this.destination = destination;
+        this.fixedInPlace = true;
+    }
+
+    public setBreakable (damage : number) {
+        this.breakableOn = damage;
+        this.breakable = true;
+        this.breaksTo = this;
+    }
+
+    public breakThrough () {
+        this.locked = false;
+        this.name = "Broken " + this.name;
+    }
+}

+ 7 - 0
app/World/Classes/Things/RestingStuff.ts

@@ -0,0 +1,7 @@
+/// <reference path="../Thing.ts" />
+class RestingStuff extends Thing {
+    constructor (name : string, description : Say | string) {
+        super({name : name, description : description});
+        this.fixedInPlace = true;
+    }
+}

+ 1 - 1
app/World/PlayBegins.ts

@@ -4,7 +4,7 @@
 module PlayBegins {
     export var rulebook = new Rulebook<void>("Play Begins");
     export var LOAD_FAILED = false;
-    let startingRoom;
+    export let startingRoom;
 
     export function execute () {
         rulebook.execute({});

+ 71 - 47
app/World/WorldState.ts

@@ -13,12 +13,12 @@ module WorldState {
 
     player.description = Humanoid.getPlayerDescription;
 
-    var rememberedRooms = new StoredVariable<Array<string>>({
+    var memoryRooms = new StoredVariable<Array<string>>({
         id : "Remembered Rooms",
         value : []
     });
 
-    var rememberedFodder : Array<Room> = [];
+    let allRememberedRooms : Array<Room> = [];
 
     let rememberedRoomsForIntelligence = 2;
     let rememberedRoomsForSurvival = 1;
@@ -51,7 +51,7 @@ module WorldState {
         firstPriority : Rule.PRIORITY_LOWEST,
         priority : Rule.PRIORITY_LOW,
         code : runner => {
-            if (rememberedRooms.value.indexOf(runner.noun.getName()) != -1 || rememberedFodder.indexOf(runner.noun) != -1) {
+            if (allRememberedRooms.includes(runner.noun)) {
                 return true;
             }
         }
@@ -71,23 +71,26 @@ module WorldState {
         }
     });
 
+    export function isPlayer (thing : any) {
+        return player === thing;
+    }
+
     export async function isRoomRemembered (room : Room) : Promise<boolean> {
+        if (room == WorldState.player.getRoom()) return true;
         let result = await RememberingRoomRulebook.execute({noun : room});
         return result == true; // can return "undefined"
     }
 
     export function getRememberedRooms () {
-        return rememberedRooms.value;
+        let roomNames = [];
+        allRememberedRooms.forEach(room => {
+            roomNames.push(room.getName());
+        });
+        return roomNames;
     }
 
     export function getRememberedRoomsAsRooms () : Array<Room> {
-        let rooms = [];
-        rememberedRooms.value.forEach(roomName => {
-            let room = Room.getRoom(roomName);
-            if (room != undefined) rooms.push(room);
-        });
-
-        return rooms;
+        return [...allRememberedRooms];
     }
 
     export function getMaximumRememberedRooms () {
@@ -97,25 +100,22 @@ module WorldState {
     }
 
     export function truncateRooms () {
-        while (rememberedRooms.value.length > getMaximumRememberedRooms()){
-            rememberedRooms.value.pop();
+        while (allRememberedRooms.length > getMaximumRememberedRooms()){
+            allRememberedRooms.pop();
         }
     }
 
     export function rememberRoom (...rooms : Array<Room>) {
         rooms.forEach((value) => {
             if (value instanceof Room) {
-                if (value.fodder) {
-                    if (rememberedFodder.indexOf(value) == -1) {
-                        rememberedFodder.push(value);
-                    }
+                let idx = allRememberedRooms.indexOf(value);
+                if (idx == -1) {
+                    allRememberedRooms.unshift(value);
                 } else {
-                    let idx = rememberedRooms.value.indexOf(value.getName());
-                    if (idx != -1) {
-                        rememberedRooms.value.splice(idx, 1);
-                    }
-                    rememberedRooms.value.unshift(value.getName());
+                    allRememberedRooms.splice(idx, 1);
+                    allRememberedRooms.unshift(value);
                 }
+                value.visited = true;
             }
         });
         truncateRooms();
@@ -138,32 +138,56 @@ module WorldState {
         firstPriority : Rule.PRIORITY_LOWEST,
         priority : Rule.PRIORITY_MEDIUM,
         code : () => {
-            let allRooms = rememberedRooms.value;
-            let currentRoom = WorldState.player.getRoom();
-            if (allRooms.length == 0) {
-                rememberedRooms.value = [currentRoom.getName()];
-                return;
+            if (memoryRooms.value.length > 0 && allRememberedRooms.length == 0) { // loading a save
+                memoryRooms.value.forEach(roomName => {
+                    let room = Room.getRoom(roomName);
+                    allRememberedRooms.push(room); // Already ordered
+                });
+                truncateRooms();
             }
-            let newRooms = [];
-
-            function recursivelyAddRooms (room : Room) {
-                if (room instanceof RoomRandom) {
-                    if (room.fodder) {
-                        rememberedFodder.push(room);
-                        room.connections.forEach((newRoom) => {
-                            recursivelyAddRooms(newRoom);
-                        });
-                    } else if (allRooms.indexOf(room.getName()) != -1 && newRooms.indexOf(room.getName()) == -1) {
-                        newRooms.push(room.getName());
-                        room.connections.forEach((newRoom) => {
-                            recursivelyAddRooms(newRoom);
-                        });
-                    }
-                }
+            if (!isRoomRemembered(WorldState.player.getRoom())) {
+                rememberRoom(WorldState.player.getRoom());
             }
-
-            recursivelyAddRooms(currentRoom);
-            rememberedRooms.value = newRooms;
+            let cleanRoomNames = [];
+            allRememberedRooms.forEach(room => {
+                if (!room.fodder) {
+                    cleanRoomNames.push(room.getName());
+                }
+            });
+            memoryRooms.value = cleanRoomNames;
+
+
+            // let allRooms = rememberedRooms.value;
+            // let currentRoom = WorldState.player.getRoom();
+            // if (allRooms.length == 0) {
+            //     rememberedRooms.value = [currentRoom.getName()];
+            //     return;
+            // }
+            // let newRooms = [];
+            //
+            // let tentativeFodder = [];
+            // function recursivelyAddRooms (room : Room) {
+            //     if (room instanceof RoomRandom) {
+            //         if (room.fodder && !tentativeFodder.includes(room)) {
+            //             tentativeFodder.push(room);
+            //             room.connections.forEach((newRoom) => {
+            //                 recursivelyAddRooms(newRoom);
+            //             });
+            //         } else if (allRooms.indexOf(room.getName()) != -1 && newRooms.indexOf(room.getName()) == -1) {
+            //             tentativeFodder.forEach(fodder => {
+            //                 rememberedFodder.push(fodder);
+            //             });
+            //             tentativeFodder = [];
+            //             newRooms.push(room.getName());
+            //             room.connections.forEach((newRoom) => {
+            //                 recursivelyAddRooms(newRoom);
+            //             });
+            //         }
+            //     }
+            // }
+            //
+            // recursivelyAddRooms(currentRoom);
+            // rememberedRooms.value = newRooms;
         }
     })
-}
+}

+ 5 - 5
content/CharacterCreation/SexStats.ts

@@ -166,12 +166,12 @@ module CharacterCreation {
             statsHeader.appendChild(document.createTextNode(")"));
 
             let setStat = (attr : Attribute, value : number, input : HTMLInputElement) => {
-                player.setStat(attr, value);
+                WorldState.player.setStat(attr, value);
                 let remaining = maxStats
-                    - player.getStat(Attributes.Strength)
-                    - player.getStat(Attributes.Agility)
-                    - player.getStat(Attributes.Intelligence)
-                    - player.getStat(Attributes.Charm);
+                    - WorldState.player.getStat(Attributes.Strength)
+                    - WorldState.player.getStat(Attributes.Agility)
+                    - WorldState.player.getStat(Attributes.Intelligence)
+                    - WorldState.player.getStat(Attributes.Charm);
 
                 if (remaining < 0) {
                     value += remaining;

+ 6 - 0
sass/text/_say.scss

@@ -71,6 +71,12 @@ p.choice {
   &.picked {
     color: $oldFontColor;
   }
+  &.small {
+    display: inline-block;
+    padding-right: 1rem;
+    margin: 0px;
+    margin-right: 0.5rem;
+  }
 }
 
 .combatChoicesContainer {

+ 161 - 9
tools/dialogger/dialogger.js

@@ -14,6 +14,7 @@ var con =
 		['dialogue.Text', 'dialogue.Function'],
 		['dialogue.Text', 'dialogue.Tree'],
 		['dialogue.Text', 'dialogue.GoToLabel'],
+		['dialogue.Text', 'dialogue.ConditionalBranch'],
 		['dialogue.Node', 'dialogue.Text'],
 		['dialogue.Node', 'dialogue.Node'],
 		['dialogue.Node', 'dialogue.Choice'],
@@ -22,6 +23,7 @@ var con =
 		['dialogue.Node', 'dialogue.Function'],
 		['dialogue.Node', 'dialogue.Tree'],
 		['dialogue.Node', 'dialogue.GoToLabel'],
+		['dialogue.Node', 'dialogue.ConditionalBranch'],
 		['dialogue.Tree', 'dialogue.Node'],
 		['dialogue.Tree', 'dialogue.Text'],
 		['dialogue.Tree', 'dialogue.Choice'],
@@ -30,6 +32,7 @@ var con =
 		['dialogue.Tree', 'dialogue.Function'],
 		['dialogue.Tree', 'dialogue.Tree'],
 		['dialogue.Tree', 'dialogue.GoToLabel'],
+		['dialogue.Tree', 'dialogue.ConditionalBranch'],
 		['dialogue.StartNode', 'dialogue.Text'],
 		['dialogue.StartNode', 'dialogue.Node'],
 		['dialogue.StartNode', 'dialogue.Choice'],
@@ -38,6 +41,7 @@ var con =
 		['dialogue.StartNode', 'dialogue.Function'],
 		['dialogue.StartNode', 'dialogue.Tree'],
 		['dialogue.StartNode', 'dialogue.GoToLabel'],
+		['dialogue.StartNode', 'dialogue.ConditionalBranch'],
 		['dialogue.Choice', 'dialogue.Text'],
 		['dialogue.Choice', 'dialogue.Node'],
 		['dialogue.Choice', 'dialogue.Set'],
@@ -45,6 +49,7 @@ var con =
 		['dialogue.Choice', 'dialogue.Function'],
 		['dialogue.Choice', 'dialogue.Tree'],
 		['dialogue.Choice', 'dialogue.GoToLabel'],
+		['dialogue.Choice', 'dialogue.ConditionalBranch'],
 		['dialogue.Set', 'dialogue.Text'],
 		['dialogue.Set', 'dialogue.Node'],
 		['dialogue.Set', 'dialogue.Set'],
@@ -52,6 +57,7 @@ var con =
 		['dialogue.Set', 'dialogue.Function'],
 		['dialogue.Set', 'dialogue.Tree'],
 		['dialogue.Set', 'dialogue.GoToLabel'],
+		['dialogue.Set', 'dialogue.ConditionalBranch'],
 		['dialogue.Function', 'dialogue.Text'],
 		['dialogue.Function', 'dialogue.Node'],
 		['dialogue.Function', 'dialogue.Set'],
@@ -59,6 +65,7 @@ var con =
 		['dialogue.Function', 'dialogue.Function'],
 		['dialogue.Function', 'dialogue.Tree'],
 		['dialogue.Function', 'dialogue.GoToLabel'],
+		['dialogue.Function', 'dialogue.ConditionalBranch'],
 		['dialogue.Branch', 'dialogue.Text'],
 		['dialogue.Branch', 'dialogue.Node'],
 		['dialogue.Branch', 'dialogue.Set'],
@@ -66,6 +73,15 @@ var con =
 		['dialogue.Branch', 'dialogue.Function'],
 		['dialogue.Branch', 'dialogue.Tree'],
 		['dialogue.Branch', 'dialogue.GoToLabel'],
+		['dialogue.Branch', 'dialogue.ConditionalBranch'],
+		['dialogue.ConditionalBranch', 'dialogue.Text'],
+		['dialogue.ConditionalBranch', 'dialogue.Node'],
+		['dialogue.ConditionalBranch', 'dialogue.Set'],
+		['dialogue.ConditionalBranch', 'dialogue.ConditionalBranch'],
+		['dialogue.ConditionalBranch', 'dialogue.Branch'],
+		['dialogue.ConditionalBranch', 'dialogue.Function'],
+		['dialogue.ConditionalBranch', 'dialogue.Tree'],
+		['dialogue.ConditionalBranch', 'dialogue.GoToLabel'],
 	],
 
 	default_link: new joint.dia.Link(
@@ -381,6 +397,112 @@ joint.shapes.dialogue.BranchView = joint.shapes.dialogue.BaseView.extend(
 	},
 });
 
+joint.shapes.dialogue.ConditionalBranch = joint.shapes.devs.Model.extend(
+	{
+		defaults: joint.util.deepSupplement
+		(
+			{
+				type: 'dialogue.ConditionalBranch',
+				size: { width: 200, height: 55, },
+				inPorts: ['input'],
+				outPorts: ['output0'],
+				values: [],
+			},
+			joint.shapes.dialogue.Base.prototype.defaults
+		),
+	});
+joint.shapes.dialogue.ConditionalBranchView = joint.shapes.dialogue.BaseView.extend(
+	{
+		template:
+			[
+				'<div class="node">',
+				'<span class="label"></span>',
+				'<button class="delete">x</button>',
+				'<button class="add">+</button>',
+				'<button class="remove">-</button>',
+				'<input type="text" value="Default" readonly/>',
+				'</div>',
+			].join(''),
+
+		initialize: function()
+		{
+			joint.shapes.dialogue.BaseView.prototype.initialize.apply(this, arguments);
+			this.$box.find('.add').on('click', _.bind(this.addPort, this));
+			this.$box.find('.remove').on('click', _.bind(this.removePort, this));
+		},
+
+		removePort: function()
+		{
+			if (this.model.get('outPorts').length > 1)
+			{
+				var outPorts = this.model.get('outPorts').slice(0);
+				outPorts.pop();
+				this.model.set('outPorts', outPorts);
+				var values = this.model.get('values').slice(0);
+				values.pop();
+				this.model.set('values', values);
+				this.updateSize();
+			}
+		},
+
+		addPort: function()
+		{
+			var outPorts = this.model.get('outPorts').slice(0);
+			outPorts.push('output' + outPorts.length.toString());
+			this.model.set('outPorts', outPorts);
+			var values = this.model.get('values').slice(0);
+			values.push(null);
+			this.model.set('values', values);
+			this.updateSize();
+		},
+
+		updateBox: function()
+		{
+			joint.shapes.dialogue.BaseView.prototype.updateBox.apply(this, arguments);
+			var values = this.model.get('values');
+			var valueFields = this.$box.find('input.value');
+
+			// Add value fields if necessary
+			for (var i = valueFields.length; i < values.length; i++)
+			{
+				// Prevent paper from handling pointerdown.
+				var $field = $('<input type="text" class="value" />');
+				$field.attr('placeholder', 'Condition ' + (i + 1).toString());
+				$field.attr('index', i);
+				this.$box.append($field);
+				$field.on('mousedown click', function(evt) { evt.stopPropagation(); });
+
+				// This is an example of reacting on the input change and storing the input data in the cell model.
+				$field.on('change', _.bind(function(evt)
+				{
+					var values = this.model.get('values').slice(0);
+					values[$(evt.target).attr('index')] = $(evt.target).val();
+					this.model.set('values', values);
+				}, this));
+			}
+
+			// Remove value fields if necessary
+			for (var i = values.length; i < valueFields.length; i++)
+				$(valueFields[i]).remove();
+
+			// Update value fields
+			valueFields = this.$box.find('input.value');
+			for (var i = 0; i < valueFields.length; i++)
+			{
+				var field = $(valueFields[i]);
+				if (!field.is(':focus'))
+					field.val(values[i]);
+			}
+		},
+
+		updateSize: function()
+		{
+			var textField = this.$box.find('input');
+			var height = textField.outerHeight(true);
+			this.model.set('size', { width: 200, height: 95 + Math.max(0, (this.model.get('outPorts').length - 1) * height) });
+		},
+	});
+
 
 joint.shapes.dialogue.StartNode = joint.shapes.devs.Model.extend(
 {
@@ -390,7 +512,7 @@ joint.shapes.dialogue.StartNode = joint.shapes.devs.Model.extend(
 			type: 'dialogue.StartNode',
 			inPorts: [],
 			outPorts: ['output'],
-			size: { width: 100, height: 30, },
+			size: { width: 100, height: 32, },
 			value: 'DialogueNode.START',
             attrs:
                 {
@@ -437,7 +559,7 @@ joint.shapes.dialogue.Choice = joint.shapes.devs.Model.extend(
 			type: 'dialogue.Choice',
 			inPorts: ['input'],
 			outPorts: ['output'],
-			size: { width: 175, height: 95, },
+			size: { width: 175, height: 93, },
 			value: '',
 		},
 		joint.shapes.dialogue.Base.prototype.defaults
@@ -531,7 +653,7 @@ joint.shapes.dialogue.AIHook = joint.shapes.devs.Model.extend(
 				type: 'dialogue.AIHook',
 				inPorts: [],
 				outPorts: [],
-				size: { width: 300, height: 124, },
+				size: { width: 300, height: 126, },
 				value: '',
 			},
 			joint.shapes.dialogue.Base.prototype.defaults
@@ -545,7 +667,13 @@ joint.shapes.dialogue.AIHookView = joint.shapes.dialogue.BaseView.extend(
 				'<div class="node">',
 				'<span class="label"></span>',
 				'<button class="delete">x</button>',
-				'<input type="text" class="hooktype" placeholder="AskAbout/Circumstance/Informative/Critical" />',
+				'<select class="hooktype">',
+					'<option value="" selected disabled hidden>Choose Type</option>',
+					'<option value="askabout">Investigate Option</option>',
+					'<option value="circumstance">Common Dialogue Option</option>',
+					'<option value="informative">Pre-Dialogue Dialogue</option>',
+					'<option value="critical">Override Dialogue</option>',
+				'</select>',
 				'<input type="text" class="identifier" placeholder="Selection Text" />',
 				'<input type="text" class="conditions" placeholder="Conditions (greeter, answerer)" />',
 				'</div>',
@@ -558,7 +686,7 @@ joint.shapes.dialogue.AIHookView = joint.shapes.dialogue.BaseView.extend(
 			{
 				this.model.set('conditions', $(evt.target).val());
 			}, this));
-			this.$box.find('input.hooktype').on('change', _.bind(function(evt)
+			this.$box.find('select.hooktype').on('change', _.bind(function(evt)
 			{
 				this.model.set('hooktype', $(evt.target).val());
 			}, this));
@@ -571,7 +699,7 @@ joint.shapes.dialogue.AIHookView = joint.shapes.dialogue.BaseView.extend(
 		updateBox: function()
 		{
 			joint.shapes.dialogue.BaseView.prototype.updateBox.apply(this, arguments);
-			var field = this.$box.find('input.hooktype');
+			var field = this.$box.find('select.hooktype');
 			if (!field.is(':focus'))
 				field.val(this.model.get('hooktype'));
 			var field = this.$box.find('input.conditions');
@@ -686,6 +814,15 @@ func.optimized_data = function()
 					node.branches[branch] = null;
 				}
 			}
+			else if (node.type === 'ConditionalBranch')
+			{
+				node.branches = {};
+				for (var j = 0; j < cell.values.length; j++)
+				{
+					var branch = cell.values[j];
+					node.branches[branch] = null;
+				}
+			}
 			else if (node.type === 'Set')
 			{
 				node.variable = cell.name;
@@ -952,12 +1089,26 @@ func.ts_data = function(nodes, name) {
 			}
 			finalString += "tree.addNode(branch);\n";
 			addDeclaration("let branch : DialogueBranch;\n");
+		} else if (node.type == "ConditionalBranch") {
+			finalString += "branch = new DialogueBranch(" + JSON.stringify(node.id) + ");\n";
+			//finalString += "branch.setVariable(() => { return " + node.variable + ";});\n";
+			for (let key in node.branches) {
+				let branchCondition = key;
+				let branchTarget = node.branches[key];
+				if (branchCondition == "_default") {
+					finalString += "branch.setNext(" + JSON.stringify(branchTarget) + ");\n";
+				} else {
+					finalString += "branch.addBranch(" + JSON.stringify(branchTarget) + ", () => { return " + branchCondition + ";});\n";
+				}
+			}
+			finalString += "tree.addNode(branch);\n";
+			addDeclaration("let branch : DialogueBranch;\n");
 		} else if (node.type == "Set" || node.type == "Function") {
 			finalString += "set = new DialogueSet(" + JSON.stringify(node.id) + ");\n";
 			if (node.type == "Function") {
-				finalString += "set.setFunction(() => {" + node.name + "});\n";
+				finalString += "set.setFunction(() => {\n" + node.name + "\n});\n";
 			} else {
-				finalString += "set.setFunction(() => { " + node.variable + " = " + node.value + ";});\n";
+				finalString += "set.setFunction(() => {\n " + node.variable + " = " + node.value + ";\n});\n";
 			}
 			if (node.next != null && node.next != undefined) {
 				finalString += "set.setNext(" + JSON.stringify(node.next) + ");\n";
@@ -1142,7 +1293,8 @@ func.ts_data = function(nodes, name) {
 	state.menu = new nw.Menu();
 	state.menu.append(new nw.MenuItem({ label: 'Text', click: func.add_node(joint.shapes.dialogue.Text) }));
 	state.menu.append(new nw.MenuItem({ label: 'Choice', click: func.add_node(joint.shapes.dialogue.Choice) }));
-	state.menu.append(new nw.MenuItem({ label: 'Branch', click: func.add_node(joint.shapes.dialogue.Branch) }));
+	state.menu.append(new nw.MenuItem({ label: 'Switch', click: func.add_node(joint.shapes.dialogue.Branch) }));
+	state.menu.append(new nw.MenuItem({ label: 'Branch', click: func.add_node(joint.shapes.dialogue.ConditionalBranch) }));
 	state.menu.append(new nw.MenuItem({ label: 'Set', click: func.add_node(joint.shapes.dialogue.Set) }));
 	state.menu.append(new nw.MenuItem({ label: 'Function', click: func.add_node(joint.shapes.dialogue.Function) }));
 	state.menu.append(new nw.MenuItem({ label: 'GoToLabel', click: func.add_node(joint.shapes.dialogue.GoToLabel) }));

+ 1 - 0
tools/dialogger/index.css

@@ -146,6 +146,7 @@ Node styles
 	box-sizing: border-box;
 	z-index: 2;
 	height: auto !important;
+	border: solid 1px #000;
 }
 
 .node.StartNode {

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
tools/dialogger/lib/joint.shapes.devs.min.js


Nem az összes módosított fájl került megjelenítésre, mert túl sok fájl változott