Reddo 5 лет назад
Родитель
Сommit
8b4d137793

+ 3 - 2
app/World/Classes/ContentPicker/Fucking/FuckingMarker.ts

@@ -2,7 +2,8 @@
 /**
  * CHEAT SHEET FOR FUCKING MARKERS
  *
- * Mandatory if present - These may all appear at once or separatedly! Separated is always rarer, so you can get away with less descriptions (Most enemies will start and finish in a single turn)
+ * Mandatory if present -   These may all appear at once or separatedly! Separated is always rarer, so you can get away with less descriptions (Most enemies will start and finish in a single turn)
+ *                          CUM_END will always appear together with Cumming.
  * FuckingState.CUM_START
  * FuckingState.CUMMING
  * FuckingState.CUM_END
@@ -29,7 +30,7 @@
  *
  * Examples:
  * {
- *     Fucking Unit: Orc is Fucker, Player is Fuckee, Orc Penis is Stick, Player Vagina is Hole, Markers: FuckingStyle.GENTLE - FuckeePosition.STANDING - FuckerPosition.STANDINGFRONT
+ *     Fucking Unit: Orc is Fucker, Player is Fuckee, Orc Penis is Stick, Player Vagina is Hole, Markers: FuckingStyle.GENTLE - FuckeePosition.STANDING
  * }
  */
 

+ 1 - 1
app/World/Settings.ts

