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(> { // 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 = 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 : { map : region.map, room : rooma, region : region } }).then(); await RegionRandom.rulebookPlaceRoom.execute({ noun : { 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); // } // 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) => { let person = runner.noun; return person.AI.picksShinies; }, code : (runner : RulebookRunner) => { 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 < 2; 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); // (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 CombatPokeDescription("Hitting Orc")) .setDescriptionFunction((target, markers) => { let say = new Say(new SayBold(target), ": "); if (markers.includes(CombatPokeResult.NOHEAT)) { say.add( new OneOf(OneOf.PURELY_AT_RANDOM, "Heh, ain't that cute?", "Pathetic." ) ); } else if (markers.includes(CombatPokeResult.ANNOYED)) { say.add( new OneOf(OneOf.PURELY_AT_RANDOM, "Oy, stahp that.", "Ya' betta' stahp that before I getting mad." ) ); } else if (markers.includes(CombatPokeResult.AGGROED)) { say.add( new OneOf(OneOf.PURELY_AT_RANDOM, "Aight. The axe it is." ) ); } return say; }) .addUnit() .setTarget(OrcDebugger) .addMarker(AdaptiveDifferential.FULLYADAPTIVE(CombatPokeResult.AGGROED, CombatPokeResult.NOHEAT, CombatPokeResult.ANNOYED)); (new CombatDescription("Allranging Fists")) .setDescriptionFunction((actor, target, weapons, markers) => { let say = new Say("You attack ", new SayThe(), target, " with your fists"); if (markers.indexOf(CombatHit.MISS) != -1) { say.add(", but you 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 your attack knocks ", new SayHimHerIt(target), " on the floor."); } else if (markers.indexOf(CombatResult.KNOCKED_OFF) != -1) { say.add(", the strength of your attack knocks ", new SayHimHerIt(target), " unconscious."); } else if (markers.indexOf(CombatResult.KILLED) != -1) { say.add(", ", new SayHeSheIt(target), " dies."); } else { say.add("."); } return say; }) .addUnit() .setActor(WorldState.player) .setTarget(Person) .addMarker(AdaptiveDifferential.FULLYADAPTIVE(CombatHit.HIT, CombatHit.CRITICAL, CombatHit.MISS)) .addMarker(AdaptiveDifferential.FULLYADAPTIVE(CombatResult.KILLED, CombatResult.KNOCKED, CombatResult.KNOCKED_OFF));