@@ -3,7 +3,7 @@ module Settings {
     var debugEmpty = () => {};
 
     export var hardDebug = false;
-    export var sayTurnTime = false;
+    export var sayTurnTime = true;
 
     export function setDebug (isDebug : boolean) {
         if (isDebug) {

+ 1 - 2
config.rb

@@ -13,14 +13,13 @@ http_generated_images_path  = "images"
 # You can select your preferred output style here (can be overridden via the command line):
 # output_style = :expanded or :nested or :compact or :compressed
 #output_style = :compressed
-output_style = :expanded
+output_style = :compressed
 
 # To enable relative paths to assets via compass helper functions. Uncomment:
 #relative_assets = false
 
 # To disable debugging comments that display the original location of your selectors. Uncomment:
 line_comments = true
-line_comments = false
 
 
 # If you prefer the indented syntax, you might want to regenerate this

+ 32 - 0
content/ContentPicker/MinotaurGuard.ts

@@ -0,0 +1,32 @@
+///<reference path="../Rooms/Forest/Maze/People/MinotaurGuard.ts"/>
+(new CombatDescription("Minotaur Attacks"))
+    .setDescriptionFunction((actor, target, weapons, markers) => {
+        let say = new Say();
+
+        if (markers.includes(CombatHit.MISS)) {
+            say.add("The minotaur heaves his heavy punches at you, but you narrowly avoid it.");
+        } else if (markers.includes(CombatHit.HIT)) {
+            say.add("The minotaur punches you with ease.");
+        } else if (markers.includes(CombatHit.CRITICAL)) {
+            say.add("The minotaur punches you in the gut. Ouch, you felt that HARD.");
+        }
+
+        say.add(" ");
+
+        if (markers.includes(CombatResult.KILLED)) {
+            say.add("YOU ARE DEAD.");
+        } else if (markers.includes(CombatResult.KNOCKED_OFF)) {
+            say.add("You lose consciousness.");
+        } else if (markers.includes(CombatResult.KNOCKED)) {
+            say.add("You are knocked to the ground!");
+        }
+
+        return say;
+    })
+    .addUnit()
+    .setActor(MinotaurGuard)
+    .setTarget(WorldState.player)
+    .setWeapon(Thing)
+    .addMarker(AdaptiveDifferential.FULLYADAPTIVE(CombatHit.MISS, CombatHit.HIT, CombatHit.CRITICAL))
+    .addMarker(AdaptiveDifferential.FULLYADAPTIVE(CombatResult.KNOCKED, CombatResult.KILLED, CombatResult.KNOCKED_OFF))
+;

+ 6 - 0
content/Rooms/Forest.ts

@@ -0,0 +1,6 @@
+///<reference path="Forest/Classes/ForestFodder.ts"/>
+module Forest {
+    export let region = new RegionRandom("The Forest");
+    region.fodderRoomClass = ForestFodder;
+    export let rnc = new RandomCoordinateGenerator();
+}

+ 62 - 0
content/Rooms/Forest/CentaurVillage.ts

@@ -0,0 +1,62 @@
+/// <reference path="../Forest.ts" />
+import CentaurCoordinates = Forest.CentaurCoordinates;
+
+module Forest{
+    export let CentaurCoordinates = Forest.rnc.generate(-4,-3, 1, 4);
+
+    export let CentaurVillageSouthwestWall = new RoomRandom("Around the Centaur Village - Southwest", false, true);
+    export let CentaurVillageNorthwestWall = new RoomRandom("The Centaur Village - North Gates", false, true);
+    export let CentaurVillageWestWall = new RoomRandom("Around the Centaur Village - West", false, true);
+    export let CentaurVillageNorthWall = new RoomRandom("Around the Centaur Village - North", false, true);
+    export let CentaurVillageNortheastWall = new RoomRandom("Around the Centaur Village - Northeast", false, true);
+    export let CentaurVillageEastWall = new RoomRandom("Around the Centaur Village - East", false, true);
+    export let CentaurVillageSoutheastGates = new RoomRandom("The Centaur Village - Eastern Gates", false, true);
+    export let CentaurVillageSouthWall = new RoomRandom("Around the Centaur Village - South", false, true);
+
+    CentaurVillageSouthwestWall.mapRoom(CentaurVillageWestWall, Direction.NORTH);
+    CentaurVillageWestWall.mapRoom(CentaurVillageNorthwestWall, Direction.NORTH);
+    CentaurVillageNorthwestWall.mapRoom(CentaurVillageNorthWall, Direction.EAST);
+    CentaurVillageNorthWall.mapRoom(CentaurVillageNortheastWall, Direction.EAST);
+    CentaurVillageNortheastWall.mapRoom(CentaurVillageEastWall, Direction.SOUTH);
+    CentaurVillageEastWall.mapRoom(CentaurVillageSoutheastGates, Direction.SOUTH);
+    CentaurVillageSoutheastGates.mapRoom(CentaurVillageSouthWall, Direction.WEST);
+    CentaurVillageSouthWall.mapRoom(CentaurVillageSouthwestWall, Direction.WEST);
+
+    export let CentaurVillageWalls = [CentaurVillageSouthwestWall, CentaurVillageWestWall, CentaurVillageNorthWall, CentaurVillageNortheastWall, CentaurVillageEastWall, CentaurVillageNorthwestWall, CentaurVillageSouthWall];
+}
+Forest.region.place(Forest.CentaurVillageSoutheastGates);
+Forest.CentaurVillageSoutheastGates.backgroundImage = "roomCentaurVillage";
+Forest.CentaurVillageWalls.forEach(wall => {
+    Forest.region.place(wall);
+    wall.backgroundImage = "roomCentaurVillage";
+});
+
+
+RegionRandom.rulebookAfterPlaceRoom.addRule(new Rule({
+    name : "After placing the Forest.CentaurVillageSoutheastGates",
+    code : (runner : RulebookRunner<RandomizingRoomOptions>) => {
+        // Place the rest of the village
+        let map = runner.noun.map;
+        let southeast = map.getCoordinates(runner.noun.room);
+        let center = [southeast[0] - 1, southeast[1] + 1];
+        map.map(Forest.CentaurVillageNorthwestWall, center[0] -1 , center[1] + 1) ;
+        map.map(Forest.CentaurVillageNorthWall, center[0], center[1] + 1) ;
+        map.map(Forest.CentaurVillageNortheastWall, center[0] + 1, center[1] + 1) ;
+        map.map(Forest.CentaurVillageWestWall, center[0] - 1, center[1]);
+        map.block(center[0], center[1]);
+        map.map(Forest.CentaurVillageEastWall, center[0] + 1, center[1]) ;
+        map.map(Forest.CentaurVillageSouthWall, center[0], center[1] - 1) ;
+        map.map(Forest.CentaurVillageSouthwestWall, center[0] - 1, center[1] - 1);
+    },
+    conditions : (runner : RulebookRunner<RandomizingRoomOptions>) => {
+        return runner.noun.room == Forest.CentaurVillageSoutheastGates;
+    }
+}));
+
+Forest.CentaurVillageSoutheastGates.trickyCode = (options : TrickyOptions) => {
+    while (!options.map.isFreeSquare(Forest.CentaurCoordinates[0] - 1, Forest.CentaurCoordinates[1] + 1, 1)) {
+        Forest.CentaurCoordinates[0] -= 1;
+    }
+
+    return options.x == Forest.CentaurCoordinates[0] && options.y == Forest.CentaurCoordinates[1];
+};

+ 132 - 0
content/Rooms/Forest/Classes/ForestFodder.ts

@@ -0,0 +1,132 @@
+class ForestFodder extends RoomRandomFodder {
+    constructor () {
+        super();
+        //this.cloneInterface(ForestFodder.POSSIBILITIES.getOne());
+        this.backgroundImage = "roomForestFodder";
+    }
+
+    public static beautified : Array<ForestFodder> = [];
+    // This defines the important rooms first
+    public static definedRooms (rooms : Array<ForestFodder>) {
+        let plainifyAround = [
+            [Forest.CentaurCoordinates[0] - 1, Forest.CentaurCoordinates[1] + 1, 5, Forest.CentaurCoordinates],
+            [Forest.OrcCoordinates[0] + 1, Forest.OrcCoordinates[1] + 1, 5, Forest.OrcCoordinates]
+        ];
+
+        plainifyAround.forEach((coordinates) => {
+            let availRooms = rooms.filter(room => {
+                return !ForestFodder.beautified.includes(room) && room instanceof ForestFodder && room.getDistanceToCoordinates(<any> coordinates) <= coordinates[2];
+            });
+            availRooms.forEach((room) => {
+                ForestFodder.beautified.push(room);
+                room.cloneInterface(ForestFodder.PlainsPossibility);
+                if (coordinates[3] == Forest.CentaurCoordinates) {
+                    let directionTo = room.getOverallDirectionTo(coordinates);
+                    room.description.add("You can see a settlement with wooden walls to the " + DirectionNames[directionTo] + ".");
+                }
+                if (coordinates[3] == Forest.OrcCoordinates) {
+                    let directionTo = room.getOverallDirectionTo(coordinates);
+                    room.description.add("You can see a fortified settlement with spiked defensive walls to the " + DirectionNames[directionTo] + ".");
+                }
+                room.extraConnectionChance = 100;
+            });
+        });
+    }
+
+    // This makes the paths and mustbe done AFTER the extra connections happen
+    public static makePaths (rooms : Array<ForestFodder>) {
+        // initial rules:
+        // There is a beaten path from the Witch Hut to the Centaur Village
+        // There will be a beaten path from the Orc Village to the Ominous Cave.
+        let beatenPath : Array<ForestFodder> = [];
+        let destinations : Array<Room> = [Forest.WitchHut, Forest.CentaurVillageSoutheastGates];
+        function makePath (start, end) {
+            let pathTo = start.getAStarPathTo(end);
+            pathTo.forEach((path) => {
+                let room = path.room;
+                if (room instanceof ForestFodder && !beatenPath.includes(room)) {
+                    beatenPath.push(room);
+                }
+            });
+            if (!destinations.includes(start)) destinations.push(start);
+            if (!destinations.includes(end)) destinations.push(end);
+        }
+
+        makePath(Forest.WitchHut, Forest.CentaurVillageSoutheastGates);
+        makePath(Forest.WitchHut, Forest.MazeEntrance);
+        makePath(Forest.OrcVillageNorthwestGates, Forest.OminousCave);
+        // makePath(Forest.OrcVillageSouthGates, Forest.Pond);
+        // makePath(Forest.CentaurVillageSoutheastGates, Forest.Pond);
+
+        beatenPath.forEach(room => {
+            room.cloneInterface(ForestFodder.BeatenPathPossibility);
+            if (!ForestFodder.beautified.includes(room)) ForestFodder.beautified.push(room);
+            let pathFollows = "The path continues ";
+            let directions = [];
+            room.connections.forEach((room : RoomRandom, direction : Direction) => {
+                if (beatenPath.includes(<any> room) || destinations.includes(room)) {
+                    directions.push(DirectionNames[direction].toLowerCase());
+                }
+            });
+            for (let i = 0; i < directions.length; i++) {
+                if ((i + 1) == directions.length) {
+                    pathFollows += " and ";
+                }
+                pathFollows += directions[i];
+                if ((i + 1) < directions.length && directions.length > 2) {
+                    pathFollows += ", ";
+                }
+            }
+            pathFollows += ".";
+            room.description.add(pathFollows);
+        });
+    }
+
+    // Last resort to find purpose for rooms.
+    public static panicModeBeautify (rooms : Array<ForestFodder>) {
+        // Give purpose to rooms without purpose
+        let realFodder = 0;
+        rooms.forEach(room => {
+            if (!ForestFodder.beautified.includes(room)) {
+                room.cloneInterface(ForestFodder.POSSIBILITIES.getOne());
+                realFodder +=1;
+            }
+        });
+        console.log(realFodder.toString() + " rooms were left without real purpose, out of " + rooms.length.toString() + " rooms.");
+    }
+
+    public static POSSIBILITIES : OneOf = new OneOf(OneOf.ROTATING_RANDOM);
+    public static addPossibility (...pos : Array<FodderPossibility>) {
+        ForestFodder.POSSIBILITIES.addPossibilities(...pos);
+    }
+
+    public static BeatenPathPossibility : FodderPossibility = {
+        name : "Beaten path",
+        description: "You are in a beaten path through the woods. "
+    };
+
+    public static PlainsPossibility : FodderPossibility = {
+        name : "Plains",
+        description : "The woods have mostly cleared up, leaving you in open fields with lush green grass. "
+    }
+}
+
+ForestFodder.addPossibility({
+    name : "Clearing in the Woods",
+    description : new Say("The trees have cleared around here and you can finally see something other than leaves up ahead, even though the lush green plains leave you exposed.")
+});
+
+ForestFodder.addPossibility({
+    name : "Dense Forest",
+    description : new Say("There are so many trees around that you can barely see where you are going, the floor is littered with sticks and leaves.")
+});
+
+ForestFodder.addPossibility({
+    name : "Flowery Plains",
+    description : new Say("This colorful area has dozens of types of flowers all around.")
+});
+
+ForestFodder.addPossibility({
+    name : "Pond",
+    description : new Say("The forest opens up to a crystalline pond.")
+});

+ 31 - 0
content/Rooms/Forest/ForestBed.ts

@@ -0,0 +1,31 @@
+/// <reference path="../Forest.ts" />
+module Forest{
+    export let ForestBed = new RoomRandom("Forest Bed", false, true);
+    ForestBed.description = new Say(
+        "A circle made of dead leaves rests in the middle of a forest dense with trees. ",
+        "The ground surrounding the circle is littered with sticks, stones and other leaves, but the circle is made exclusively out of dead leaves, placed in such a way that it could serve as a makeshift bed. ",
+        "This \"bed\" was where you first woke up, with no exact memory of how you got here. ",
+        "You can barely make out the Obelisk through the trees - it's a long ways north, but the sleek, black tower can still be seen from this far. "
+    );
+    ForestBed.place(new RestingStuff("Leafbed", new Say("A bunch of dead leaves spread in a circle. There's quite a lot of them here, this could be used as a bed.")));
+    ForestBed.connectableOn = [Direction.NORTH];
+    ForestBed.backgroundImage = "roomForestBed";
+    ForestBed.place(new Thing({name: "Rock"}));
+    ForestBed.place(new Thing({name: "Rock"}));
+    ForestBed.place(new Thing({name: "Rock"}));
+    ForestBed.place(new Thing({name: "Rock"}));
+    ForestBed.place(new Thing({name: "Rock"}));
+    ForestBed.place(new Thing({name: "Rock"}));
+    ForestBed.place(new Thing({name: "Rock"}));
+    ForestBed.place(new Thing({name: "Rock"}));
+    ForestBed.place(new Thing({name: "Rock"}));
+    ForestBed.place(new Thing({name: "Rock"}));
+}
+
+let mapOfTest = new MapNote({name: "Map of the Forest",
+description : "This is a simple map showing all the rooms here.", unique : true});
+mapOfTest.addRegion(Forest.region);
+WorldState.player.setCarried(mapOfTest);
+
+PlayBegins.setStartingRoom(Forest.ForestBed);
+Forest.region.place(Forest.ForestBed);

+ 44 - 0
content/Rooms/Forest/Maze/People/MinotaurGuard.ts

@@ -0,0 +1,44 @@
+class MinotaurGuard extends Humanoid {
+    constructor () {
+        super({
+            isMale : true,
+            name : "Minotaur",
+            description : new Say(
+                "This gigantic man is entirely covered with brown fur and has the head of a bull, horns and all. ",
+                "Standing tall at over ", new Measure(250), " tall and packing an unbelievable amount of muscle, this is obviously someone not to be messed with. ",
+                "The only clothing he wears, if it can be called that, is a black, leather loincloth, which only covers the front — his bull-like tail hanging freely.",
+                Say.LINE_BREAK,
+                "He wields an axe even taller than himself and holds a serious face, yet there is a tinge of kindness in his eyes."
+            ),
+            unique: true
+        });
+
+        this.setStat(Attributes.Strength, 5);
+        this.setStat(Attributes.Agility, 2);
+        this.setStat(Attributes.Intelligence, 4);
+        this.setStat(Attributes.Charm, 3);
+        this.setGenderValue(0);
+        this.AI.wanderer = false;
+
+        let myLoincloth = new FrontalLeatherLoincloth();
+        this.setWorn(myLoincloth);
+    }
+
+    public static AgreedToDuel = new StoredVariable({id: "Agreed to duel with the minotaur", value: false});
+    public static BestedMinotaur = new StoredVariable({id: "Bested the Minotaur", value: false});
+    public static AgreedToSex = new StoredVariable({id: "Agreed to Ride the Bull", value: false});
+
+    public static isMadeNoChoice() {
+        return !MinotaurGuard.AgreedToDuel.value && !MinotaurGuard.AgreedToSex.value && !MinotaurGuard.BestedMinotaur.value;
+    }
+}
+//paddedBra = new Clothing({name : "Padded Bra", unique : true});
+// paddedBra.breastPadding = 3;
+// paddedBra.slots = [Humanoid.SLOT_BREASTS];
+class FrontalLeatherLoincloth extends Clothing {
+    constructor() {
+        super({name : "Frontal Leather Loincloth", description: new Say("This is a black leather loincloth that ties around your waist, but only has frontal cover.")});
+        this.slots = [Humanoid.SLOT_CROTCH_FRONT, Humanoid.SLOT_HIPS];
+        this.sluttinessValue = 25;
+    }
+}

+ 27 - 0
content/Rooms/Forest/MazeEntrance.ts

@@ -0,0 +1,27 @@
+/// <reference path="../Forest.ts" />
+/// <reference path="Maze/People/MinotaurGuard.ts" />
+module Forest{
+    export let MazeEntrance = new RoomRandom("Ancient Crypt", false, true);
+    export let MazeCoordinates = Forest.rnc.generate(-3,0, 3, 6);
+
+    MazeEntrance.description = new Say(
+        "These fields are clear of trees, but you can see what appears to be a small rock crypt with two stone pillars to the sides of its entrance. ",
+        "The crypt appears ancient: there is moss growing on it and most of the rocks that make it are chipped or broken. ",
+        "Yet, it stands. There is a writing on the slab on top of it, but between the moss and the weird symbols used to write it, you can't imagine what it means."
+    );
+    MazeEntrance.backgroundImage = "roomCrypt";
+
+    export let MazeMinotaurGuard = new MinotaurGuard();
+
+    MazeEntrance.place(MazeMinotaurGuard);
+}
+Forest.region.place(Forest.MazeEntrance);
+
+Forest.MazeEntrance.trickyCode = (options : TrickyOptions) => {
+    let blocked = !options.map.isFree(Forest.MazeCoordinates[0], Forest.MazeCoordinates[1]);
+    while (blocked) {
+        Forest.MazeCoordinates[0] -= 1;
+        blocked = !options.map.isFree(Forest.MazeCoordinates[0], Forest.MazeCoordinates[1]);
+    }
+    return options.x == Forest.MazeCoordinates[0] && options.y == Forest.MazeCoordinates[1];
+};

+ 18 - 0
content/Rooms/Forest/OminousCave.ts

@@ -0,0 +1,18 @@
+/// <reference path="../Forest.ts" />
+/// <reference path="OrcVillage.ts" />
+module Forest{
+    export let OminousCave = new RoomRandom("Ominous Cave", false, true);
+    export let CaveCoordinates = Forest.rnc.generate(Forest.OrcCoordinates[0],Forest.OrcCoordinates[0] + 2, Forest.OrcCoordinates[0] + 3, Forest.OrcCoordinates[0] + 5);
+    OminousCave.backgroundImage = "roomOminousCave";
+    OminousCave.connectableOn = [Direction.SOUTH];
+}
+Forest.region.place(Forest.OminousCave);
+
+Forest.OminousCave.trickyCode = (options : TrickyOptions) => {
+    let blocked = !options.map.isFree(Forest.CaveCoordinates[0], Forest.CaveCoordinates[1]);
+    while (blocked) {
+        Forest.CaveCoordinates[1] += 1;
+        blocked = !options.map.isFree(Forest.CaveCoordinates[0], Forest.CaveCoordinates[1]);
+    }
+    return options.x == Forest.CaveCoordinates[0] && options.y == Forest.CaveCoordinates[1];
+};

+ 62 - 0
content/Rooms/Forest/OrcVillage.ts

@@ -0,0 +1,62 @@
+/// <reference path="../Forest.ts" />
+module Forest{
+    export let OrcCoordinates = Forest.rnc.generate(3,4, 2, 4);
+
+    export let OrcVillageSouthGates = new RoomRandom("The Orc Tribe - South Gates", false, true);
+    export let OrcVillageNorthwestGates = new RoomRandom("The Orc Tribe - North Gates", false, true);
+    export let OrcVillageWestWall = new RoomRandom("Around the Orc Tribe - West", false, true);
+    export let OrcVillageNorthWall = new RoomRandom("Around the Orc Tribe - North", false, true);
+    export let OrcVillageNortheastWall = new RoomRandom("Around the Orc Tribe - Northeast", false, true);
+    export let OrcVillageEastWall = new RoomRandom("Around the Orc Tribe - East", false, true);
+    export let OrcVillageSoutheastWall = new RoomRandom("Around the Orc Tribe - Southeast", false, true);
+    export let OrcVillageSouthWall = new RoomRandom("Around the Orc Tribe - South", false, true);
+
+    OrcVillageSouthGates.mapRoom(OrcVillageWestWall, Direction.NORTH);
+    OrcVillageWestWall.mapRoom(OrcVillageNorthwestGates, Direction.NORTH);
+    OrcVillageNorthwestGates.mapRoom(OrcVillageNorthWall, Direction.EAST);
+    OrcVillageNorthWall.mapRoom(OrcVillageNortheastWall, Direction.EAST);
+    OrcVillageNortheastWall.mapRoom(OrcVillageEastWall, Direction.SOUTH);
+    OrcVillageEastWall.mapRoom(OrcVillageSoutheastWall, Direction.SOUTH);
+    OrcVillageSoutheastWall.mapRoom(OrcVillageSouthWall, Direction.WEST);
+    OrcVillageSouthWall.mapRoom(OrcVillageSouthGates, Direction.WEST);
+
+    export let OrcVillageWalls = [OrcVillageWestWall, OrcVillageNorthWall, OrcVillageNortheastWall, OrcVillageEastWall, OrcVillageSoutheastWall, OrcVillageSouthWall];
+}
+Forest.region.place(Forest.OrcVillageSouthGates);
+Forest.region.place(Forest.OrcVillageNorthwestGates);
+Forest.OrcVillageSouthGates.backgroundImage = "roomOrcVillage";
+Forest.OrcVillageNorthwestGates.backgroundImage = "roomOrcVillage";
+Forest.OrcVillageWalls.forEach(wall => {
+    Forest.region.place(wall);
+    wall.backgroundImage = "roomOrcVillage";
+});
+
+
+RegionRandom.rulebookAfterPlaceRoom.addRule(new Rule({
+    name : "After placing the Forest.OrcVillageSouthGates",
+    code : (runner : RulebookRunner<RandomizingRoomOptions>) => {
+        // Place the rest of the village
+        let map = runner.noun.map;
+        let southwest = map.getCoordinates(runner.noun.room);
+        let center = [southwest[0] + 1, southwest[1] + 1];
+        map.map(Forest.OrcVillageNorthwestGates, center[0] -1 , center[1] + 1) ;
+        map.map(Forest.OrcVillageNorthWall, center[0], center[1] + 1) ;
+        map.map(Forest.OrcVillageNortheastWall, center[0] + 1, center[1] + 1) ;
+        map.map(Forest.OrcVillageWestWall, center[0] - 1, center[1]);
+        map.block(center[0], center[1]);
+        map.map(Forest.OrcVillageEastWall, center[0] + 1, center[1]) ;
+        map.map(Forest.OrcVillageSouthWall, center[0], center[1] - 1) ;
+        map.map(Forest.OrcVillageSoutheastWall, center[0] + 1, center[1] - 1);
+    },
+    conditions : (runner : RulebookRunner<RandomizingRoomOptions>) => {
+        return runner.noun.room == Forest.OrcVillageSouthGates;
+    }
+}));
+
+Forest.OrcVillageSouthGates.trickyCode = (options : TrickyOptions) => {
+    while (!options.map.isFreeSquare(Forest.OrcCoordinates[0] + 1, Forest.OrcCoordinates[1] + 1, 1)) {
+        Forest.OrcCoordinates[0] += 1;
+    }
+
+    return options.x == Forest.OrcCoordinates[0] && options.y == Forest.OrcCoordinates[1];
+};

+ 20 - 0
content/Rooms/Forest/Pond.ts

@@ -0,0 +1,20 @@
+/// <reference path="../Forest.ts" />
+module Forest{
+    export let Pond = new RoomRandom("Pond", false, true);
+    export let PondCoordinates = Forest.rnc.generate(-1,1, 1, 4);
+    Pond.backgroundImage = "roomPond";
+}
+Forest.region.place(Forest.Pond);
+
+Forest.Pond.trickyCode = (options : TrickyOptions) => {
+    let blocked = !options.map.isFree(Forest.PondCoordinates[0], Forest.PondCoordinates[1]);
+    while (blocked) {
+        Forest.PondCoordinates[1] += 1;
+        blocked = !options.map.isFree(Forest.PondCoordinates[0], Forest.PondCoordinates[1]);
+        if (blocked) {
+            Forest.PondCoordinates[0] += 1;
+            blocked = !options.map.isFree(Forest.PondCoordinates[0], Forest.PondCoordinates[1]);
+        }
+    }
+    return options.x == Forest.PondCoordinates[0] && options.y == Forest.PondCoordinates[1];
+};

+ 17 - 0
content/Rooms/Forest/TheObelisk.ts

@@ -0,0 +1,17 @@
+/// <reference path="../Forest.ts" />
+module Forest{
+    export let TheObelisk = new RoomRandom("The Obelisk", false, true);
+    export let ObeliskCoordinates = Forest.rnc.generate(-1,1, 6, 8);
+    TheObelisk.backgroundImage = "roomObelisk";
+    TheObelisk.connectableOn = [Direction.SOUTH];
+}
+Forest.region.place(Forest.TheObelisk);
+
+Forest.TheObelisk.trickyCode = (options : TrickyOptions) => {
+    let blocked = !options.map.isFree(Forest.ObeliskCoordinates[0], Forest.ObeliskCoordinates[1]);
+    while (blocked) {
+        Forest.ObeliskCoordinates[1] += 1;
+        blocked = !options.map.isFree(Forest.ObeliskCoordinates[0], Forest.ObeliskCoordinates[1]);
+    }
+    return options.x == Forest.ObeliskCoordinates[0] && options.y == Forest.ObeliskCoordinates[1];
+};

+ 16 - 0
content/Rooms/Forest/WitchHut.ts

@@ -0,0 +1,16 @@
+/// <reference path="../Forest.ts" />
+module Forest{
+    export let WitchHut = new RoomRandom("The Witch's Hut", false, true);
+    export let WitchCoordinates = Forest.rnc.generate(-2,2, 2, 4);
+    WitchHut.backgroundImage = "roomWitch";
+}
+Forest.region.place(Forest.WitchHut);
+
+Forest.WitchHut.trickyCode = (options : TrickyOptions) => {
+    let blocked = !options.map.isFree(Forest.WitchCoordinates[0], Forest.WitchCoordinates[1]);
+    while (blocked) {
+        Forest.WitchCoordinates[1] += 1;
+        blocked = !options.map.isFree(Forest.WitchCoordinates[0], Forest.WitchCoordinates[1]);
+    }
+    return options.x == Forest.WitchCoordinates[0] && options.y == Forest.WitchCoordinates[1];
+};

+ 112 - 0
content/Rooms/Forest/_Positioner.ts

@@ -0,0 +1,112 @@
+/// <reference path="../Forest.ts" />
+/// <reference path="ForestBed.ts" />
+/// <reference path="OrcVillage.ts" />
+/// <reference path="WitchHut.ts" />
+/// <reference path="MazeEntrance.ts" />
+/// <reference path="TheObelisk.ts" />
+/// <reference path="CentaurVillage.ts" />
+/// <reference path="OminousCave.ts" />
+PlayBegins.rulebook.addRule(new Rule({
+    name : "Randomize Forest Region",
+    firstPriority : Rule.PRIORITY_HIGHEST,
+    code : async runner => {
+        await Forest.region.randomize();
+    }
+}));
+
+RegionRandom.rulebookRandomizeRegion.addRule(new Rule({
+    name : "Add the first rooms to guarantee position.",
+    firstPriority : Rule.PRIORITY_HIGHEST,
+    code : async runner => {
+        // Add the clearing in the woods
+        let cells = [
+            Forest.ForestBed,
+            Forest.OrcVillageSouthGates,
+            Forest.CentaurVillageSoutheastGates,
+            Forest.Pond,
+            Forest.WitchHut,
+            Forest.MazeEntrance,
+            Forest.OminousCave,
+            Forest.TheObelisk,
+        ];
+
+        for (let i = 0; i < cells.length; i++) {
+            let options : RandomizingRoomOptions = {
+                map : Forest.region.map,
+                room : cells[i],
+                region : Forest.region
+            };
+            await RegionRandom.rulebookBeforePlaceRoom.execute({noun : options});
+            await RegionRandom.rulebookPlaceRoom.execute({noun : options});
+            await RegionRandom.rulebookAfterPlaceRoom.execute({noun : options});
+        }
+
+        let minimumAmountOfRooms = cells.length * 7;
+        let placed = Forest.region.getRooms().length; // At this stage, every room is placed
+
+        for (let i = (minimumAmountOfRooms - placed); i > 0; i--) {
+            Forest.region.place(new Forest.region.fodderRoomClass());
+        }
+    },
+    conditions : runner => {
+        return runner.noun == Forest.region;
+    }
+}));
+
+RegionRandom.rulebookRandomizeRegion.createAndAddRule({
+    name : "Beautify the Forest - Plainify",
+    firstPriority : RegionRandom.ruleAddExtraConnections.firstPriority,
+    priority : RegionRandom.ruleAddExtraConnections.priority + 1,
+    code : () => {
+        ForestFodder.definedRooms(<Array<ForestFodder>> Forest.region.getRooms().filter((room : RoomRandom) => {
+            return room.placed && room instanceof ForestFodder;
+        }));
+    }
+});
+
+RegionRandom.rulebookRandomizeRegion.createAndAddRule({
+    name : "Beautify the Forest - Make Beaten Paths",
+    firstPriority : RegionRandom.ruleAddExtraConnections.firstPriority,
+    priority : RegionRandom.ruleAddExtraConnections.priority - 1,
+    code : () => {
+        ForestFodder.makePaths(<Array<ForestFodder>> Forest.region.getRooms().filter((room : RoomRandom) => {
+            return room.placed && room instanceof ForestFodder;
+        }));
+    }
+});
+
+RegionRandom.rulebookRandomizeRegion.createAndAddRule({
+    name : "Beautify the Forest - Just give descriptions to them",
+    firstPriority : RegionRandom.ruleAddExtraConnections.firstPriority,
+    priority : RegionRandom.ruleAddExtraConnections.priority - 2,
+    code : () => {
+        ForestFodder.panicModeBeautify(<Array<ForestFodder>> Forest.region.getRooms().filter((room : RoomRandom) => {
+            return room.placed && room instanceof ForestFodder;
+        }));
+    }
+});
+
+RegionRandom.rulebookRandomizeRegion.addRule(new Rule({
+    name : "After placing and doing extra connections",
+    firstPriority : -1,
+    code : async runner => {
+        // Add the clearing in the woods
+        let cells = [
+
+        ];
+
+        for (let i = 0; i < cells.length; i++) {
+            let options : RandomizingRoomOptions = {
+                map : Forest.region.map,
+                room : cells[i],
+                region : Forest.region
+            };
+            await RegionRandom.rulebookBeforePlaceRoom.execute({noun : options});
+            await RegionRandom.rulebookPlaceRoom.execute({noun : options});
+            await RegionRandom.rulebookAfterPlaceRoom.execute({noun : options});
+        }
+    },
+    conditions : runner => {
+        return runner.noun == Forest.region;
+    }
+}));

+ 3 - 0
content/Rooms/OrcVillage.ts

@@ -0,0 +1,3 @@
+module OrcVillage {
+    export let region = new RegionRandom("Inside the Orc Village");
+}

+ 4 - 0
content/Rooms/OrcVillage/Barracks.ts

@@ -0,0 +1,4 @@
+/// <reference path="../OrcVillage.ts" />
+module OrcVillage {
+    export let Barracks = new RoomRandom("Barracks");
+}

+ 4 - 0
content/Rooms/OrcVillage/NorthEntrance.ts

@@ -0,0 +1,4 @@
+/// <reference path="../OrcVillage.ts" />
+module OrcVillage {
+    export let NorthEntrance = new RoomRandom("Northern Exit");
+}

+ 4 - 0
content/Rooms/OrcVillage/SouthEntrance.ts

@@ -0,0 +1,4 @@
+/// <reference path="../OrcVillage.ts" />
+module OrcVillage {
+    export let SouthEntrance = new RoomRandom("Southern Exit");
+}

+ 10 - 0
content/Rooms/OrcVillage/_Positioner.ts

@@ -0,0 +1,10 @@
+/// <reference path="NorthEntrance.ts" />
+/// <reference path="SouthEntrance.ts" />
+/// <reference path="Barracks.ts" />
+OrcVillage.region.map.emptyCache();
+OrcVillage.region.placeAt(OrcVillage.NorthEntrance, 0, 2);
+OrcVillage.region.placeAt(OrcVillage.Barracks, 0, 1);
+OrcVillage.region.placeAt(OrcVillage.SouthEntrance, 0, 0);
+
+OrcVillage.SouthEntrance.mapRoom(OrcVillage.Barracks, Direction.NORTH);
+OrcVillage.Barracks.mapRoom(OrcVillage.NorthEntrance, Direction.NORTH);

+ 459 - 399
content/main.ts

@@ -1,406 +1,466 @@
-var rooma = new RoomRandom("Room A");
-rooma.description = new Say(
-    "You are inside a box of metal painted blue. Your head almost touches the ceiling, making the room look smaller than it really is.",
-    Say.LINE_BREAK,
-    new SayIf(
-        () => {
-            return Thing.InsideRoomRelation.getLeft(vase) == rooma || Thing.InsideRoomRelation.getLeft(urn) == rooma;
-        },
-        " Still, there are some things thrown about."
-    )
-);
-
-PlayBegins.setStartingRoom(rooma);
-
-let paddedBra = new Clothing({name : "Padded Bra", unique : true});
-paddedBra.breastPadding = 3;
-paddedBra.slots = [Humanoid.SLOT_BREASTS];
-
-let paddedUnderwear = new Clothing({name : "Padded Underwear", unique : true});
-paddedUnderwear.crotchPadding = 15;
-paddedUnderwear.slots = [Humanoid.SLOT_CROTCH_BACK, Humanoid.SLOT_CROTCH_FRONT];
-
-// Thing.WearRelation.setRelation(WorldState.player, paddedBra);
-// Thing.WearRelation.setRelation(WorldState.player, paddedUnderwear);
-
-let player = WorldState.player;
-WorldState.player = player;
-var vase = new Thing({name : "Vase"});
-vase.description = new Say("This is an ornamental vase that'd look very nice on your lap. Why? Who knows.");
-rooma.place(vase);
-
-let mapOfTest = new MapNote({name: "Map of this Region",
-description : "This is a simple map showing all the rooms here.", unique : true});
-rooma.place(mapOfTest);
-
-let urn = new Thing({unique: true, name : "Urn of Dreams", image : "image001"});
-urn.description = new Say("This appears to be a simple, black urn containing the ashes of your dreams.");
-rooma.place(urn);
-
-var roomb = new RoomRandom("Room B");
-
-roomb.place(vase);
-
-
-let frillyPouch = new CoinPouch({
-    name : "Frilly Pouch",
-    //image : new SayImage("image001"),
-    description : new Say("This is a very gay little pink pouch full of little hearts.")
-});
-frillyPouch.addCoins(502);
-rooma.place(frillyPouch);
-
-// ActionTake.check.addRule(new Rule(<RuleOptions<any>> {
-//     code : function () {
-//         let promise = new Promise(() => {});
-//         return promise;
+// var rooma = new RoomRandom("Room A");
+// rooma.description = new Say(
+//     "You are inside a box of metal painted blue. Your head almost touches the ceiling, making the room look smaller than it really is.",
+//     Say.LINE_BREAK,
+//     new SayIf(
+//         () => {
+//             return Thing.InsideRoomRelation.getLeft(vase) == rooma || Thing.InsideRoomRelation.getLeft(urn) == rooma;
+//         },
+//         " Still, there are some things thrown about."
+//     )
+// );
+//
+// PlayBegins.setStartingRoom(rooma);
+//
+//
+// let paddedBra = new Clothing({name : "Padded Bra", unique : true});
+// paddedBra.breastPadding = 3;
+// paddedBra.slots = [Humanoid.SLOT_BREASTS];
+//
+// let paddedUnderwear = new Clothing({name : "Padded Underwear", unique : true});
+// paddedUnderwear.crotchPadding = 15;
+// paddedUnderwear.slots = [Humanoid.SLOT_CROTCH_BACK, Humanoid.SLOT_CROTCH_FRONT];
+//
+// // Thing.WearRelation.setRelation(WorldState.player, paddedBra);
+// // Thing.WearRelation.setRelation(WorldState.player, paddedUnderwear);
+//
+// let player = WorldState.player;
+// WorldState.player = player;
+// var vase = new Thing({name : "Vase"});
+// vase.description = new Say("This is an ornamental vase that'd look very nice on your lap. Why? Who knows.");
+// rooma.place(vase);
+//
+// let mapOfTest = new MapNote({name: "Map of this Region",
+// description : "This is a simple map showing all the rooms here.", unique : true});
+// rooma.place(mapOfTest);
+//
+// let urn = new Thing({unique: true, name : "Urn of Dreams", image : "image001"});
+// urn.description = new Say("This appears to be a simple, black urn containing the ashes of your dreams.");
+// rooma.place(urn);
+//
+// var roomb = new RoomRandom("Room B");
+// let gates = new Door("Gates of Orkindom", OrcVillage.SouthEntrance, true);
+// rooma.place(gates);
+//
+// roomb.place(vase);
+//
+//
+//
+// let frillyPouch = new CoinPouch({
+//     name : "Frilly Pouch",
+//     //image : new SayImage("image001"),
+//     description : new Say("This is a very gay little pink pouch full of little hearts.")
+// });
+// frillyPouch.addCoins(502);
+// rooma.place(frillyPouch);
+//
+// // ActionTake.check.addRule(new Rule(<RuleOptions<any>> {
+// //     code : function () {
+// //         let promise = new Promise(() => {});
+// //         return promise;
+// //     }
+// // }));
+//
+// let bigSack = new CoinPouch({name : "Big Sack"});
+// bigSack.addCoins(100);
+// rooma.place(bigSack);
+//
+// let region = new RegionRandom("Test Region");
+//
+//
+// region.place(rooma, roomb);
+// for (let i = 1; i < 6; i++) {
+//     let room = new RoomRandom("Room " + i.toString());
+//     // let randomDirection = new Shuffler([Room.NORTH, Room.SOUTH, Room.EAST, Room.WEST])
+//     // room.connectableOn = [randomDirection.getOne()];
+//     //room.connectableOn = [Room.NORTH, Room.SOUTH, Room.EAST, Room.WEST];
+//     region.place(room);
+//
+//     if (Math.random() > 0.5) {
+//         //mapOfTest.addRoom(room);
+//     }
+//
+// }
+//
+// mapOfTest.addRegion(region);
+//
+// let southestRoom = new RoomRandom("Southest Room");
+// //southestRoom.appearChance = 0;
+//
+// region.place(southestRoom);
+//
+// //southestRoom.connectableOn = [Room.NORTH];
+// southestRoom.appearChance = 100;
+// southestRoom.backgroundImage = "bloo";
+//
+// southestRoom.trickyCode = (options : TrickyOptions) => {
+//     let myCoordinates = [options.x, options.y];
+//     for (let i = 0; i < Room.DIRECTIONS.length; i++) {
+//         let direction = Room.DIRECTIONS[i];
+//         if (direction != Direction.NORTH) {
+//             let coordinates = Room.shift(myCoordinates, direction);
+//             if (!options.map.isFree(coordinates[0], coordinates[1])) {
+//                 return false; // This room can only have rooms on north of it
+//             }
+//             coordinates = Room.shift(coordinates, direction);
+//             if (!options.map.isFree(coordinates[0], coordinates[1])) {
+//                 return false; // This room can only have rooms on north of it
+//             }
+//         }
+//     }
+//     return true;
+// };
+//
+// RegionRandom.rulebookAfterPlaceRoom.addRule(new Rule({
+//     name : "After placing the southest room",
+//     code : runner => {
+//         let options = <RandomizingRoomOptions> runner.noun;
+//         let myCoordinates = options.map.getCoordinates(options.room);
+//         if (myCoordinates != undefined) {
+//             for (let i = 0; i < Room.DIRECTIONS.length; i++) {
+//                 let direction = Room.DIRECTIONS[i];
+//                 if (direction != Direction.NORTH) {
+//                     let coordinates = Room.shift(myCoordinates, direction);
+//                     options.map.block(coordinates[0], coordinates[1]);
+//                     coordinates = Room.shift(coordinates, direction);
+//                     options.map.block(coordinates[0], coordinates[1]);
+//                 }
+//             }
+//         }
+//     },
+//     conditions : runner => {
+//         return runner.noun.room == southestRoom;
+//     }
+// }))
+//
+// Thing.CarryRelation.setRelation(WorldState.player, mapOfTest);
+//
+// // Settings.setDebug(false);
+// PlayBegins.rulebook.addRule(new Rule({
+//     name : "randomize region",
+//     firstPriority : Rule.PRIORITY_HIGHEST,
+//     code : async runner => {
+//         //let t0 = performance.now();
+//         await region.randomize();
+//         //let t1 = performance.now();
 //     }
 // }));
-
-let bigSack = new CoinPouch({name : "Big Sack"});
-bigSack.addCoins(100);
-rooma.place(bigSack);
-
-let region = new RegionRandom("Test Region");
-
-
-region.place(rooma, roomb);
-for (let i = 1; i < 6; i++) {
-    let room = new RoomRandom("Room " + i.toString());
-    // let randomDirection = new Shuffler([Room.NORTH, Room.SOUTH, Room.EAST, Room.WEST])
-    // room.connectableOn = [randomDirection.getOne()];
-    //room.connectableOn = [Room.NORTH, Room.SOUTH, Room.EAST, Room.WEST];
-    region.place(room);
-
-    if (Math.random() > 0.5) {
-        //mapOfTest.addRoom(room);
-    }
-
-}
-
-mapOfTest.addRegion(region);
-
-let southestRoom = new RoomRandom("Southest Room");
-//southestRoom.appearChance = 0;
-
-region.place(southestRoom);
-
-//southestRoom.connectableOn = [Room.NORTH];
-southestRoom.appearChance = 100;
-southestRoom.backgroundImage = "bloo";
-
-southestRoom.trickyCode = (options : TrickyOptions) => {
-    let myCoordinates = [options.x, options.y];
-    for (let i = 0; i < Room.DIRECTIONS.length; i++) {
-        let direction = Room.DIRECTIONS[i];
-        if (direction != Direction.NORTH) {
-            let coordinates = Room.shift(myCoordinates, direction);
-            if (!options.map.isFree(coordinates[0], coordinates[1])) {
-                return false; // This room can only have rooms on north of it
-            }
-            coordinates = Room.shift(coordinates, direction);
-            if (!options.map.isFree(coordinates[0], coordinates[1])) {
-                return false; // This room can only have rooms on north of it
-            }
-        }
-    }
-    return true;
-};
-
-RegionRandom.rulebookAfterPlaceRoom.addRule(new Rule({
-    name : "After placing the southest room",
-    code : runner => {
-        let options = <RandomizingRoomOptions> runner.noun;
-        let myCoordinates = options.map.getCoordinates(options.room);
-        if (myCoordinates != undefined) {
-            for (let i = 0; i < Room.DIRECTIONS.length; i++) {
-                let direction = Room.DIRECTIONS[i];
-                if (direction != Direction.NORTH) {
-                    let coordinates = Room.shift(myCoordinates, direction);
-                    options.map.block(coordinates[0], coordinates[1]);
-                    coordinates = Room.shift(coordinates, direction);
-                    options.map.block(coordinates[0], coordinates[1]);
-                }
-            }
-        }
-    },
-    conditions : runner => {
-        return runner.noun.room == southestRoom;
-    }
-}))
-
-Thing.CarryRelation.setRelation(WorldState.player, mapOfTest);
-
-// Settings.setDebug(false);
-PlayBegins.rulebook.addRule(new Rule({
-    name : "randomize region",
-    firstPriority : Rule.PRIORITY_HIGHEST,
-    code : async runner => {
-        //let t0 = performance.now();
-        await region.randomize();
-        //let t1 = performance.now();
-    }
-}));
-
-RegionRandom.rulebookRandomizeRegion.addRule(new Rule({
-    name : "Add room A and B to region",
-    firstPriority : Rule.PRIORITY_HIGHEST,
-    code : async runner => {
-        await RegionRandom.rulebookPlaceRoom.execute({
-            noun : <RandomizingRoomOptions> {
-                map : region.map,
-                room : rooma,
-                region : region
-            }
-        }).then();
-        await RegionRandom.rulebookPlaceRoom.execute({
-            noun : <RandomizingRoomOptions> {
-                map : region.map,
-                room : roomb,
-                region : region
-            }
-        }).then();
-    },
-    conditions : runner => {
-        return runner.noun == region;
-    }
-}));
-
-function getPath (rooma : Room, roomb : Room) {
-    console.debug("The best path from " + rooma.getPrintedName() + " to " + roomb.getPrintedName() + " is:");
-    let t0, dir, t1;
-    t0 = performance.now();
-    dir = rooma.bestDirectionTo(roomb);
-    t1 = performance.now();
-    if (dir != undefined) {
-        console.debug(DirectionNames[Direction[dir]]);
-    } else {
-        console.debug("There is no path.");
-    }
-    console.debug("Call to doSomething took " + (t1 - t0) + " milliseconds.");
-}
-
-// let t0 = performance.now();
-// for (var i = 0; i < 1; i++) {
-//     getPath(rooma, roomj);
+//
+// RegionRandom.rulebookRandomizeRegion.addRule(new Rule({
+//     name : "Add room A and B to region",
+//     firstPriority : Rule.PRIORITY_HIGHEST,
+//     code : async runner => {
+//         await RegionRandom.rulebookPlaceRoom.execute({
+//             noun : <RandomizingRoomOptions> {
+//                 map : region.map,
+//                 room : rooma,
+//                 region : region
+//             }
+//         }).then();
+//         await RegionRandom.rulebookPlaceRoom.execute({
+//             noun : <RandomizingRoomOptions> {
+//                 map : region.map,
+//                 room : roomb,
+//                 region : region
+//             }
+//         }).then();
+//     },
+//     conditions : runner => {
+//         return runner.noun == region;
+//     }
+// }));
+//
+// function getPath (rooma : Room, roomb : Room) {
+//     console.debug("The best path from " + rooma.getPrintedName() + " to " + roomb.getPrintedName() + " is:");
+//     let t0, dir, t1;
+//     t0 = performance.now();
+//     dir = rooma.bestDirectionTo(roomb);
+//     t1 = performance.now();
+//     if (dir != undefined) {
+//         console.debug(DirectionNames[Direction[dir]]);
+//     } else {
+//         console.debug("There is no path.");
+//     }
+//     console.debug("Call to doSomething took " + (t1 - t0) + " milliseconds.");
 // }
-// window['roome'] = roome;
-// let t1 = performance.now();
-// console.debug("Total: " + (t1 - t0) + " milliseconds.");
-
-
-let wanderRegion = new RegionRandom("OrcableRegion");
-wanderRegion.place(rooma, roomb);
-region.place(wanderRegion);
-
-// Test done with a thousand orcs searching paths and reading rooms. Total time taken: 200ms.
-// 1000 orcs, no wanderers: 130ms
-// 100 orcs, no wanderer: 13ms (so the same amount of time as not having any orcs)
-// 100 orcs, wanderer: 30ms
-// 100 orcs, no shiny picker, wanderer: 30ms
-// 100 orcs, no wanderer, no shiny picker: 13ms
-// 1000 orcs, no wanderer, no shiny picker: 51ms
-/**
- * Conclusion:
- * The test had all orcs placed in the same room, so every time ShinyPicker AI runs, all the orcs were looking at all the other orcs.
- * Game feels "instant" up to 100ms turns, which was reached at 250 orcs with JUST one PIcks Shiny and Wanderer rules.
- * With 100 EMPTY shiny rules, 250 orcs already take up 500ms per turn!
- * If the orcs fail the conditions for wanderer and shiny rules, then there is just a small overhead to having these rules there.
- * 100 Rules failing the conditions allows for 800 orcs below 100ms.
- * 50 orcs with 150 Shiny Rules which go through all the other orcs while they wander towards a region results in a 250ms wait.
- * That's not good since those numbers are similar to what end game should have, however real rules might not be as expensive as Pick Shinies with a room full of orcs..
- * Wait is bearable for 100 orcs with 800 disabled rules, so it should be "fine" to have all rules in the same rulebook.
- * First possible optimization: make AI rules have a static "check" and bake them into each NPC's rulebook so that they don't have to be checked every turn.
- * Second possible optimization: use maximum distance from player to decide which NPCs have their AIs executed (even better: only run AI for the current region).
- * Third optimization: reduce number of rules. Last resort.
- */
-let rooms = region.getRooms();
-let shuffler = new Shuffler(rooms);
-for (let i = 0; i < 0; i++) {
-    AI.rules.createAndAddRule({
-        name : "Pick Shinies",
-        firstPriority : AIRules.PRIORITY_ACTING_ON_PLACE,
-        conditions : (runner : RulebookRunner<Person>) => {
-            let person = runner.noun;
-            return person.AI.picksShinies;
-        },
-        code : (runner : RulebookRunner<Person>) => {
-            let person = runner.noun;
-            let room = person.getRoom();
-            let visibleThings = room.getContainedAndVisibleTo(person);
-
-            if (visibleThings.length > 0) {
-                for (let i = 0; i < visibleThings.length; i++) {
-                    if (!visibleThings[i].fixedInPlace && visibleThings[i].getShiny()) {
-                        return new ActionTake(person, visibleThings[i]);
-                    }
-                }
-            }
-        }
-    });
-}
-let randomOrc;
-let randomOrc2;
-for (let i = 0; i < 8; i++) {
-    let orc = new OrcDebugger();
-    randomOrc = orc;
-    if (randomOrc2 == undefined) {
-        randomOrc2 = orc;
-    }
-    orc.AI.wanderer = true;
-    orc.AI.picksShinies = true;
-    orc.AI.wandersOn = wanderRegion;
-    let room = new Shuffler(region.getRooms()).getOne();
-    room.place(orc);
-}
-
-var fTarget = new ContentGroup();
-fTarget.addUnit(
-    new FuckingUnit()
-        .setFucked(WorldState.player)
-        .setFucker(randomOrc)
-        .setHole(WorldState.player.getPart(HumanoidVagina))
-        .setStick(randomOrc.getPart(HumanoidPenis))
-);
-fTarget.addUnit(
-    new FuckingUnit()
-        .setFucked(WorldState.player)
-        .setFucker(randomOrc2)
-        .setHole(WorldState.player.getPart(HumanoidHead))
-        .setStick(randomOrc2.getPart(HumanoidPenis))
-);
-
-let spitroast = (new FuckingDescription("Orc spitroast!"));
-spitroast.setDescription(new Say("Orc Spitroast!"))
-    .addUnit()
-    .setFucker(OrcDebugger)
-    .setHole(HumanoidVagina)
-    .setStick(HumanoidPenis);
-spitroast.addUnit()
-    .setFucker(OrcDebugger)
-    .setHole(HumanoidHead)
-    .setStick(HumanoidPenis);
-
-(new FuckingDescription("Specific Orc in Vagina"))
-    .setDescription(new Say("Specific Orc in Vagina."))
-    .addUnit()
-    .setFucker(randomOrc)
-    .setHole(WorldState.player.getPart(HumanoidVagina))
-    .setStick(randomOrc.getPart(HumanoidPenis));
-
-(new FuckingDescription("Specific Orc Starts Cumming in Vagina"))
-    .setDescription(new Say("Specific Orc Starts Cumming in Vagina"))
-    .addUnit()
-    .setFucker(randomOrc)
-    .setHole(WorldState.player.getPart(HumanoidVagina))
-    .addMarker(FuckingState.CUM_START)
-    .setStick(randomOrc.getPart(HumanoidPenis));
-
-(new FuckingDescription("Orc in mouth"))
-    .setDescription(new Say("Orc in mouth."))
-    .addUnit()
-    .setFucker(OrcDebugger)
-    .setHole(HumanoidHead)
-    .setStick(HumanoidPenis);
-
+//
+// // let t0 = performance.now();
+// // for (var i = 0; i < 1; i++) {
+// //     getPath(rooma, roomj);
+// // }
+// // window['roome'] = roome;
+// // let t1 = performance.now();
+// // console.debug("Total: " + (t1 - t0) + " milliseconds.");
+//
+//
+// let wanderRegion = new RegionRandom("OrcableRegion");
+// wanderRegion.place(rooma, roomb);
+// region.place(wanderRegion);
+//
+// // Test done with a thousand orcs searching paths and reading rooms. Total time taken: 200ms.
+// // 1000 orcs, no wanderers: 130ms
+// // 100 orcs, no wanderer: 13ms (so the same amount of time as not having any orcs)
+// // 100 orcs, wanderer: 30ms
+// // 100 orcs, no shiny picker, wanderer: 30ms
+// // 100 orcs, no wanderer, no shiny picker: 13ms
+// // 1000 orcs, no wanderer, no shiny picker: 51ms
+// /**
+//  * Conclusion:
+//  * The test had all orcs placed in the same room, so every time ShinyPicker AI runs, all the orcs were looking at all the other orcs.
+//  * Game feels "instant" up to 100ms turns, which was reached at 250 orcs with JUST one PIcks Shiny and Wanderer rules.
+//  * With 100 EMPTY shiny rules, 250 orcs already take up 500ms per turn!
+//  * If the orcs fail the conditions for wanderer and shiny rules, then there is just a small overhead to having these rules there.
+//  * 100 Rules failing the conditions allows for 800 orcs below 100ms.
+//  * 50 orcs with 150 Shiny Rules which go through all the other orcs while they wander towards a region results in a 250ms wait.
+//  * That's not good since those numbers are similar to what end game should have, however real rules might not be as expensive as Pick Shinies with a room full of orcs..
+//  * Wait is bearable for 100 orcs with 800 disabled rules, so it should be "fine" to have all rules in the same rulebook.
+//  * First possible optimization: make AI rules have a static "check" and bake them into each NPC's rulebook so that they don't have to be checked every turn.
+//  * Second possible optimization: use maximum distance from player to decide which NPCs have their AIs executed (even better: only run AI for the current region).
+//  * Third optimization: reduce number of rules. Last resort.
+//  */
+// let rooms = region.getRooms();
+// let shuffler = new Shuffler(rooms);
+// for (let i = 0; i < 0; i++) {
+//     AI.rules.createAndAddRule({
+//         name : "Pick Shinies",
+//         firstPriority : AIRules.PRIORITY_ACTING_ON_PLACE,
+//         conditions : (runner : RulebookRunner<Person>) => {
+//             let person = runner.noun;
+//             return person.AI.picksShinies;
+//         },
+//         code : (runner : RulebookRunner<Person>) => {
+//             let person = runner.noun;
+//             let room = person.getRoom();
+//             let visibleThings = room.getContainedAndVisibleTo(person);
+//
+//             if (visibleThings.length > 0) {
+//                 for (let i = 0; i < visibleThings.length; i++) {
+//                     if (!visibleThings[i].fixedInPlace && visibleThings[i].getShiny()) {
+//                         return new ActionTake(person, visibleThings[i]);
+//                     }
+//                 }
+//             }
+//         }
+//     });
+// }
+// let randomOrc;
+// let randomOrc2;
+// for (let i = 0; i < 8; i++) {
+//     let orc = new OrcDebugger();
+//     randomOrc = orc;
+//     if (randomOrc2 == undefined) {
+//         randomOrc2 = orc;
+//     }
+//     orc.AI.wanderer = true;
+//     orc.AI.picksShinies = true;
+//     orc.AI.wandersOn = wanderRegion;
+//     let room = new Shuffler(region.getRooms()).getOne();
+//     room.place(orc);
+// }
+//
+// var fTarget = new ContentGroup();
+// fTarget.addUnit(
+//     new FuckingUnit()
+//         .setFucked(WorldState.player)
+//         .setFucker(randomOrc)
+//         .setHole(WorldState.player.getPart(HumanoidVagina))
+//         .setStick(randomOrc.getPart(HumanoidPenis))
+// );
+// fTarget.addUnit(
+//     new FuckingUnit()
+//         .setFucked(WorldState.player)
+//         .setFucker(randomOrc2)
+//         .setHole(WorldState.player.getPart(HumanoidHead))
+//         .setStick(randomOrc2.getPart(HumanoidPenis))
+// );
+//
 
-// (new CombatDescription("Poking 2")
-//     .setDescription("Oy cheeky kunt stahp that"))
-//     .addPokeUnit()
-//     .setTarget(Person)
-//     .addMarker(CombatPokeResult.ANNOYED);
 //
-// (new CombatDescription("Poking 1")
-//     .setDescription("Heh whatever"))
-//     .addPokeUnit()
-//     .setTarget(Person)
-//     .addMarker(CombatPokeResult.NOHEAT);
+// (new FuckingDescription("Specific Orc in Vagina"))
+//     .setDescription(new Say("Specific Orc in Vagina."))
+//     .addUnit()
+//     .setFucker(randomOrc)
+//     .setHole(WorldState.player.getPart(HumanoidVagina))
+//     .setStick(randomOrc.getPart(HumanoidPenis));
+//
+// (new FuckingDescription("Specific Orc Starts Cumming in Vagina"))
+//     .setDescription(new Say("Specific Orc Starts Cumming in Vagina"))
+//     .addUnit()
+//     .setFucker(randomOrc)
+//     .setHole(WorldState.player.getPart(HumanoidVagina))
+//     .addMarker(FuckingState.CUM_START)
+//     .setStick(randomOrc.getPart(HumanoidPenis));
+//
+// (new FuckingDescription("Orc in mouth"))
+//     .setDescription(new Say("Orc in mouth."))
+//     .addUnit()
+//     .setFucker(OrcDebugger)
+//     .setHole(HumanoidHead)
+//     .setStick(HumanoidPenis);
+//
 //
-// (new CombatDescription("Poking 3")
-//     .setDescription("A'IGHT YOU GET FUCKED NOW MATE SWAER ON ME MUM"))
-//     .addPokeUnit()
+// // (new CombatDescription("Poking 2")
+// //     .setDescription("Oy cheeky kunt stahp that"))
+// //     .addPokeUnit()
+// //     .setTarget(Person)
+// //     .addMarker(CombatPokeResult.ANNOYED);
+// //
+// // (new CombatDescription("Poking 1")
+// //     .setDescription("Heh whatever"))
+// //     .addPokeUnit()
+// //     .setTarget(Person)
+// //     .addMarker(CombatPokeResult.NOHEAT);
+// //
+// // (new CombatDescription("Poking 3")
+// //     .setDescription("A'IGHT YOU GET FUCKED NOW MATE SWAER ON ME MUM"))
+// //     .addPokeUnit()
+// //     .setTarget(Person)
+// //     .addMarker(CombatPokeResult.AGGROED);
+//
+// (new CombatDescription("Allranging Fists"))
+//     .setDescriptionFunction((actor, target, weapons, markers) => {
+//         let say = new Say(...Say.Mention(actor), " attack", target != WorldState.player ? "s " : " ", ...Say.Mention(target), " with ", ...Say.YourTheir(target) ," fists");
+//         if (markers.indexOf(CombatHit.MISS) != -1) {
+//             say.add(", but ", ...Say.Mention(actor), " miss");
+//         } else if (markers.indexOf(CombatHit.CRITICAL) != -1) {
+//             say.add(", it is a strong hit");
+//         }
+//
+//         if (markers.indexOf(CombatResult.KNOCKED) != -1) {
+//             say.add(", the strength of ", ...Say.YourTheir(target) ," attack knocks ", ...Say.Mention(target) , " on the floor.");
+//         } else if (markers.indexOf(CombatResult.KNOCKED_OFF) != -1) {
+//             say.add(", the strength of ", ...Say.YourTheir(target) ," attack knocks ", ...Say.Mention(target), " unconscious.");
+//         } else if (markers.indexOf(CombatResult.KILLED) != -1) {
+//             say.add(", ", new SayHeSheIt(target), " dies.");
+//         } else {
+//             say.add(".");
+//         }
+//
+//         return say;
+//     })
+//     .addUnit()
+//     .setActor(Person)
 //     .setTarget(Person)
-//     .addMarker(CombatPokeResult.AGGROED);
-
-(new CombatDescription("Allranging Fists"))
-    .setDescriptionFunction((actor, target, weapons, markers) => {
-        let say = new Say(...Say.Mention(actor), " attack", target != WorldState.player ? "s " : " ", ...Say.Mention(target), " with ", ...Say.YourTheir(target) ," fists");
-        if (markers.indexOf(CombatHit.MISS) != -1) {
-            say.add(", but ", ...Say.Mention(actor), " miss");
-        } else if (markers.indexOf(CombatHit.CRITICAL) != -1) {
-            say.add(", it is a strong hit");
-        }
-
-        if (markers.indexOf(CombatResult.KNOCKED) != -1) {
-            say.add(", the strength of ", ...Say.YourTheir(target) ," attack knocks ", ...Say.Mention(target) , " on the floor.");
-        } else if (markers.indexOf(CombatResult.KNOCKED_OFF) != -1) {
-            say.add(", the strength of ", ...Say.YourTheir(target) ," attack knocks ", ...Say.Mention(target), " unconscious.");
-        } else if (markers.indexOf(CombatResult.KILLED) != -1) {
-            say.add(", ", new SayHeSheIt(target), " dies.");
-        } else {
-            say.add(".");
-        }
-
-        return say;
-    })
-    .addUnit()
-    .setActor(Person)
-    .setTarget(Person)
-    .addMarker(AdaptiveDifferential.FULLYADAPTIVE(CombatHit.HIT, CombatHit.CRITICAL, CombatHit.MISS))
-    .addMarker(AdaptiveDifferential.FULLYADAPTIVE(CombatResult.KILLED, CombatResult.KNOCKED, CombatResult.KNOCKED_OFF));
-
-
-(new CombatPokeDescription("Hitting Orc"))
-    .setDescriptionFunction((aggressor, target, markers) => {
-        let say = new Say(new SayBold(target), ": ");
-
-        let action = new SayAction();
-        action.add("looks at ");
-        if (aggressor != WorldState.player) {
-            action.add(new SayThe(), aggressor)
-        } else {
-            action.add("you");
-        }
-        say.add(action);
-
-        if (markers.includes(AIRules.resultHostile)) {
-            say.add("Fucking seriously!? I'm going to rip your head off!");
-            return say;
-        } else if (AIRules.resultRetaliate) {
-            say.add("Fucking STOP that!");
-            return say;
-        }
-
-        if (markers.includes(AIRules.actionMin)) {
-            say.add(
-                new OneOf(OneOf.PURELY_AT_RANDOM,
-                    "Stop that.",
-                    "You better stop that.",
-                    "Look, I'm not very patient. Stop that."
-                )
-            );
-        } else if (markers.includes(AIRules.actionMed)) {
-            say.add(
-                new OneOf(OneOf.PURELY_AT_RANDOM,
-                    "Fucking do that again, see what happens.",
-                    "Watch it!",
-                    "I'm THIS close to ripping your head off!"
-                )
-            );
-        } else if (markers.includes(AIRules.actionMax)) {
-            say.add(
-                new OneOf(OneOf.PURELY_AT_RANDOM,
-                    "Goddamn it.",
-                    "Watch it!",
-                    "I'm THIS close to ripping your head off!"
-                )
-            );
-        }
-
-        return say;
-    })
-    .addUnit()
-    .setAggressor(Person)
-    .setTarget(OrcDebugger)
-    .addMarker(AdaptiveDifferential.FULLYADAPTIVE(AIRules.resultRetaliate, AIRules.resultHostile, AIRules.resultNotHostile))
-    .addMarker(AdaptiveDifferential.FULLYADAPTIVE(AIRules.actionMin, AIRules.actionMed, AIRules.actionMax));
+//     .addMarker(AdaptiveDifferential.FULLYADAPTIVE(CombatHit.HIT, CombatHit.CRITICAL, CombatHit.MISS))
+//     .addMarker(AdaptiveDifferential.FULLYADAPTIVE(CombatResult.KILLED, CombatResult.KNOCKED, CombatResult.KNOCKED_OFF));
+//
+//
+// (new CombatPokeDescription("Hitting Orc"))
+//     .setDescriptionFunction((aggressor, target, markers) => {
+//         let say = new Say(new SayBold(target), ": ");
+//
+//         let action = new SayAction();
+//         action.add("looks at ");
+//         if (aggressor != WorldState.player) {
+//             action.add(new SayThe(), aggressor)
+//         } else {
+//             action.add("you");
+//         }
+//         say.add(action);
+//
+//         if (markers.includes(AIRules.resultHostile)) {
+//             say.add("Fucking seriously!? I'm going to rip your head off!");
+//             return say;
+//         } else if (AIRules.resultRetaliate) {
+//             say.add("Fucking STOP that!");
+//             return say;
+//         }
+//
+//         if (markers.includes(AIRules.actionMin)) {
+//             say.add(
+//                 new OneOf(OneOf.PURELY_AT_RANDOM,
+//                     "Stop that.",
+//                     "You better stop that.",
+//                     "Look, I'm not very patient. Stop that."
+//                 )
+//             );
+//         } else if (markers.includes(AIRules.actionMed)) {
+//             say.add(
+//                 new OneOf(OneOf.PURELY_AT_RANDOM,
+//                     "Fucking do that again, see what happens.",
+//                     "Watch it!",
+//                     "I'm THIS close to ripping your head off!"
+//                 )
+//             );
+//         } else if (markers.includes(AIRules.actionMax)) {
+//             say.add(
+//                 new OneOf(OneOf.PURELY_AT_RANDOM,
+//                     "Goddamn it.",
+//                     "Watch it!",
+//                     "I'm THIS close to ripping your head off!"
+//                 )
+//             );
+//         }
+//
+//         return say;
+//     })
+//     .addUnit()
+//     .setAggressor(Person)
+//     .setTarget(OrcDebugger)
+//     .addMarker(AdaptiveDifferential.FULLYADAPTIVE(AIRules.resultRetaliate, AIRules.resultHostile, AIRules.resultNotHostile))
+//     .addMarker(AdaptiveDifferential.FULLYADAPTIVE(AIRules.actionMin, AIRules.actionMed, AIRules.actionMax));
+
+// (new CombatDescription("test"))
+//     .setDescriptionFunction((actor, target, weapons, markers) => {
+//         let say = new Say();
+//         let verb = "attack";
+//         if (!WorldState.isPlayer(actor)) {
+//             verb += "s";
+//         }
+//         say.add(Say.Mention(actor), " " + verb + " ", Say.Mention(target), " with ", Say.Possessive(actor), " fists.");
+//
+//         if (markers.includes(CombatHit.MISS)) {
+//             say.add(" ", Say.Subject(actor),  " " + ("miss" + (WorldState.isPlayer(actor) ? "es." : ".")));
+//         } else if (markers.includes(CombatHit.CRITICAL)) {
+//             say.add(" It was a very strong hit!");
+//         }
+//
+//         if (markers.includes(CombatResult.KILLED)) {
+//             say.add(" ", Say.Mention(target), " was killed by the force of the attack.");
+//         } else if (markers.includes(CombatResult.KNOCKED_OFF)) {
+//             say.add(" ", Say.Mention(target), " was incapacitated by the force of the attack.");
+//         } else if (markers.includes(CombatResult.KNOCKED)) {
+//             say.add(" ", Say.Mention(target), " was thrown to the ground by the force of the attack.");
+//         }
+//
+//         return say;
+//     })
+//     .addUnit()
+//         .setActor(Person)
+//         .setTarget(Person)
+//         .setWeapon(HumanoidHands)
+//         .addMarker(AdaptiveDifferential.FULLYADAPTIVE(CombatHit.MISS, CombatHit.HIT, CombatHit.CRITICAL))
+//         .addMarker(AdaptiveDifferential.FULLYADAPTIVE(CombatResult.KNOCKED, CombatResult.KILLED, CombatResult.KNOCKED_OFF))
+// ;
+
+// (new CombatDescription("Minotaur Attacks"))
+//     .setDescriptionFunction((actor, target, weapons, markers) => {
+//         let say = new Say();
+//
+//         if (markers.includes(CombatHit.MISS) {
+//             say.add("The minotaur heaves his heavy punches at you, but you narrowly avoid it.");
+//         } else if (markers.includes(CombatHit.HIT) {
+//             say.add("The minotaur punches you with ease.");
+//         } else if (markers.includes(CombatHit.CRITICAL) {
+//             say.add("The minotaur punches you in the gut. Ouch, you felt that HARD.");
+//         }
+//
+//         say.add(" ");
+//
+//         if (markers.includes(CombatResult.KILLED) {
+//             say.add("YOU ARE DEAD.");
+//         } else if (markers.includes(CombatResult.KNOCKED_OFF) {
+//             say.add("You lose consciousness.");
+//         } else if (markers.includes(CombatResult.KNOCKED) {
+//             say.add("You are knocked to the ground!");
+//         }
+//
+//         return say;
+//     })
+//     .addUnit()
+//     .setActor(MinotaurGuard)
+//     .setTarget(WorldState.player)
+//     .setWeapon(Thing)
+//     .addMarker(AdaptiveDifferential.FULLYADAPTIVE(CombatHit.MISS, CombatHit.HIT, CombatHit.CRITICAL))
+//     .addMarker(AdaptiveDifferential.FULLYADAPTIVE(CombatResult.KNOCKED, CombatResult.KILLED, CombatResult.KNOCKED_OFF))
+// ;

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
dist/The Obelisk.html


+ 36 - 0
sass/_map.scss

@@ -137,6 +137,42 @@
   background: blue;
 }
 
+.roomOrcVillage {
+  background: #410004;
+}
+
+.roomWitch {
+  background: #d6cab4;
+}
+
+.roomCrypt {
+  background: #938500;
+}
+
+.roomObelisk {
+  background: #000;
+}
+
+.roomForestBed {
+  background: #07cb00;
+}
+
+.roomCentaurVillage {
+  background: #00319d;
+}
+
+.roomPond  {
+  background: #8157ff
+}
+
+.roomOminousCave {
+  background: #47002e;
+}
+
+.roomForestFodder {
+  background: #ff7a5c;
+}
+
 .blocked {
   background-color: black;
 

Разница между файлами не показана из-за своего большого размера
+ 0 - 45
stylesheets/fonts.css


Разница между файлами не показана из-за своего большого размера
+ 0 - 43
stylesheets/images.css


+ 0 - 876
stylesheets/screen.css

@@ -1,876 +0,0 @@
-p {
-  margin: 0px;
-  padding: 0px;
-}
-
-.error {
-  padding: 0.5em;
-  background-color: #d6a3a7;
-  color: #780100;
-}
-.error:before {
-  content: "ERROR: ";
-  font-weight: bold;
-}
-
-.unselectable, .textLink, .lineLink, .columnLink, .ccButton, .roomObject, .roomDirection, .rememberedRoomLink, .inventoryLink, .statusLink, .roundButton, p.choice, .combatChoice {
-  -moz-user-select: -moz-none;
-  -khtml-user-select: none;
-  -webkit-user-select: none;
-  -o-user-select: none;
-  user-select: none;
-}
-.unselectable:hover, .textLink:hover, .lineLink:hover, .columnLink:hover, .ccButton:hover, .roomObject:hover, .roomDirection:hover, .rememberedRoomLink:hover, .inventoryLink:hover, .statusLink:hover, .roundButton:hover, p.choice:hover, .combatChoice:hover {
-  cursor: default;
-}
-
-.scrollbar {
-  overflow: auto;
-}
-.scrollbar::-webkit-scrollbar-track {
-  -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
-  background-color: #F5F5F5;
-}
-.scrollbar::-webkit-scrollbar {
-  width: 6px;
-  background-color: #F5F5F5;
-}
-.scrollbar::-webkit-scrollbar-thumb {
-  background-color: #000000;
-}
-
-.header, .roomName, .roomExitsHeader, .inventoryHeader, .appearanceHeader {
-  font-weight: bold;
-  font-variant: small-caps;
-  font-size: 1.1rem;
-}
-
-.alignRight {
-  text-align: right;
-}
-
-.alignCenter {
-  text-align: center;
-}
-
-body {
-  background-color: #000;
-}
-
-#mainPage {
-  background-color: #fff;
-}
-
-#mainPage, .topBottomFlex {
-  position: absolute;
-  left: 0px;
-  top: 0px;
-  width: 100%;
-  height: 100%;
-  display: flex;
-  flex-direction: column;
-  flex-wrap: nowrap;
-  justify-content: flex-start;
-  align-content: stretch;
-  align-items: flex-start;
-}
-
-.leftRightFlex {
-  display: flex;
-  flex-direction: row;
-  flex-wrap: nowrap;
-  justify-content: flex-start;
-  align-content: stretch;
-  align-items: flex-start;
-}
-
-#mainPage {
-  color: #000;
-  font-family: "Source Sans Pro";
-}
-
-#statusLine, .noshrinkFlex, #roomExitsHolder {
-  flex: 0 0 auto;
-  align-self: stretch;
-  height: auto;
-  overflow: visible;
-  position: relative;
-}
-
-#statusLine {
-  background-color: black;
-  color: #FFF;
-  font-family: "Cousine";
-  white-space: nowrap;
-  padding: 0.4rem;
-}
-
-.statusColumnRight {
-  margin-left: auto;
-}
-
-.statusColumnCenter {
-  margin-left: auto;
-  margin-right: auto;
-}
-
-.statusColumnDivider {
-  border-right: #fff solid 1px;
-  margin-left: 0.5rem;
-  margin-right: 0.5rem;
-}
-
-#gameContainer, .growingFlex {
-  flex: 1 1 auto;
-  align-self: stretch;
-  position: relative;
-}
-
-#leftWindow, #centerWindow, #rightWindow {
-  flex: 1 0 auto;
-  align-self: auto;
-  flex-grow: 1;
-  min-width: 10rem;
-  position: relative;
-  background-color: #fff;
-}
-
-#centerWindow {
-  max-width: 60%;
-  flex: 1 1 auto;
-  flex-grow: 4;
-}
-
-#windowContainer {
-  display: flex;
-  flex-direction: row;
-  flex-wrap: nowrap;
-  justify-content: flex-start;
-  align-content: stretch;
-  align-items: stretch;
-  height: 100%;
-  width: 100%;
-  position: absolute;
-}
-
-#currentTurn {
-  overflow-x: hidden;
-  overflow-y: scroll;
-  min-height: 100px;
-  background: linear-gradient(to bottom, rgba(0, 0, 0, 0.03) 75%, rgba(0, 0, 0, 0.06) 100%);
-}
-
-#currentTurn > * {
-  animation: fadein 1s;
-}
-
-@keyframes fadein {
-  from {
-    opacity: 0;
-  }
-  to {
-    opacity: 1;
-  }
-}
-#leftWindow {
-  background: linear-gradient(to right, rgba(0, 0, 0, 0.12) 0%, rgba(0, 0, 0, 0.09) 50%, rgba(0, 0, 0, 0.06) 100%);
-}
-
-#appearanceTab {
-  overflow: auto;
-  padding: 0.8rem;
-}
-
-#inventoryTab {
-  overflow-y: auto;
-  flex-shrink: 1;
-}
-
-#currentRoomTab {
-  overflow: hidden;
-  background: linear-gradient(to bottom, rgba(0, 0, 0, 0.06) 75%, rgba(0, 0, 0, 0.03) 100%);
-  padding: 0.8rem;
-}
-
-#hyperlinksTab {
-  background: rgba(0, 0, 0, 0.03);
-  text-indent: 1em;
-}
-
-#mainPage.mobile #fakeparserTab {
-  display: none;
-}
-
-#fakeparserTab {
-  overflow: visible;
-  background: rgba(0, 0, 0, 0.03);
-  padding: 0.8rem;
-  font-family: "Cousine";
-}
-#fakeparserTab:before {
-  content: ">";
-}
-
-#currentCommand, #fakeParserThingy {
-  font-weight: bold;
-}
-
-#fakeParserThingy {
-  animation: blinker 0.9s cubic-bezier(0.5, 0, 1, 1) infinite alternate;
-}
-
-@keyframes blinker {
-  to {
-    opacity: 0;
-  }
-}
-#rememberedRoomsTab, #mapTab {
-  overflow-y: auto;
-  background: linear-gradient(to bottom left, rgba(0, 0, 0, 0.12) 0%, rgba(0, 0, 0, 0.09) 50%, rgba(0, 0, 0, 0.06) 100%);
-  padding: 0.8rem;
-}
-
-#roomExitsHolder {
-  background: linear-gradient(to top left, rgba(0, 0, 0, 0.12) 0%, rgba(0, 0, 0, 0.09) 50%, rgba(0, 0, 0, 0.06) 100%);
-  padding: 2ex;
-  padding-top: 0px;
-}
-
-#exitsTab {
-  overflow: hidden;
-  background-color: rgba(0, 0, 0, 0.03);
-  padding: 0.8rem;
-}
-
-#modalWindow {
-  opacity: 0;
-  pointer-events: none;
-  z-index: 10;
-  position: absolute;
-  left: 0px;
-  right: 0px;
-  top: 0px;
-  bottom: 0px;
-  transition: opacity .3s ease-in-out;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  overflow: hidden;
-}
-
-*:not(#modalWindow) {
-  transition: filter .3s ease-in-out;
-}
-
-body.modal > *:not(#modalWindow) {
-  filter: blur(3px);
-}
-body.modal #modalWindow {
-  opacity: 1;
-  pointer-events: auto;
-}
-
-#modalContent {
-  width: auto;
-  height: auto;
-  max-height: 85%;
-  max-width: 85%;
-  background: rgba(230, 240, 255, 0.4);
-  border: 2px dashed transparent;
-  box-shadow: 0px 0px 1px 1px rgba(0, 0, 0, 0.15);
-  border-radius: 4px;
-  overflow-x: hidden;
-  overflow-y: auto;
-  padding: 1.6rem;
-  padding-top: 0px;
-}
-
-#leftWindow, #rightWindow, #currentRoomTab, #hyperlinksTab, #fakeparserTab, #statusLine {
-  transition: filter .3s ease-in-out;
-}
-
-#sceneAnimation {
-  transition: opacity  .3s ease-in-out;
-}
-
-#mainPage.turn #sceneAnimation {
-  opacity: 1;
-}
-#mainPage.turn #leftWindow, #mainPage.turn #rightWindow, #mainPage.turn #currentRoomTab, #mainPage.turn #hyperlinksTab, #mainPage.turn #fakeparserTab, #mainPage.turn #statusLine {
-  pointer-events: none;
-  filter: blur(2px);
-}
-
-#mainPage.intro #leftWindow, #mainPage.intro #rightWindow, #mainPage.intro #statusLine {
-  display: none;
-}
-#mainPage.intro #centerWindow {
-  max-width: 100%;
-}
-
-#loadingScreen {
-  position: absolute;
-  left: 0px;
-  top: 0px;
-  bottom: 0px;
-  right: 0px;
-  background: linear-gradient(1deg, #FFF, #EEE);
-  background-size: 400% 400%;
-  z-index: 99999;
-}
-
-.lineLink {
-  line-height: 2em;
-  padding-left: 0.35em;
-  padding-right: 0.35em;
-}
-
-.columnLink {
-  line-height: 2em;
-  padding-left: 0.35em;
-  padding-right: 0.35em;
-}
-
-#linkTarget {
-  font-weight: bold;
-  display: inline;
-  line-height: 2em;
-  margin-left: 2ex;
-}
-
-#linkActions {
-  display: inline;
-  white-space: nowrap;
-}
-
-.mapRow {
-  padding: 0;
-  margin: 0;
-  list-style: none;
-  display: flex;
-  flex-flow: row;
-  justify-content: center;
-  margin-top: 1ex;
-  z-index: 1;
-}
-
-.mapRoom {
-  z-index: 2;
-  opacity: 0.5;
-  max-width: 4.5ex;
-  color: white;
-  flex: 1 0 auto;
-  margin-left: 1ex;
-  height: auto;
-  position: relative;
-}
-.mapRoom:last-child {
-  margin-right: 1ex;
-}
-.mapRoom.linked:hover {
-  opacity: 1;
-  cursor: pointer;
-}
-.mapRoom.current {
-  opacity: 1;
-}
-.mapRoom.current:hover {
-  cursor: auto;
-}
-.mapRoom.current::after {
-  display: block;
-  position: absolute;
-  content: '';
-  border: solid 2px #000;
-  box-sizing: border-box;
-  height: 100%;
-  width: 100%;
-}
-.mapRoom.unknown {
-  opacity: 0;
-  pointer-events: none;
-}
-.mapRoom::before {
-  content: '';
-  float: left;
-  padding-top: 100%;
-}
-
-.mapRoomConnection, .mapRoomConnectionEast, .mapRoomConnectionWest, .mapRoomConnectionNorth, .mapRoomConnectionSouth {
-  pointer-events: none;
-  display: block;
-  background-color: black;
-  position: absolute;
-  z-index: 3;
-}
-
-.mapRoomConnectionEast {
-  width: 1ex;
-  height: 0.6ex;
-  right: -1ex;
-  top: 50%;
-  margin-top: -0.3ex;
-}
-
-.mapRoomConnectionWest {
-  width: 1ex;
-  height: 0.6ex;
-  left: -1ex;
-  top: 50%;
-  margin-top: -0.3ex;
-}
-
-.mapRoomConnectionNorth {
-  width: 0.6ex;
-  height: 1ex;
-  left: 50%;
-  top: -1ex;
-  margin-left: -0.3ex;
-}
-
-.mapRoomConnectionSouth {
-  width: 0.6ex;
-  height: 1ex;
-  left: 50%;
-  bottom: -1ex;
-  margin-left: -0.3ex;
-}
-
-.mapRoomName {
-  display: none;
-  position: absolute;
-  text-align: center;
-  bottom: 105%;
-  right: 5%;
-  padding: 5%;
-  white-space: nowrap;
-  margin: auto;
-  background-color: white;
-  color: #000;
-  font-weight: bold;
-  border: solid 2px #000;
-  z-index: 4;
-  pointer-events: none;
-}
-
-.mapRoom.linked:hover > .mapRoomName {
-  display: block;
-}
-
-.tomato {
-  background: tomato;
-}
-
-.bloo {
-  background: blue;
-}
-
-.blocked {
-  background-color: black;
-}
-.blocked.unknown {
-  opacity: 1;
-}
-
-#hoverInfo {
-  z-index: 100;
-  position: fixed;
-  top: 10px;
-  left: 10px;
-  background-color: #fff;
-  border: solid 2px #000;
-  padding: 4px;
-  font-weight: bold;
-  pointer-events: none;
-  max-width: 100%;
-  display: none;
-  box-sizing: border-box;
-}
-
-#forceTurnToTop {
-  display: none;
-}
-
-#mainPage.mainmenu #statusLine, #mainPage.mainmenu #statusLine, #mainPage.mainmenu #rightWindow, #mainPage.mainmenu #currentRoomTab, #mainPage.mainmenu #fakeparserTab, #mainPage.mainmenu #hyperlinksTab, #mainPage.mainmenu #leftWindow {
-  display: none;
-}
-#mainPage.mainmenu #forceTurnToTop {
-  display: block;
-}
-#mainPage.mainmenu #centerWindow {
-  max-width: 100%;
-}
-#mainPage.mainmenu #currentTurnTab {
-  padding-left: 15%;
-  padding-right: 15%;
-  padding-bottom: 2%;
-}
-#mainPage.mainmenu.mobile #currentTurnTab {
-  padding-left: 0%;
-  padding-right: 0%;
-}
-#mainPage.mainmenu p.choice {
-  border: none;
-  background: none;
-  color: #000;
-  font-weight: bold;
-  text-align: center;
-  padding: 0rem;
-  margin: 0rem;
-  margin-bottom: 0.1rem;
-  margin-top: 0.1rem;
-}
-#mainPage.mainmenu p.choice::before {
-  color: #000;
-}
-#mainPage.mainmenu p.choice::after {
-  content: "  ";
-  color: #000;
-}
-#mainPage.mainmenu p.choice:hover::before {
-  content: "< ";
-  color: #000;
-}
-#mainPage.mainmenu p.choice:hover::after {
-  content: " >";
-  color: #000;
-}
-#mainPage.mainmenu p.choice[data-shortcut]:hover::before {
-  content: "< " attr(data-shortcut) ") ";
-  color: #000;
-}
-#mainPage.mainmenu p.choice[data-shortcut]:hover::after {
-  content: " >";
-  color: #000;
-}
-
-#characterCreation {
-  display: flex;
-  flex-direction: row;
-  flex-wrap: nowrap;
-  justify-content: space-around;
-  align-content: stretch;
-  align-items: center;
-}
-
-#ccLeft {
-  flex: 0 1 auto;
-  width: 50%;
-  align-self: auto;
-}
-
-#ccRight {
-  flex: 0 1 auto;
-  width: 50%;
-  align-self: center;
-}
-
-.ccOption {
-  margin: auto;
-  margin-bottom: 2.25ex;
-  text-align: center;
-}
-
-.ccRange {
-  vertical-align: top;
-  margin-left: 1.5ex;
-  margin-right: 1.5ex;
-}
-
-.ccButton[data-shortcut]:before {
-  content: " (" attr(data-shortcut) ") " !important;
-}
-
-.ccOptionTopLabel {
-  text-align: center;
-  font-weight: bold;
-  margin-bottom: -0.25ex;
-}
-
-.ccHeader {
-  text-align: center;
-  font-weight: bold;
-  margin-top: 2ex;
-  margin-bottom: 0.5ex;
-  font-size: 1.1rem;
-}
-
-.rangeValue {
-  margin-top: -0.25ex;
-}
-
-.textLink, .lineLink, .columnLink, .ccButton, .roomObject, .roomDirection, .rememberedRoomLink, .inventoryLink {
-  font-weight: bold;
-  color: #0000aa;
-}
-.textLink:hover, .lineLink:hover, .columnLink:hover, .ccButton:hover, .roomObject:hover, .roomDirection:hover, .rememberedRoomLink:hover, .inventoryLink:hover {
-  color: #0000ff;
-  cursor: pointer;
-}
-.textLink:hover:active, .lineLink:hover:active, .columnLink:hover:active, .ccButton:hover:active, .roomObject:hover:active, .roomDirection:hover:active, .rememberedRoomLink:hover:active, .inventoryLink:hover:active {
-  color: #000099;
-}
-.textLink[data-shortcut]:before, [data-shortcut].lineLink:before, [data-shortcut].columnLink:before, [data-shortcut].ccButton:before, [data-shortcut].roomObject:before, [data-shortcut].roomDirection:before, [data-shortcut].rememberedRoomLink:before, [data-shortcut].inventoryLink:before {
-  content: attr(data-shortcut) ") ";
-}
-
-.statusLink {
-  font-weight: bold;
-  color: #99f;
-}
-.statusLink:hover {
-  color: #ddf;
-  cursor: pointer;
-}
-.statusLink:hover:active {
-  color: #66d;
-}
-
-.roomName {
-  font-size: 1.2rem;
-  margin-bottom: 1ex;
-}
-
-.roomDescription {
-  text-indent: 1rem;
-  text-align: justify;
-  margin-top: 1ex;
-}
-
-.roomExitsHeader {
-  margin-top: 2ex;
-}
-
-.roomExit {
-  text-indent: 1.5rem;
-  margin-top: 1ex;
-}
-
-.rememberedRoomRow {
-  display: flex;
-  flex-direction: row;
-  flex-wrap: nowrap;
-  justify-content: space-between;
-  align-content: stretch;
-  align-items: stretch;
-}
-.rememberedRoomRow:hover {
-  background-color: rgba(0, 0, 0, 0.12);
-}
-.rememberedRoomRow.currentRoom > .rememberedRoomLink {
-  color: #000;
-  display: none;
-}
-.rememberedRoomRow.currentRoom > .rememberedRoomLink:hover {
-  cursor: auto;
-}
-.rememberedRoomRow.currentRoom > .rememberedRoomLink.name {
-  display: initial;
-}
-
-.rememberedRoomLink {
-  white-space: nowrap;
-  overflow: hidden;
-  order: 2;
-  flex: 0 0 auto;
-  align-self: auto;
-  padding: 0.6ex;
-  padding-left: 1ex;
-  padding-right: 1ex;
-}
-.rememberedRoomLink.name {
-  text-overflow: ellipsis;
-  padding-left: 1.5ex;
-  order: 1;
-  flex: 1 1 auto;
-}
-.rememberedRoomLink.name:hover {
-  overflow: visible;
-  white-space: initial;
-}
-
-p.contentOld {
-  color: #888;
-}
-p.contentOld:hover {
-  color: #000;
-}
-
-p.contentOld, p.content {
-  text-indent: 1rem;
-  text-align: justify;
-  padding: 0.5rem;
-  padding-left: 1.6rem;
-  padding-right: 1.6rem;
-}
-p.contentOld.centered, p.content.centered {
-  text-indent: 0px;
-  text-align: center;
-}
-
-.textIndenter {
-  margin-right: 1rem;
-}
-
-div.choiceContainer {
-  padding-bottom: 0.8rem;
-}
-div.choiceContainer:hover {
-  background-color: rgba(10, 10, 80, 0.05);
-}
-
-.roundButton, p.choice, .combatChoice {
-  padding: 0.3rem;
-  margin: 0.8rem;
-  margin-bottom: 0.3rem;
-  margin-top: 0.3rem;
-  border: solid 1px #0000aa;
-  border-radius: 1rem;
-  background-color: #fff;
-}
-.roundButton[data-shortcut]:before, p[data-shortcut].choice:before, [data-shortcut].combatChoice:before {
-  content: attr(data-shortcut) ") ";
-  font-weight: bold;
-  color: #0000aa;
-}
-.roundButton:hover, p.choice:hover, .combatChoice:hover {
-  cursor: pointer;
-  background-color: #eeeeff;
-  border-color: #0000ff;
-}
-.roundButton:hover[data-shortcut]:before, p.choice:hover[data-shortcut]:before, .combatChoice:hover[data-shortcut]:before {
-  color: #0000ff;
-}
-.roundButton:active, p.choice:active, .combatChoice:active {
-  background-color: #c8c8ff;
-  border-color: #000099;
-}
-.roundButton:active[data-shortcut]:before, p.choice:active[data-shortcut]:before, .combatChoice:active[data-shortcut]:before {
-  color: #000099;
-}
-
-p.choice {
-  text-indent: 0.5rem;
-  text-align: justify;
-}
-p.choice.picked {
-  color: #888;
-}
-
-.combatChoicesContainer {
-  display: flex;
-  flex-direction: row;
-  flex-wrap: wrap;
-  justify-content: center;
-  align-content: stretch;
-  align-items: stretch;
-}
-
-.combatChoice {
-  flex: 0 0 auto;
-  align-self: auto;
-  overflow: hidden;
-  padding: 0.6em;
-  margin: 0.3em;
-}
-
-p.turnStart {
-  display: flex;
-  flex-basis: 100%;
-  align-items: center;
-  font-size: 0.8rem;
-  font-weight: bold;
-  line-height: 1em;
-}
-p.turnStart::before {
-  margin-right: 10px;
-}
-p.turnStart::after {
-  margin-left: 10px;
-}
-p.turnStart::before, p.turnStart::after {
-  content: "";
-  flex-grow: 1;
-  border-bottom: solid 1px rgba(180, 0, 0, 0.6);
-}
-
-.horFlex {
-  display: flex;
-  flex-direction: row;
-  flex-wrap: nowrap;
-  justify-content: space-around;
-  align-content: stretch;
-  align-items: center;
-}
-
-.horFlexColumn {
-  flex: 0 1 auto;
-  width: 50%;
-  align-self: auto;
-}
-
-b.action {
-  color: #F00;
-}
-b.action::before {
-  content: " *";
-}
-b.action::after {
-  content: "* ";
-}
-
-.inventoryHeader {
-  margin-top: 1em;
-  margin-left: 1ex;
-}
-
-.inventoryRow {
-  display: flex;
-  flex-direction: row;
-  flex-wrap: nowrap;
-  justify-content: space-between;
-  align-content: stretch;
-  align-items: stretch;
-}
-.inventoryRow:hover {
-  background-color: rgba(0, 0, 0, 0.12);
-}
-
-.inventoryLink {
-  white-space: nowrap;
-  overflow: hidden;
-  order: 2;
-  flex: 0 0 auto;
-  align-self: auto;
-  padding: 0.6ex;
-  padding-left: 1ex;
-  padding-right: 1ex;
-}
-.inventoryLink.name {
-  text-overflow: ellipsis;
-  padding-left: 1.5ex;
-  order: 1;
-  flex: 1 1 auto;
-}
-.inventoryLink.name:hover {
-  overflow: visible;
-  white-space: initial;
-}
-
-.inventoryGold {
-  text-align: center;
-  font-size: 1rem;
-  font-weight: normal;
-  padding-top: 1ex;
-}
-
-#inventoryTarget {
-  padding-bottom: 1ex;
-}
-
-.appearanceDescription {
-  padding: 0.5ex;
-  text-align: justify;
-  text-indent: 1em;
-  line-height: 1.5em;
-}

+ 99 - 0
tools/CombatDescriptionGenerator.html

@@ -0,0 +1,99 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <title>Title</title>
+    <style type="text/css">
+        #code {
+            position: fixed;
+            top: 0px;
+            right: 1ex;
+        }
+
+        textarea {
+            width: 400px;
+            height: 60px;
+        }
+    </style>
+</head>
+<body>
+<b>Name of this Description:</b><input type="text" id="name" value="Unnamed" /><br/>
+<b>Attacker:</b><input type="text" id="attacker" value="Person" /><br/>
+<b>Target:</b><input type="text" id="target" value="Person" /><br/>
+<b>Weapon(s):</b><input type="text" id="weapons" value="Thing" /><br/>
+
+<br/><b>Description on Miss:</b><br/>
+<textarea id="missDesc"></textarea><br/>
+
+<br/><b>Description on Critical Hit:</b><br/>
+<textarea id="critDesc"></textarea><br/>
+
+<br/><b>Description on Hit:</b><br/>
+<textarea id="hitDesc"></textarea><br/>
+
+<br/><b>Description on Knocked Down:</b><br/>
+<textarea id="knockedDesc"></textarea><br/>
+
+<br/><b>Description on Knocked Off:</b><br/>
+<textarea id="koDesc"></textarea><br/>
+
+<br/><b>Description on Killed:</b><br/>
+<textarea id="killedDesc"></textarea><br/>
+
+<pre id="code">
+(new CombatDescription("<span id="nameCode"></span>"))
+    .setDescriptionFunction((actor, target, weapons, markers) => {
+        let say = new Say();
+
+        if (markers.includes(CombatHit.MISS)) {
+            say.add(<span id="missCode"></span>);
+        } else if (markers.includes(CombatHit.HIT)) {
+            say.add(<span id="hitCode"></span>);
+        } else if (markers.includes(CombatHit.CRITICAL)) {
+            say.add(<span id="criticalCode"></span>);
+        }
+
+        say.add(" ");
+
+        if (markers.includes(CombatResult.KILLED)) {
+            say.add(<span id="killedCode"></span>);
+        } else if (markers.includes(CombatResult.KNOCKED_OFF)) {
+            say.add(<span id="koCode"></span>);
+        } else if (markers.includes(CombatResult.KNOCKED)) {
+            say.add(<span id="knockedCode"></span>);
+        }
+
+        return say;
+    })
+    .addUnit()
+        .setActor(<span id="attackerCode"></span>)
+        .setTarget(<span id="targetCode"></span>)
+        .setWeapon(<span id="weaponCode"></span>)
+        .addMarker(AdaptiveDifferential.FULLYADAPTIVE(CombatHit.MISS, CombatHit.HIT, CombatHit.CRITICAL))
+        .addMarker(AdaptiveDifferential.FULLYADAPTIVE(CombatResult.KNOCKED, CombatResult.KILLED, CombatResult.KNOCKED_OFF))
+;
+</pre>
+
+<script type="text/javascript">
+    function updateCode () {
+        document.getElementById("nameCode").innerText = document.getElementById("name").value;
+        document.getElementById("missCode").innerText = document.getElementById("missDesc").value;
+        document.getElementById("hitCode").innerText = document.getElementById("hitDesc").value;
+        document.getElementById("criticalCode").innerText = document.getElementById("critDesc").value;
+        document.getElementById("killedCode").innerText = document.getElementById("killedDesc").value;
+        document.getElementById("koCode").innerText = document.getElementById("koDesc").value;
+        document.getElementById("knockedCode").innerText = document.getElementById("knockedDesc").value;
+        document.getElementById("attackerCode").innerText = document.getElementById("attacker").value;
+        document.getElementById("targetCode").innerText = document.getElementById("target").value;
+        document.getElementById("weaponCode").innerText = document.getElementById("weapons").value;
+    }
+    updateCode();
+
+    let inputs = document.getElementsByTagName("input");
+    let textareas = document.getElementsByTagName("textarea");
+    (new Array(...inputs, ...textareas)).forEach(element => {
+        element.addEventListener("keyup", updateCode);
+    });
+</script>
+</body>
+</html>

+ 40 - 12
tools/FuckingDescription.html

@@ -3,22 +3,50 @@
 <head>
     <meta charset="UTF-8">
     <title>Title</title>
+    <style type="text/css">
+        #code {
+            position: fixed;
+            top: 0px;
+            right: 1ex;
+        }
+
+        textarea {
+            width: 400px;
+            height: 60px;
+        }
+    </style>
 </head>
 <body>
-<script type="text/javascript">
-    // (new FuckingDescription("Specific Orc Starts Cumming in Vagina"))
-    //     .setDescription(new Say("Specific Orc Starts Cumming in Vagina"))
-    //     .addUnit()
-    //     .setFucker(randomOrc)
-    //     .setHole(WorldState.player.getPart(HumanoidVagina))
-    //     .addMarker(FuckingState.CUM_START)
-    //     .setStick(randomOrc.getPart(HumanoidPenis));
-    FuckedTarget -> WorldState.player
-    Fucker -> Person
-    SetStick -> SexStick
-    SetHole -> SexHole
+<b>Name of this Description:</b><input type="text" id="name" value="Unnamed" /><br/>
+<br/><b>Description:</b><br/>
+<textarea id="missDesc"></textarea><br/>
+<a href="#" id="addUnit">Add Unit</a>
 
+<pre id="code">
+(new CombatDescription(""))
+let fd = (new FuckingDescription("<span id="nameCode"></span>"));
+spitroast.setDescription(new Say(<span id="descriptionCode"></span>))
+    .addUnit()
+    .setFucker(OrcDebugger)
+    .setHole(HumanoidVagina)
+    .setStick(HumanoidPenis);
+spitroast.addUnit()
+    .setFucker(OrcDebugger)
+    .setHole(HumanoidHead)
+    .setStick(HumanoidPenis);
+</pre>
+
+<script type="text/javascript">
+    function updateCode () {
+        document.getElementById("nameCode").innerText = document.getElementById("name").value;
+    }
+    updateCode();
 
+    let inputs = document.getElementsByTagName("input");
+    let textareas = document.getElementsByTagName("textarea");
+    (new Array(...inputs, ...textareas)).forEach(element => {
+        element.addEventListener("keyup", updateCode);
+    });
 </script>
 </body>
 </html>

Некоторые файлы не были показаны из-за большого количества измененных файлов