JavaScript is required. Please enable it to continue.
Your browser lacks required capabilities. Please upgrade it or switch to another to continue.
Loading…
<<set $building to lib.findInArray($town.buildings, "key", $currentPassage.key)>> <<set $building.brew to lib.createAlchemy({ type: "brewing potion" })>><<set $currentPassage.availableRelationships to lib.alchemistCustomers>> <<set $associatedNPC to $npcs[$building.associatedNPC.key]>> <<include "PrintImage">> <span class='flex-line'><<button "Edit $building.name" $building.initPassage>><</button>></span> <h1 class="interactive-only">$building.name</h1><span @id="$building.key"></span><p><<print lib.getAlchemistIntroduction($building, $associatedNPC)>></p> <section><h3>Chemist</h3><p><<print lib.articles.output($associatedNPC.descriptor)>> <<print $associatedNPC.greeting.random()>> <<print ['when you come inside', 'after finishing with another customer', 'as soon as you come through the door', 'when you come up to the counter', 'while you look around'].random()>>. <<print $associatedNPC.heshe.toUpperFirst()>> introduces $associatedNPC.himherself as <<profile $associatedNPC>>, the $associatedNPC.owner of the shop, and asks what $associatedNPC.heshe can do for you.</p> <<linkreplace "Talk with $associatedNPC.name" t8n>> <h6>$associatedNPC.name</h6><<include "ChemistTalk">><</linkreplace>></section> <span class="interactive-only"><<link "Generate plothook">><<set $associatedNPC.plot to setup.alchemistMission($town)>><</link>></span><<liveblock>><<if def $associatedNPC.plot>><div id="plothook"><<print ["When you say that you're adventurers, $associatedNPC.hisher $associatedNPC.eyes eyes light up, and $associatedNPC.heshe says", "You chat for a while, and then $associatedNPC.firstName says ", "You discuss business, and when you talk about your adventures, $associatedNPC.firstName asks "].random()>> "$associatedNPC.plot"</div><</if>><</liveblock>> <<include "RandomPotion">> <<include "AlchemistSell">> <<details "CreateNewNpc" "Customers">>
<<linkreplace "<h4>Buy something</h4>">><h6>$building.name's Potions and Wares</h6><<nobr>> <div class="descriptive"> <<switch $town.type>> <<case "city">> $associatedNPC.name smiles, and says "You're looking to buy something? Name it, here at $building.name, we stock everything the average adventurer could possibly need! <<case "town">> $associatedNPC.name says "Well, what is it you need? We specialise in <<print ["healing", "combat", "regenerative", "medicinal", "the more... poisonous", "healing", "minor", "major"].random()>> potions, but can do just about anything for you. <<case "village">> $associatedNPC.name says "Sure, what are you after? <<print lib.alchemistData.ingredients.random().toUpperFirst()>>? Or perhaps a potion? <<case "hamlet">> $associatedNPC.name smiles, and says "What are you after? We can do just about anything you could possibly need; salts, herbs, <<print lib.alchemistData.ingredients.random()>>, <<print lib.alchemistData.ingredients.random()>>, <<print lib.alchemistData.ingredients.random()>>, you name it! <</switch>><<print lib.closestMatch(lib.alchemistData.get.priceTalk($building), "priceTalk", "priceModifier", "wealth", $building.priceModifier, $building.roll.wealth)>>"</div> <<switch $town.type>> <<case "city">> <<set _availability to 4>> <<case "town">> <<set _availability to 3>> <<case "village">> <<set _availability to 2>> <<case "hamlet">> <<set _availability to 1>> <<default>> <<set _availability to 3>> <</switch>> <<set _selling to { category: { 'adventuring gear': 'adventuring gear', 'tools': 'tools', 'consumables': 'consumables' } }>> <<shop $building, _selling>> <</nobr>><</linkreplace>>
$associatedNPC.firstName looks <<print setup.npcData.currentMood.random()>>, and idly shifts a box of <<print lib.alchemistData.ingredients.random()>> as $associatedNPC.heshe talks. The $associatedNPC.raceNote <<print $associatedNPC.chitchat.random()>> as you peruse the shop. $associatedNPC.firstName tells you that $associatedNPC.heshe is working on <<print lib.articles.output($building.brew.potionPurpose)>>, and points to the <<linkappend $building.brew.containerDescription t8n>>. Looking inside the $building.brew.vesselType, you see <<print lib.articles.output($building.brew.liquidDescription)>> bubbling away<</linkappend>>.
<<silently>><<set $currentPassage.index to lib.findIndexInArray($town.buildings, "key", $currentPassage.key)>> <<run lib.clampRolls($town.buildings[$currentPassage.index].roll)>> <<set $town.buildings[$currentPassage.index].priceModifier to Math.clamp($town.buildings[$currentPassage.index].priceModifier, -10, 10)>> <<set _name to $town.buildings[$currentPassage.index].name>><</silently>> <<linkreplace "Rename <strong>_name</strong>">><h3><label name="Rename the town"><<textbox "$town.buildings[$currentPassage.index].name" _name>></h3><</linkreplace>> <<button "Delete _name" Start>><<run setup.deleteBuilding($town, $town.buildings[$currentPassage.index])>><</button>> <span class="tip" data-tippy-content="How wealthy is the patronage?"><label for="numberslider-input-townbuildingscurrentpassageindexrollwealth"> Alchemist Wealth</label>: <<numberslider "$town.buildings[$currentPassage.index].roll.wealth" $town.buildings[$currentPassage.index].roll.wealth 1 100 1>></span> <span class="tip" data-tippy-content="How large is the shop?"><label for="numberslider-input-townbuildingscurrentpassageindexrollsize"> Alchemist Size</label>: <<numberslider "$town.buildings[$currentPassage.index].roll.size" $town.buildings[$currentPassage.index].roll.size 1 100 1>></span> <span class="tip" data-tippy-content="Is it well known, or is it a hobby shop?"><label for="numberslider-input-townbuildingscurrentpassageindexrollreputation"> Alchemist Reputation</label>: <<numberslider "$town.buildings[$currentPassage.index].roll.reputation" $town.buildings[$currentPassage.index].roll.reputation 1 100 1>></span> <span class="tip" data-tippy-content="How clean is the alchemist?"><label for="numberslider-input-townbuildingscurrentpassageindexrollcleanliness"> Alchemist Cleanliness</label>: <<numberslider "$town.buildings[$currentPassage.index].roll.cleanliness" $town.buildings[$currentPassage.index].roll.cleanliness 1 100 1>></span> <span class="tip" data-tippy-content="How skilled is the alchemist? Can they make complex potions, or are they limited to a simple healing potion?"><label for="numberslider-input-townbuildingscurrentpassageindexrollexpertise"> Alchemist Expertise</label>: <<numberslider "$town.buildings[$currentPassage.index].roll.expertise" $town.buildings[$currentPassage.index].roll.expertise 1 100 1>></span> <span class="tip" data-tippy-content="How busy is the alchemist?"><label for="numberslider-input-townbuildingscurrentpassageindexrollactivity"> Alchemist Activity</label>: <<numberslider "$town.buildings[$currentPassage.index].roll.activity" $town.buildings[$currentPassage.index].roll.activity 1 100 1>></span> <span class="tip" data-tippy-content="How do the prices here compare to your average alchemist?">Alchemist <label for="numberslider-input-townbuildingscurrentpassageindexrollmodifier"> Price Modifier</label>: <<numberslider "$town.buildings[$currentPassage.index].priceModifier" $town.buildings[$currentPassage.index].priceModifier -10 10 1>></span> When you're ready, here's the [[alchemist|AlchemistOutput]]
<span class="click-and-remove-link"><<link "<h6>Generate a random potion!</h6>">> <<set $randomPotion to lib.createAlchemy({ type: "potion" })>><<update>> <</link>></span> <<liveblock>><<if $randomPotion>><span id="randpotion"> <div class='descriptive'><h6>$randomPotion.titleReadout</h6>$randomPotion.descriptionReadout</div> <blockquote>$randomPotion.effectReadout</blockquote> </span><</if>><</liveblock>>
<<linkreplace "<h4>Talk with $associatedNPC.name</h4>" t8n>> <h6>$associatedNPC.name</h6>$associatedNPC.firstName looks <<print setup.npcData.currentMood.random()>>, and while you chat, $associatedNPC.heshe moves around the shop, preparing a new project for work. $associatedNPC.firstName <<print ["loves working in $building.name", "hates working in $building.name", "finds working in the $building.name boring", "finds working in the smithy fun", "finds working in the $building.name rather lonely", "wishes $associatedNPC.heshe could go adventuring instead of working here"].random()>>, and says that the patrons of $building.name are <<print ["alright, especially the farmers, who are pretty decent blokes", "a bit stingy, especially the farmers", "somewhat stingy", "fine, but rather dull", "way too exciting for $associatedNPC.firstName's tastes", "nice and friendly", "nice and friendly, but rather boring- nothing interesting is ever ordered", "just a bunch of hicks", "a bunch of weirdos", "pretty normal stock, nothing out of the ordinary", "a bit rude and stuck up", "fairly generous with their coin"].random()>>. /* <span class="click-and-remove-link"><<link "What are you working on?">><<set $associatedNPC.currentProject to setup.createBlacksmithProject($town, $building, $associatedNPC)>><<update>><</link>></span> */ /* <span class="click-and-remove-link"><<link "Test">><<set $associatedNPC.currentProject to "GREAT SUCCESS">><<update>><</link>></span> */ <<liveblock>><<if def $associatedNPC.currentProject>><span id="currentProject">$associatedNPC.currentProject</span><</if>><</liveblock>><</linkreplace>>
<<silently>> <<set $currentPassage.index to lib.findIndexInArray($town.buildings, "key", $currentPassage.key)>> <<run lib.clampRolls($town.buildings[$currentPassage.index].roll)>> <<set $town.buildings[$currentPassage.index].priceModifier to Math.clamp($town.buildings[$currentPassage.index].priceModifier, -10, 10)>> <<set _name to $town.buildings[$currentPassage.index].name>><</silently>> <<linkreplace "Rename <strong>_name</strong>">><h3><label name="Rename the smithy"><<textbox "$town.buildings[$currentPassage.index].name" _name>></label></h3><</linkreplace>> <<button "Delete _name" Start>><<run setup.deleteBuilding($town, $town.buildings[$currentPassage.index])>><</button>> <span class="tip" data-tippy-content="How wealthy is the patronage?"><label for="numberslider-input-townbuildingscurrentpassageindexrollwealth">Smithy Wealth</label>: <<numberslider "$town.buildings[$currentPassage.index].roll.wealth" $town.buildings[$currentPassage.index].roll.wealth 1 100 1>></span> <span class="tip" data-tippy-content="How large is the smithy?"><label for="numberslider-input-townbuildingscurrentpassageindexrollsize">Smithy Size</label>: <<numberslider "$town.buildings[$currentPassage.index].roll.size" $town.buildings[$currentPassage.index].roll.size 1 100 1>></span> <span class="tip" data-tippy-content="Is it well known, or does the guy just shoe horses?"><label for="numberslider-input-townbuildingscurrentpassageindexrollreputation">Smithy Reputation</label>: <<numberslider "$town.buildings[$currentPassage.index].roll.reputation" $town.buildings[$currentPassage.index].roll.reputation 1 100 1>></span> <span class="tip" data-tippy-content="How clean is the smithy?"><label for="numberslider-input-townbuildingscurrentpassageindexrollcleanliness">Smithy Cleanliness</label>: <<numberslider "$town.buildings[$currentPassage.index].roll.cleanliness" $town.buildings[$currentPassage.index].roll.cleanliness 1 100 1>></span> <span class="tip" data-tippy-content="How skilled is the blacksmith?"><label for="numberslider-input-townbuildingscurrentpassageindexrollexpertise">Smithy Expertise</label>: <<numberslider "$town.buildings[$currentPassage.index].roll.expertise" $town.buildings[$currentPassage.index].roll.expertise 1 100 1>></span> <span class="tip" data-tippy-content="How busy is the blacksmith? Do they have a backlog a mile long, or are they sitting idle?"><label for="numberslider-input-townbuildingscurrentpassageindexrollactivity">Smithy Activity</label>: <<numberslider "$town.buildings[$currentPassage.index].roll.activity" $town.buildings[$currentPassage.index].roll.activity 1 100 1>></span> <span class="tip" data-tippy-content="How do the prices here compare to your average blacksmith?">Smithy <label for="numberslider-input-townbuildingscurrentpassageindexrollmodifier">Price Modifier</label>: <<numberslider "$town.buildings[$currentPassage.index].priceModifier" $town.buildings[$currentPassage.index].priceModifier -10 10 1>></span> When you're ready, here's the [[smithy|SmithyOutput]]
<<set $building to lib.findInArray($town.buildings, "key", $currentPassage.key)>> <<set $associatedNPC to $npcs[$building.associatedNPC.key]>> <<set $currentPassage.availableRelationships to lib.smithyCustomers>> <<include "PrintImage">> <span class='flex-line'><<button "Edit $building.name" $building.initPassage>><</button>></span> <h1 class="interactive-only">$building.name</h1><span @id="$building.key"></span> <p>You make your way down <<print lib.createTippyFull($town.roads[$building.road].description, $town.roads[$building.road].name)>>, and enter $building.name and see that inside, the $building.structure.descriptor is $building.size. <<print lib.closestMatch(lib.smithyData.get.lookAround($building), 'note', 'cleanliness', 'wealth', $building.roll.cleanliness, $building.roll.wealtYh)>> There is a blacksmith currently <<print $associatedNPC.idle.random()>>.</p> <p><<print lib.closestMatch(lib.smithyData.get.expertise($building), 'note', 'expertise', 'wealth', $building.roll.expertise, $building.roll.wealth)>> <section><h3>Blacksmith</h3><p>The blacksmith <<print $associatedNPC.greeting.random()>> <<print ['when you come inside', 'after finishing with another customer', 'as soon as you come through the door', 'when you come up to the counter', 'while you look around'].random()>>. <<print $associatedNPC.heshe.toUpperFirst()>> introduces $associatedNPC.himherself as <<profile $associatedNPC>>, the $associatedNPC.owner of the smithy, and asks what $associatedNPC.heshe can do for you.</p> <<include "BlacksmithTalk">></section> <span class="temporarily-removed click-and-remove-link"><<link "<h4>Generate plothook</h4>">><<set $associatedNPC.plot to setup.blacksmithMission($town)>><<update>><</link>></span><<liveblock>><<if def $associatedNPC.plot>><div id="plothook"><<print ["When you say that you're adventurers, $associatedNPC.hisher $associatedNPC.eyes eyes light up, and $associatedNPC.heshe says", "You chat for a while, and then $associatedNPC.firstName says ", "You discuss business, and when you talk about your adventures, $associatedNPC.firstName asks "].random()>> "$associatedNPC.plot"</div><</if>><</liveblock>> <<include "SmithySell">> <<details "CreateNewNpc" "Customers">>
<<linkappend "<h4>Buy something</h4>" t8n>><<nobr>><div class="descriptive"> <<print lib.getSellingTalk($town, $building, $associatedNPC)>></div> <<switch $town.type>> <<case "city">> <<set _availability to 4>> <<case "town">> <<set _availability to 3>> <<case "village">> <<set _availability to 2>> <<case "hamlet">> <<set _availability to 1>> <<default>> <<set _availability to 3>> <</switch>> <<set _selling to { category: { weapon: 'weapons', armour: 'armour', 'adventuring gear': 'adventuring gear', 'tools': 'tools' } }>> <<shop $building, _selling>> <</nobr>><</linkappend>>
<div id="BuildingRelationshipsTable" > <<set _buildingRelationships to lib.findReciprocalRelationships( $town, lib.findInArray($town.buildings, "key", $currentPassage.key), null, 'building')>> <<if _buildingRelationships.length > 0>> <div class='classTable'> <table> <tr> <th>Name</th> <th>Race</th> <th>Occupation</th> <th>Relationship</th> </tr> <<for _i to 0; _i lt _buildingRelationships.length; _i++>><<set _profession to lib.findProfession($town, $npcs[_buildingRelationships[_i].npcKey])>> <tr> <td><<profile $npcs[_buildingRelationships[_i].npcKey]>></td> <td><<print $npcs[_buildingRelationships[_i].npcKey].race.toUpperFirst()>></td> <td><<print lib.createTippyFull(_profession.description.toUpperFirst(), $npcs[_buildingRelationships[_i].npcKey].profession.toUpperFirst())>></td> <td> <<if _buildingRelationships[_i].description || _buildingRelationships[_i].reciprocalRelationship>> <<print lib.createTippyFull( _buildingRelationships[_i].description || lib.findInArray($town.buildings, "key", _buildingRelationships[_i].otherKey).name + " is " + lib.articles.output(_buildingRelationships[_i].reciprocalRelationship) + " to " + $npcs[_buildingRelationships[_i].npcKey].firstName, lib.toTitleCase(_buildingRelationships[_i].relationship))>> <<else>> <<print lib.toTitleCase(_buildingRelationships[_i].relationship)>> <</if>> </td> </tr> <</for>> </table> </div> <</if>> </div>
<span class="interactive-only"> <<include "NPCListboxes">> -- <<button "Create NPC">> <<run lib.logger.info('Creating new NPC!')>> <<set $currentPassage.randomNPC to setup.createReciprocalRelationshipNpc( $town, $currentPassage, $npcs[$currentPassage?.associatedNPC?.key] || $npcs[$associatedNPC.key] || $npcs[$building.associatedNPC.key], $currentPassage.availableRelationships, { base: _randomNPC, targetKey: _stringRelationship })>> <<update>> <<include "RandomNPCPrep">> <<replace "#BuildingRelationshipsTable">><<include "BuildingRelationshipsTable">><</replace>> <<replace "#NPCListboxes">><<include "NPCListboxes">><</replace>> <<run tippy('.tip')>> <</button>> <<liveblock>> <<if $currentPassage.randomNPC>> <div id="NPC" class="descriptive no-indent"> <h3>$currentPassage.randomNPC.name</h3> <<print lib.firstCharacter(lib.articles.output($currentPassage.randomNPC.descriptor).toUpperFirst())>> is currently <<print $currentPassage.randomNPC.idle.random()>> in the corner. You strike up conversation with $currentPassage.randomNPC.himher, and the $currentPassage.randomNPC.descriptor introduces $currentPassage.randomNPC.himherself as <<profile $currentPassage.randomNPC>>, <<print lib.articles.find($currentPassage.randomNPC.profession)>> <<tooltip $currentPassage.randomNPC.profession lib.professions[$currentPassage.randomNPC.profession].description.toUpperFirst()>>. </div> <</if>> <</liveblock>> </span> <<include "BuildingRelationshipsTable">>
<<set _randomNPC to lib.randomiseNPC()>> <<set _relationships to $currentPassage.availableRelationships || "visitor">> <<set _stringRelationship to _relationships.random().relationshipDescription>>
<<if !_randomNPC>><<include "RandomNPCPrep">><</if>> <<liveblock>> <span id="NPCListboxes" class='hide-on-print'> <label name="NPC gender" for="listbox-randomnpcgender"><<listbox "_randomNPC.gender" autoselect>><<option "" null>><<optionsfrom ["man", "woman"]>><</listbox>></label> -- <label name="NPC age stage" for="listbox-randomnpcagestage"><<listbox "_randomNPC.ageStage" autoselect>><<option "" null>><<optionsfrom ["young adult", "settled adult", "elderly"]>><</listbox>></label> -- <label name="NPC race" for="listbox-randomnpcrace"><<listbox "_randomNPC.race" autoselect>><<option "" null>><<optionsfrom Object.keys(lib.raceTraits)>><</listbox>></label> /* -- <<listbox "_randomNPC.profession" autoselect>><<option "" null>><<optionsfrom Object.keys(lib.professions)>><</listbox>></label>*/ -- <<if passage() is "FactionProfile">> <label name="NPC relationship" for="listbox-stringrelationship"><<listbox "_randomNPC.profession" autoselect>><<option "" null>><<optionsfrom $currentPassage.availableRelationships>><</listbox>></label> <<else>> <label name="NPC relationship" for="listbox-stringrelationship"><<listbox "_stringRelationship" autoselect>><<optionsfrom _relationships.map(obj => { return obj.relationshipDescription })>><</listbox>></label> <</if>> </span> <</liveblock>>
<<set $building to lib.findInArray($town.buildings, "key", $currentPassage.key)>><<include "PrintImage">> \<h1 class="interactive-only">$building.name</h1><span @id="$building.key"></span> <<for _i to 0; _i < $building.PassageFormat.length; _i++>> <<print $building.PassageFormat[_i]>> <</for>>
<<silently>><<set $currentPassage.index to lib.findIndexInArray($town.buildings, "key", $currentPassage.key)>> <<run lib.clampRolls($town.buildings[$currentPassage.index].roll)>> <<set $town.buildings[$currentPassage.index].priceModifier to Math.clamp($town.buildings[$currentPassage.index].priceModifier, -10, 10)>> <<set _name to $town.buildings[$currentPassage.index].name>><</silently>> <<nobr>><<link "reroll">> <<replace "#name">> \<<set $town.buildings[$currentPassage.index].name to lib.createname()>> <<textbox "$town.buildings[$currentPassage.index].name" _name>> <</replace>><</link>> <span id="name"><label name="Rename this building"><<textbox "$town.buildings[$currentPassage.index].name" _name>></label></span><</nobr>> <<button "Delete _name" Start>><<run setup.deleteBuilding($town, $town.buildings[$currentPassage.index])>><</button>> <<nobr>><<for _key, _obj range lib.townData.rollData.sliderTooltips>> <<if _obj.isHidden>> <<continue>> <<else>> <tr><td class="tip" @data-tippy-content=lib.townData.rollData.sliderTooltips[_key].tooltip><label for="townrollkey"><<print lib.townData.rollData.sliderTooltips[_key].preceding>></label></td><td style="21vw"> <<numberslider "$town.roll[_key]" $town.roll[_key] 1 100 1>></td></tr><</if>> <</for>><</nobr>> When you're ready, here's the [[output|TavernOutput]]
<<set $building to lib.findInArray($town.buildings, "key", $currentPassage.key)>> <<include "PrintImage">> <h1 class="interactive-only"><<print lib.toTitleCase($building.name)>></h1><span @id="$building.key"></span> <p>This $building.size $building.wordNoun was built by $building.builtBy $building.age, and is $building.condition. As you approach, <<print lib.castleLocation[$town.location].vignette.random()>>.</p> <<if $building.namesake>><p>It is named after <<profile $building.namesake>>, <<print lib.articles.output($building.namesake.profession)>>.</p><</if>><p>It is known for $building.knownFor, and is worth defending because $building.defense.reason. The castle needs assistance $building.lookingFor.</p> <h3>Ruler</h3><p>$building.name is ruled by $building.ruler.type. It was $building.ruler.acquisitionMethod. <<profile $building.associatedNPC>> is currently looking for $building.ruler.lookingFor.</p> <h3>Defenses</h3><p>Its outer walls are defended by $building.defense.outerWalls, and the lands it encompasses are $building.landSizeDescriptive.</p> <p>The $building.wordNoun's inner walls feature $building.defense.innerWalls, protecting the castle itself, which is $building.sizeDescriptive.</p> <<include "CastleDungeon">> <<details "BuildingRelationshipsTable" "Relationships">> <<button "Create a historical siege">><<set $building.siege to setup.castle.siege.create($town)>><<update>><</button>> <span id="siege"><<liveblock>><<if $building.siege>><section class='descriptive'><h3>$building.siege.name</h3>$building.siege.readout</section><</if>><</liveblock>></span>
<section><h2>Dungeon</h2> <p>The $building.dungeon.wordNoun located $building.dungeon.location is known as $building.dungeon.name, and was built $building.dungeon.age.</p> <p>It is known for $building.dungeon.knownFor, and consists of $building.dungeon.format. It is rumoured that hidden inside the dungeon is $building.dungeon.secret.</p></section> <section><h4>Cells</h4><p>The cells inside the dungeon are $building.dungeon.cells.condition, and prisoners are kept in $building.dungeon.cells.format.</p> <p>Prisoners of $building.name are treated $building.dungeon.cells.prisoners.treatment. The jailer, <<profile $building.dungeon.associatedNPC>>, is $building.dungeon.jailerType.</p></section> <span class="click-and-remove-link"><<link "Meet a prisoner">><<set $building.dungeon.prisoner to setup.castle.dungeon.cells.prisoners.create($town, $building)>><<update>><</link>></span> <div id="prisoners"><<liveblock>><<if def $building.dungeon.prisoner>>$building.dungeon.prisoner<</if>><</liveblock>></div>
<<set $building to lib.findInArray($town.buildings, "key", $currentPassage.key)>><h1 class="interactive-only">$building.name</h1><span @id="$building.key"></span><p>$building.name is $building.location and was built $building.age. It is known for $building.knownFor, and consists of $building.format. It is rumoured that hidden inside the dungeon is $building.secret.</p> <h4>Cells</h4><p>The cells inside the dungeon are $building.cells.condition, and prisoners are kept in $building.cells.format.</p> <p>Prisoners of $building.name are treated $building.cells.prisoners.treatment. The jailer, <<profile $building.associatedNPC>>, is $building.jailerType.</p> <span class="click-and-remove-link"><<link "Meet a prisoner">><<set $building.prisoner to setup.castle.dungeon.cells.prisoners.create($town, $building)>><<update>><</link>></span> <<liveblock>><div id="prisoners"><<if $building.prisoner>>$building.prisoner<</if>></div><</liveblock>> <<include "BuildingRelationshipsTable">>
<<if ndef lib.findInArray($town.buildings, "key", $currentPassage.key).parentKey>> <<include "StandaloneDungeon">> <<else>> <<include "CastleDungeon">> <</if>>
<<set $building to lib.findInArray($town.buildings, "key", $currentPassage.key)>> <<set $currentPassage.availableRelationships to lib.docksCustomers>> <<include "PrintImage">> <h1 class="interactive-only"><<print $building.name.toUpperFirst()>></h1><span @id="$building.key"></span> <p>You walk down to the docks, which are known best for $building.notableFeature. You notice $building.notice.</p> <p>The docks are $building.sizeDescriptive, and is $building.cleanlinessDescriptive. It's currently $building.activity.</p> <<button "Create a ship">><<run setup.docks.ships.create($town, $building)>><<update>><</button>> <<include "ShipList">> <<details "CreateNewNpc" "People Around">>
<<liveblock>><<nobr>> <div id="ships"> <div class='classTable'> <table> <tr> <th>Name</th> <th>Type</th> <th class="interactive-only">Delete</th> </tr> <<for _key, _ship range $building.ships>> <<capture _key, _ship>> <tr> <td> <<link "_ship.name" ShipProfile>> <<set $currentPassage to {shipKey: _key}>> <</link>> </td> <td> <<print _ship.type.toUpperFirst()>> </td> <td class="interactive-only"> <<link "x">> <<set _deleted to $building.ships[_ship.key]>> <<run setup.deleteNPC(_deleted.captain)>><<if def $building.ships[_ship.key].crew>><<run setup.deleteNPC($building.ships[_ship.key].crew)>><</if>> <<run delete $town.buildings[$currentPassage.index].ships[_ship.key]>> <<update>> <<notify 5000>>Deleted _deleted.name<</notify>> <</link>> </td> </tr> <</capture>> <</for>> </table> </div> </div> <</nobr>><</liveblock>>
<<set $currentShip to $building.ships[$currentPassage.shipKey]>><h1>$currentShip.name</h1>This is the $currentShip.type $currentShip.name. It has a crew of $currentShip.crewMen and is commanded by $currentShip.captainType, <<print setup.profile($currentShip.captain)>>. <<if $currentShip.masts === 1>> The ship has one mast, which is $currentShip.rigging rigged <<elseif $currentShip.masts > 1>> The ship has $currentShip.masts $currentShip.rigging rigged masts <<else>> The ship has no masts<</if>> <<if $currentShip.hasOars is true>>, and is <<if $currentShip.masts > 0>>, also <</if>>propelled via oars<</if>>. It is <<if settings.showMetric === true>> <<print ($currentShip.length * 0.0254).toFixed(1) + " metres.">> <<else>> <<set _feet = Math.trunc($currentShip.length / 12)>> <<print _feet + " feet.">> <</if>> It is <<print lib.articles.output($currentShip.size)>> <<print $currentShip.purpose.random()>> ship $currentShip.cleanliness. <br>The hull of the ship $currentShip.hull. On board you notice $currentShip.detail. The crew are currently $currentShip.event. <br><<link "Meet one of the Crew">><<set $currentShip.crew to setup.docks.ships.crew.create($town, $building)>><<update>><</link>> <<liveblock>><div id="crew"><<if def $currentShip.crew>>$currentShip.crew<</if>></div><</liveblock>>
<<set $currentPassage to $town.factions[$currentPassage.key]>><<run lib.logger.info($currentPassage)>> <<set $currentPassage.availableRelationships to lib.factionData.types[$currentPassage.type].members.professions>> <<include "PrintImage">> <h1 class="interactive-only">$currentPassage.name</h1><span @id="$currentPassage.key"></span> <span class='flex-line'> <<button "Edit $currentPassage.name" FactionSliders>> <<run setup.history($currentPassage, "FactionSliders", "Editing " + $currentPassage.name)>> <</button>> </span> <p class='no-indent'><<print lib.firstCharacter($currentPassage.name)>> is <<print lib.articles.output($currentPassage.type)>> $currentPassage.wordNoun. It's $currentPassage.age, and the $currentPassage.size faction has <<print lib.articles.output($currentPassage.reputation)>> reputation, and is motivated by $currentPassage.motivation. They are $currentPassage.misc.</p> <<if $currentPassage.isPolicing>><<include "PolicingFaction">><</if>> <<include "FactionGovernance">> <p><h3>Members</h3>Members of $currentPassage.name are identifiable by <<print $currentPassage.membersTrait || $currentFaction.membersTrait>>. Membership requires $currentPassage.joiningRequirement, and costs $currentPassage.joiningFee. The initiation into $currentPassage.name involves $currentPassage.joiningInitiation.</p> <<linkreplace "<h4>Create Members</h4>" t8n>><<include "CreateFactionNpc">><</linkreplace>> <<include "FactionRelationshipsTable">> <<include "FactionResources">> <<include "FactionPolitics">>
<span class="interactive-only hide-on-print"><<nobr>><span id="NPCListboxes"><<include "NPCListboxes">></span> -- <<button "Create NPC">> <<set _selectedRelationship to $currentPassage.availableRelationships>> <<if _selectedRelationship.base>><<set _randomNPC to Object.assign(_randomNPC, _selectedRelationship.base)>><</if>> <<if def _newNPC>> <<run setup.deleteNPC($npcs[_newNPC])>> <</if>><<run lib.logger.info("_randomNPC:")>><<run lib.logger.info(_randomNPC)>> <<set $currentPassage.randomNPC to setup.createNPC($town, _randomNPC)>> <<run lib.createReciprocalRelationship($town, $currentPassage, $npcs[$currentPassage.randomNPC.key], { relationship: ["member", "high-ranking member", "low-ranking member"].random(), reciprocalRelationship: "faction that " + $npcs[$currentPassage.randomNPC.key].firstName + " is a part of." })>> <<replace "#NPC">> <div class="descriptive"> <h3>$currentPassage.randomNPC.name</h3> <<print lib.articles.output($currentPassage.randomNPC.descriptor).toUpperFirst()>> is currently <<print $currentPassage.randomNPC.idle.random()>> in the corner. You strike up conversation with $currentPassage.randomNPC.himher, and the $currentPassage.randomNPC.descriptor introduces $currentPassage.randomNPC.himherself as <<profile $currentPassage.randomNPC>>, <<print lib.articles.find($currentPassage.randomNPC.profession)>> <<tooltip $currentPassage.randomNPC.profession lib.professions[$currentPassage.randomNPC.profession].description.toUpperFirst()>>.</div> <</replace>> <<replace "#BuildingRelationshipsTable">><<include "FactionRelationshipsTable">><</replace>> <<replace "#NPCListboxes">><<include "NPCListboxes">><</replace>> <</button>><</nobr>></span> <div id="NPC"></div>
<section><h3>Governance</h3> <p>It's ruled by <<switch $currentPassage.leadershipType>> <<case "group">> <<print lib.articles.output($currentPassage.leaderGroupTitle)>> of $currentPassage.leaderGroupSize, who were $currentPassage.leaderQualification. They are $currentPassage.leaderCompetence, and their positions on the $currentPassage.leaderGroupTitle are $currentPassage.stability<<if def $currentPassage.stabilityCause>> due to $currentPassage.stabilityCause<</if>>. Their meetings are held $currentPassage.meetingRegularity, and are $currentPassage.meetingAccessibility. Bribes to the $currentPassage.leaderGroupTitle $currentPassage.leaderBribes. <<default>><<set _leader to $currentPassage.leader>> <<profile $npcs[$currentPassage.leader.key]>>, who was $currentPassage.leaderQualification. <<print _leader.heshe.toUpperFirst()>> is $currentPassage.leaderCompetence, and _leader.hisher position is $currentPassage.stability<<if def $currentPassage.stabilityCause>> due to $currentPassage.stabilityCause<</if>>. Bribes $currentPassage.leaderBribes. <</switch>></p></section>
<h3>Politics</h3>$currentPassage.name have $currentPassage.alliesDescription; <<nobr>><<if $currentPassage.allies.length == 2>> <<print $currentPassage.allies[0].toUpperFirst()>> and $currentPassage.allies[1] can be called on for aid. <<elseif $currentPassage.allies.length == 1>> <<print $currentPassage.allies[0].toUpperFirst()>> are the only others they can rely on. <<else>> <<print $currentPassage.allies[0].toUpperFirst()>> <<print [ "will answer a call for aid, as will ", "regularly assist $currentPassage.name. At their disposal, they also have ", "will help if called upon, as will " ].random()>> <<for _allies range $currentPassage.allies.slice(1, -1)>> _allies, <</for>> and <<print $currentPassage.allies[$currentPassage.allies.length - 1]>>. <</if>> $currentPassage.name have $currentPassage.rivalsDescription; <<if $currentPassage.rivals.length == 2>> <<print $currentPassage.rivals[0].toUpperFirst()>> and $currentPassage.rivals[1] are the only that wish $currentPassage.name ill. <<elseif $currentPassage.rivals.length == 1>> <<print $currentPassage.rivals[0].toUpperFirst()>> are the enemies of $currentPassage.name. <<else>> <<print $currentPassage.rivals[0].toUpperFirst()>> <<print [ "are enemies of $currentPassage.name, as are ", "wish ill of $currentPassage.name. Their other enemies also include ", "are rivals. Other enemies include " ].random()>> <<for _rivals range $currentPassage.rivals.slice(1, -1)>> _rivals, <</for>> and <<print $currentPassage.rivals[$currentPassage.rivals.length - 1]>>. <</if>><</nobr>>
<div id="BuildingRelationshipsTable"><<nobr>> <<set _factionRelationships to lib.findReciprocalRelationships($town, $town.factions[$currentPassage.key], null, 'faction')>> <<run lib.logger.info("RELATIONSHIPS:")>> <<run lib.logger.info(_factionRelationships)>> /* <<if _factionRelationships.length > 0>> */ <div class='classTable'> <details><summary>Relationships</summary> <table> <tr> <th>Name</th> <th>Race</th> <th>Occupation</th> <th>Relationship</th> </tr> <<for _i to 0; _i lt _factionRelationships.length; _i++>><<set _profession to lib.findProfession($town, $npcs[_factionRelationships[_i].npcKey])>> <tr><td><<profile $npcs[_factionRelationships[_i].npcKey]>></td> <td><<print $npcs[_factionRelationships[_i].npcKey].race.toUpperFirst()>></td> <td><<print lib.createTippyFull(_profession.description.toUpperFirst(), $npcs[_factionRelationships[_i].npcKey].profession.toUpperFirst())>></td> <td><<print lib.createTippyFull( _factionRelationships[_i].description || $town.factions[$currentPassage.key].name + " is " + lib.articles.output(_factionRelationships[_i].reciprocalRelationship), (lib.toTitleCase(_factionRelationships[_i].relationship || "member")) )>></td></tr> <</for>> </table></details> </div> /* <</if>> */ <</nobr>></div>
<section><h3>Resources</h3><<nobr>><p>They have $currentPassage.resourcesDescription resources. <<if $currentPassage.resources.list.length == 2>> <<factionResource $currentPassage.resources.list[0] true>> and <<factionResource $currentPassage.resources.list[1]>> are their only significant resources. <<elseif $currentPassage.resources.list.length == 1>> <<factionResource $currentPassage.resources.list[0] true>> are their only significant resources. <<else>> <<factionResource $currentPassage.resources.list[0] true>> <<print [ "are jealously guarded, as are ", "are part of those resources. Also at their disposal are ", "are in their proverbial warchest, as are " ].random()>> <<for _resources range $currentPassage.resources.list.slice(1, -1)>> <<factionResource _resources>>, <</for>> and <<set _num to $currentPassage.resources.list.length - 1>><<factionResource $currentPassage.resources.list[_num]>>. <</if>></p><</nobr>></section>
<<set $currentPassage to $town.factions[$currentPassage.key]>> <<run $("body").on("change", [".type"], function() { $(document).trigger(":liveupdate"); tippy('[data-tippy-content]'); });>> <<liveblock>> <<link "reroll">> <<set $town.factions[$currentPassage.key].name to lib.setFactionName($town, $town.factions[$currentPassage.key])>><<update>> <</link>> -- <span class="auto-update"><label name="Rename the faction"><<textbox "$town.factions[$currentPassage.key].name" $town.factions[$currentPassage.key].name>></label></span> -- <<button "Delete $currentPassage.name" Start>><<run setup.deleteFaction($town, $town.factions[$currentPassage.key])>><</button>> <</liveblock>> <label for="listbox-townfactionscurrentpassagekeytype">Type:</label> <span class="type"><<listbox "$town.factions[$currentPassage.key].type" autoselect>><<optionsfrom Object.keys(lib.factionData.types)>><</listbox>></span> <table> <tr> <th>Attribute Slider</th> <th>Percentage Value</th> </tr> <<for _key, _obj range lib.factionSliders>><<capture _key, _obj>><<run lib.clampRolls($town.factions[$currentPassage.key].roll)>> <<if _obj.isHidden>><<continue>><<else>><tr><td class="tip" @data-tippy-content=lib.factionSliders[_key].description><label for="numberslider-townfactionscurrentpassagekeyrollkey"><<print lib.factionSliders[_key].preceding>></label></td><td style="21vw"> <<numberslider "$town.factions[$currentPassage.key].roll[_key]" $town.factions[$currentPassage.key].roll[_key] 1 100 1>></td></tr><</if>> <</capture>><</for>> </table> <<liveblock>> <p><label for="listbox-townfactionscurrentpassagekeymotivation">Motivation:</label> <<listbox "$town.factions[$currentPassage.key].motivation" autoselect>><<optionsfrom Object.keys(lib.factionData.types[$town.factions[$currentPassage.key].type].motivation)>><</listbox>></p> <p><label for="listbox-townfactionscurrentpassagekeymemberstrait">Member's Trait:</label> <<listbox "$town.factions[$currentPassage.key].membersTrait" autoselect>><<optionsfrom Object.keys(lib.factionData.types[$town.factions[$currentPassage.key].type].membersTrait)>><</listbox>></p> <p><label for="listbox-townfactionscurrentpassagekeyleadershiptype">Leadership Type:</label> <<listbox "$town.factions[$currentPassage.key].leadershipType" autoselect>><<optionsfrom Object.keys(lib.factionData.types[$town.factions[$currentPassage.key].type].leader.format)>><</listbox>></p> /* <p><<if $town.factions[$currentPassage.key].leadershipType === 'individual'>> <label for='listbox-townfactionscurrentpassagekeyleader'>Leader:</label> <<set _newLeader to $npcs[$town.factions[$currentPassage.key].leader.key].name>> <<listbox "_newLeader" autoselect>> <<optionsfrom Object.values($npcs).map(el => el.name)>> <</listbox>> <<if $town.factions[$currentPassage.key].leader.name !== _newLeader>><<button "Save">> <<run lib.deleteReciprocalRelationship($town, $town.factions[$currentPassage.key], lib.findInContainer($npcs)('name', $town.factions[$currentPassage.key].leader.name))>> <<run lib.createReciprocalRelationship( $town, $town.factions[$currentPassage.key], lib.findInContainer($npcs)('name', _newLeader), { relationship: 'head of faction', reciprocalRelationship: 'controlled faction', description: _newLeader + ' is the leader of ' + $town.factions[$currentPassage.key].name + ', and is ' + $town.factions[$currentPassage.key].leaderCompetence + '.'} )>> <<set $town.factions[$currentPassage.key].leader to lib.findInContainer($npcs, 'name', _newLeader)>> <</button>> <</if>> <</if>></p> */ <p><label for="listbox-townfactionscurrentpassagekeyjoiningrequirement">Joining Requirement:</label> <<listbox "$town.factions[$currentPassage.key].joiningRequirement" autoselect>><<optionsfrom Object.keys(lib.factionData.types[$currentPassage.type].joiningRequirement).concatUnique([ 'a display of bravery', 'a display of loyalty', 'a display of skill', 'a favour to be done', 'an excellent reputation', 'endorsement by the current leader', 'referral by an existing member', 'referral by several members', 'some social status', 'to be called on for a favour'])>><</listbox>></p> <p><label for="listbox-townfactionscurrentpassagekeyjoininginitiation">Joining Initiation: </label><<listbox "$town.factions[$currentPassage.key].joiningInitiation" autoselect>><<optionsfrom Object.keys(lib.factionData.types[$currentPassage.type].joiningInitiation).concatUnique([ 'a mission', 'a secret ritual', 'a secret task', 'a simple form to be filled', 'an oath to be taken', 'nothing particularly interesting'])>><</listbox>></p><</liveblock>> <br> <<run tippy('[data-tippy-content]')>> <<button "Save" FactionProfile>><<set $town.factions[$currentPassage.key] to setup.createFaction($town, $town.factions[$currentPassage.key])>><</button>>
<section><h3>Policing</h3><p><<switch $currentPassage.type>> <<case "guard">>$town.name is policed by $currentPassage.name. <<print lib.getGuardFunding($town)>> <<default>>$town.name is policed by $currentPassage.name, rather than a separate guard. <<print lib.getGuardFunding($town)>> <</switch>>One can recognise a member of $currentPassage.name by the <<if def $currentPassage.livery>><<print lib.readoutLivery($currentPassage.livery)>><<else>>$currentPassage.membersTrait<</if>>.</p></section> <section><h4>Law in $town.name</h4><<print lib.getTownLaw($town)>></section>
<<set $building to lib.findInArray($town.buildings, "key", $currentPassage.key)>> <<set $associatedNPC to $npcs[$building.associatedNPC.key]>> <<set $currentPassage.availableRelationships to lib.generalStoreCustomers>> <<include "PrintImage">> <span class='flex-line'><<button "Edit $building.name" $building.initPassage>><</button>></span> <h1 class="interactive-only">$building.name</h1><span @id="$building.key"></span> <p><<print lib.getGeneralStoreIntroduction($town, $building)>></p> <h3>Shopkeeper</h3> <p>The shopkeep <<print $associatedNPC.greeting.random()>> <<print ['when you come inside', 'after finishing with another customer', 'as soon as you come through the door', 'when you come up to the counter', 'while you look around'].random()>>. <<print $associatedNPC.heshe.toUpperFirst()>> introduces $associatedNPC.himherself as <<profile $associatedNPC>>, the $associatedNPC.owner of the General Store, and $building.say. $building.shopkeepNote.</p> <span class="interactive-only click-and-remove-link"><<link "<h4>Buy a magic trinket</h4>">><<set $toolbox.trinket to lib.createMagic('trinket')>><<update>><</link>></span> <<liveblock>><<if $toolbox.trinket>>$associatedNPC.firstName reaches under the shop counter and says "I think you might like this." before pulling out and placing the $toolbox.trinket.name on the counter. <div class='descriptive'><h3>$toolbox.trinket.name</h3>$toolbox.trinket.description</div><</if>><</liveblock>> <<include "GeneralStoreSell">> <<details "CreateNewNpc" "Customers">>
<<linkappend "<h4>Buy something</h4>" t8n>><<nobr>><div class="descriptive"><<run lib.logger.info("BUILDING:")>><<run lib.logger.info($building)>> <<switch $town.type>> <<case "city">> $associatedNPC.name smiles, and says "You're looking to buy something? Name it, here at $building.name, we stock everything the average man could possibly need! <<case "town">> $associatedNPC.name says "Well, what is it you need? We can do just about anything for you. <<case "village">> $associatedNPC.name says "Sure, what are you after? <<print $associatedNPC.mundane.random().toUpperFirst()>>? <<case "hamlet">> $associatedNPC.name smiles, and says "What are you after? We can do just about anything you could possibly need; <<print $associatedNPC.mundane.random()>>, <<print $associatedNPC.mundane.random()>>, <<print $associatedNPC.mundane.random()>>, you name it! <<default>> $associatedNPC.name says "Well, what is it you need? We can do just about anything for you. <</switch>><<print lib.closestMatch(lib.generalStore.get.priceTalk($building), 'priceTalk', 'priceModifier', 'wealth', '$building.priceModifier', '$building.roll.wealth')>>"</div> <<switch $town.type>> <<case "city">> <<set _availability to 4>> <<case "town">> <<set _availability to 3>> <<case "village">> <<set _availability to 2>> <<case "hamlet">> <<set _availability to 1>> <<default>> <<set _availability to 3>> <</switch>> <<set _selling to { category: { weapon: 'weapons', armour: 'armour', 'adventuring gear': 'adventuring gear', 'tools': 'tools' } }>> <<shop $building, _selling>> <</nobr>><</linkappend>>
<<set $currentPassage.index to lib.findIndexInArray($town.buildings, "key", $currentPassage.key)>> <<run lib.clampRolls($town.buildings[$currentPassage.index].roll)>> <<set $town.buildings[$currentPassage.index].priceModifier to Math.clamp($town.buildings[$currentPassage.index].priceModifier, -10, 10)>> <<set _name to $town.buildings[$currentPassage.index].name>> <<linkreplace "Rename <strong>_name</strong>">><h3><label name="Rename the general store"><<textbox "$town.buildings[$currentPassage.index].name" _name>></label></h3><</linkreplace>> <<button "Delete _name" Start>><<run setup.deleteBuilding($town, $town.buildings[$currentPassage.index])>><</button>> <span class="tip" data-tippy-content="How wealthy is the patronage?"><label for="numberslider-input-townbuildingscurrentpassageindexrollwealth">General Store Wealth</label>: <<numberslider "$town.buildings[$currentPassage.index].roll.wealth" $town.buildings[$currentPassage.index].roll.wealth 1 100 1>></span> <span class="tip" data-tippy-content="How large is it?"><label for="numberslider-input-townbuildingscurrentpassageindexrollsize">General Store Size</label>: <<numberslider "$town.buildings[$currentPassage.index].roll.size" $town.buildings[$currentPassage.index].roll.size 1 100 1>></span> <span class="tip" data-tippy-content="Is it well known, or another no-name shop?"><label for="numberslider-input-townbuildingscurrentpassageindexrollreputation">General Store Reputation</label>: <<numberslider "$town.buildings[$currentPassage.index].roll.reputation" $town.buildings[$currentPassage.index].roll.reputation 1 100 1>></span> <span class="tip" data-tippy-content="How clean is it?"><label for="numberslider-input-townbuildingscurrentpassageindexrollcleanliness">General Store Cleanliness</label>: <<numberslider "$town.buildings[$currentPassage.index].roll.cleanliness" $town.buildings[$currentPassage.index].roll.cleanliness 1 100 1>></span> <span class="tip" data-tippy-content="How busy is it? Are they getting much custom at the moment?"><label for="numberslider-input-townbuildingscurrentpassageindexrollactivity">General Store Activity</label>: <<numberslider "$town.buildings[$currentPassage.index].roll.activity" $town.buildings[$currentPassage.index].roll.activity 1 100 1>></span> <span class="tip" data-tippy-content="How likely is it to find magic here?"><label for="numberslider-input-townbuildingscurrentpassageindexrollmagic">General Store Magic</label>: <<numberslider "$town.buildings[$currentPassage.index].roll.magic" $town.buildings[$currentPassage.index].roll.magic 1 100 1>></span> <span class="tip" data-tippy-content="How much cheaper or more expensive than average is it?">General <label for="numberslider-input-townbuildingscurrentpassageindexrollmodifier">Store Price Modifier</label>: <<numberslider "$town.buildings[$currentPassage.index].priceModifier" $town.buildings[$currentPassage.index].priceModifier -10 10 1>></span> When you're ready, here's the [[general store|generalStoreOutput]]
<<nobr>><<linkreplace "<h4>Create a wild surge!</h4>">> <<set $surge to surge()>> <<replace "#surge">> $surge <</replace>><<link "<h4>Another wild surge!</h4>">><<set $surge to surge()>> <<replace "#surge">> $surge<</replace>><</link>> <</linkreplace>> <span id="surge"></span><</nobr>>
<h2>Contributors</h2> <<print lib.createBadge(lib.badges.stats.githubContributors)>> Thanks to the following for their contributions to the codebase! * WishingOnAWendy * cktang88 * glinkis * axxroytovu * monsagri * Zer0-Ark * ever-3W
<div id="cookie"> We use cookies to anonymously track site usage, like most modern websites. This helps us understand what parts of the website are being used with what technologies, and other anonymous data. <br> If you don't want to be tracked, you can disallow now, or later turn it off in settings, install the Google Analytics Opt Out add on, or build the generator from source on our GitHub page. <br> By closing this banner or continuing to browse otherwise, you agree to the use of cookies and our Google Analytics tracking. <br> <<run State.metadata.set('cookiePopupWasShown', true)>> <<button "X Disallow">> <<set settings.disableAnalytics to true>> <<run State.metadata.set('disableAnalytics', settings.disableAnalytics)>> <<run window['ga-disable-UA-119249239-1'] = settings.disableAnalytics>> <<include "RemoveCookieNotifier">> <</button>> -- <<button "X Close">> <<include "RemoveCookieNotifier">> <</button>> </div>
<<if Dialog.isOpen()>> <<if State.metadata.get("neverShowWelcome") is true>> <<if State.metadata.get("showBiomeGeneration") is true>> <<run Dialog.close()>> <<run Engine.play("BiomeGeneration")>> <<else>> <<run Dialog.close()>> <<run Engine.play("Start")>> <</if>> <<else>> <<run Dialog.close()>> <<run Engine.play("Start")>> <</if>> <<else>> <<run document.getElementById("notify").remove()>> <</if>>
<h2>Credits</h2> Many thanks to /u/FamousHippopotamus and /u/OrkishBlade for their excellent tables and DMing advice over the years. Shoutout to /r/twinegames and the good folks in the Discord server who helped a total newb learn how to code in Twine. [[Sign up to the newsletter to be notified about updates|EmailSignUp]] <<include "Legal">> <<include "PatreonSupporters">> <<include "Contributors">> <<include "FunStats">>
<h2>Fun Stats</h2> <<for _badge range lib.badges.stats>> <<print lib.createBadge(_badge, {imgArgs: 'style=width:auto'})>> <</for>>
<<include "Auth">><<if State.metadata.get('patreonPass') === $_>><<run setup.openDialog({ header: 'Pantheon Setup', passage: 'EditPantheon', rerender: true })>><<else>><<run setup.openDialog({ header: 'Patreon Only', passage: 'ImportPatreon', rerender: true })>><</if>>
<<set _retorts to ['Not quite- did you spell it wrong?', 'Try again.', 'Third time is not the charm, apparently.',,,,,,,,, 'It is pretty obvious that you have not paid',,,,,, 'Please consider chucking me $5',,,, 'I spent a long time on this',,,,, "If you can't be bothered, then there's always hosting locally!",,,, '...but we both know that the chances of you doing that are very low...',,,,,,, 'So come on, the button is right [[here|https://www.patreon.com/eigengrausgenerator/membership]]!',,,'Or, you can always post the generator on Reddit, etc.',,,,,'If you submit it to a blog, then I will send you a key!',,,,,"Plus, this feature gets unlocked for everyone at 150 patrons!",,,,,,,,,,,,, '...',,,, '...',,,, "You're just having fun now.",,,, "You think this is some kind of game?!",,,,,,,,,,, "Well it isn't! It's a TOWN GENERATOR.",,,,,,,,, 'NOT a game.',,,,,,,,,,,,,,, 'But, I admit that it is kinda fun to write these messages.',,,,,,,, 'I wonder if anyone is actually going to read them.',,,, 'Probably not.',,,, 'But then again, maybe!',,,,,,, 'But you should probably just quit.',, 'The whole schtick of talking through the error message is kinda getting old',,,,,,, "Plus, I can't really think of anything else witty to say.",,,,,,,, "Or even vaguely amusing, for that matter.",,,,,,,,,,,,,,,,,,,,,,, 'Go watch a movie or something!',,,, 'Or, I dunno, pay for the patreon?',,,,,,, 'That could be fun!',,,,,,,,,,, "Don't knock it until you try it!",,,,, 'Okay, I give up.',,,,,,,,,,,, 'You really want the password?',,,,,,, 'Fine.',,,,,,,,, 'Have it.',,,,,, 'I suppose you deserve something for god knows how many clicks.',,,,,,,,,,,,, 'Okay, stop clicking super fast.', "It's this next one",, "It's embedded in this YouTube video: https://www.youtube.com/watch?v=YddwkMJG1Jo"]>><<set _temp to lib.random(['Abracadabra', 'Please?', 'Expecto Patron!', 'Open Sesame', 'I cast Knock', 'I use thieves tools'])>><<set _i to 0>> This is a [[Hero tier Patron|https://www.patreon.com/join/eigengrausgenerator?]] only feature. However, I'm not one for hard content walls; if you are not a Patreon supporter, you can access it by compiling from source, which is downloadable from our GitHub. <<include "GoalBasedUnlockingText">> If you are a Hero tier Patron, take a look at the [[membership page|https://www.patreon.com/eigengrausgenerator/membership]], and enter the code below: <<textbox "$tovvn" _temp autofocus>> -- <<button "Submit">><<include "Auth">><<if $tovvn === $_>><<run Dialog.close()>><<else>><<replace "#result">> -- <<if _i < _retorts.length && _retorts[_i]>><<print _retorts[_i]>><<elseif _i > 100>><<print "I promise you, there's nothing further.">><</if>><<set _i++>><</replace>><</if>><</button>> <span id="result"></span>
<<set _suggestedTweets to { 'Matthew Mercer': '@matthewmercer', 'Matt Colville': '@mattcolville', 'Mike Mearls': '@MikeMearls', 'Tribality': '@TribalityRPG', 'DM David': '@DMDavidBlog', 'The Angry GM': '@TheAngryGM', 'Chris Perkins': '@ChrisPerkinsDND', 'Sly Flourish': '@SlyFlourish' }>><<set _selected to Object.keys(_suggestedTweets).random()>><<set _content to 'http://twitter.com/intent/tweet?text=.' + _suggestedTweets[_selected] + '.Hey%2C%20you%20should%20check%20out%20Eigengrau%27s%20Generator-%20it%27s%20a%20town%20generator%20like%20no%20other%2C%20open%20sourced%20too.%20Give%20it%20some%20love!'>> Eigengrau's Generator runs on a "goal based content unlocking" system, where paywalls come down when we hit Patreon milestones. So, it's in your best interests to <<link "spread the word!">><<run UI.share()>><</link>> Send the blog to [[Kotaku|mailto:tips@kotaku.com]], [[Geek and Sundry|https://geekandsundry.com/contact/]], and any other blog, and tell them to cover it! You could always do something like <a target="_blank" @href=_content> tweet _selected</a>
<h2>Google Analytics & Tracking</h2> We use Google Analytics for aggregated, anonymized website traffic analysis. In order to track your session usage, Google drops a cookie (_ga) with a randomly-generated ClientID in your browser. This ID is anonymized and contains no identifiable information like email, phone number, name, etc. We also send Google your IP Address. We use GA to track aggregated website behavior, such as what pages you looked at, for how long, and so on. This information is important to us for improving the user experience and determining site effectiveness. If you would like to access what browsing information we have - or ask us to delete any GA data - please delete your _ga cookies, reach out to us via email, and/or install the Google Analytics Opt-Out Browser Add-On. <h2>WotC Content</h2> This is unofficial fan content, permitted under the WotC Fan Content Policy. It is not approved/endorsed by Wizards of the Coast. Portions of the materials used are property of Wizards of the Coast. © Wizards of the Coast.
<h2>Patreon Supporters</h2> Special thanks to my [[Patreon|https://www.patreon.com/join/eigengrausgenerator?]] supporters! <<if setup.supporters>> <ul style="columns:2; -webkit-columns:2; -moz-columns:2;"> <<for _index, _item range setup.printSupporters()>><li>_item</li><</for>> </ul><</if>>
Thanks to my wonderful Patrons, you can access the Pantheon system for free! <<include "GoalBasedUnlockingText">> <br> When we hit <b>175 patrons</b>, I will start work on an inventory system, so NPCs have appropriate gear! <br> [[Become a Patron|https://www.patreon.com/join/eigengrausgenerator?]] <br> <<import '$town.religion._customPantheon' 'json' 'Import Custom Pantheon'>>
<<if !_isTextOutput>> <<set _roll to random(1, 5)>> <<if _roll is 5>> <blockquote class='interactive-only hide-on-print'>Did you know? Eigengrau's Generator can <<link "Export to Foundry" OutputEverything>><<set $history.length to 0>><<run setup.history({passageName: "OutputEverything", linkDescription: "Export", name: "Export"})>><</link>></blockquote> <<elseif _roll is 4>> <blockquote class='interactive-only hide-on-print'>Find us on Reddit at [[/r/EigengrausGenerator|https://www.reddit.com/r/EigengrausGenerator]]!</blockquote> <<elseif _roll is 3>> <blockquote class='interactive-only hide-on-print'><<include "PatreonSupporters">></blockquote> <<elseif _roll is 2>> <blockquote class='interactive-only hide-on-print'><<include "EmailSignUp">></blockquote> <<elseif _roll is 1>> <blockquote class='interactive-only hide-on-print'>If you have any bug reports, please click here: [[Submit a bug report|https://github.com/ryceg/Eigengrau-s-Essential-Establishment-Generator/issues/]]</blockquote> <</if>> <</if>>
<<nobr>> <!-- Begin MailChimp Signup Form --> <div id="mc_embed_signup"> <form action="https://eigengrausgenerator.us18.list-manage.com/subscribe/post?u=208547295e01c1ea0810ecfe0&id=dee6199f13" method="post" id="mc-embedded-subscribe-form" name="mc-embedded-subscribe-form" class="validate" target="_blank" novalidate> <div id="mc_embed_signup_scroll"> <h2>Subscribe to our mailing list</h2> <div class="mc-field-group"> <label for="mce-EMAIL">Email Address <span class="asterisk">*</span> </label> <input type="email" value="" name="EMAIL" class="required email" id="mce-EMAIL"> </div> <div id="mce-responses" class="clear"> <div class="response" id="mce-error-response" style="display:none"></div> <div class="response" id="mce-success-response" style="display:none"></div> </div> <!-- real people should not fill this in and expect good things - do not remove this or risk form bot signups--> <div style="position: absolute; left: -5000px;" aria-hidden="true"><input type="text" name="b_208547295e01c1ea0810ecfe0_dee6199f13" tabindex="-1" value=""></div> <div class="clear"><input type="submit" value="Subscribe" name="subscribe" id="mc-embedded-subscribe" class="button"></div> </div> </form> </div> <!--End mc_embed_signup--> <</nobr>>
<<set $building to lib.findInArray($town.buildings, "key", $currentPassage.key)>> <<set $associatedNPC to $building.associatedNPC>> <<set $currentPassage.availableRelationships to lib.brothelCustomers>> <<include "PrintImage">> <h1 class="interactive-only">$building.name</h1><span @id="$building.key"></span><p>You make your way down <<print lib.createTippyFull($town.roads[$building.road].description, $town.roads[$building.road].name)>>, and enter $building.name $building.structure.descriptor. Inside, the $building.size $building.structure.material.noun building is $building.cleanliness. You notice $building.notice. When people talk about $building.name, they say $building.talk. Apparently, it specialises in $building.specialty. Rumours abound in whorehouses, and $building.name is no different; apparently, $building.rumour.</p> <section><h3>Brothel $associatedNPC.title</h3> <p>The $associatedNPC.title is $building.idle, $associatedNPC.heshe appears to be $building.owner. <<print ['Upon seeing you come in', 'Upon noticing your arrival', 'Once $associatedNPC.heshe notices you', 'After a few minutes', 'As soon as you enter'].random()>> the $associatedNPC.raceNote <<print $associatedNPC.greeting.random()>>, and <<print ["saunters", "walks", "strolls", "walks", "slowly walks", "swaggers", "quickly walks", "slides", 'quickly walks', 'casually saunters', 'ambles', 'trudges', 'strides', 'slowly wanders', 'marches'].random()>> over to where you are. <<print $associatedNPC.heshe.toUpperFirst()>> introduces $associatedNPC.himherself as <<profile $associatedNPC>>, the $associatedNPC.title of $building.name, and asks what $associatedNPC.heshe can do for you.</p></section> <span class="temporarily-removed click-and-remove-link"><<link "Meet one of the harlots">><<set $building.harlot to setup.brothelData.harlot.create($town, $building)>><<update>><</link>></span> <<liveblock>><<if def $building.harlot>><span id="harlot">$building.harlot</span><</if>><</liveblock>> <<details "CreateNewNpc" "Customers">>
<<set $building to lib.findInArray($town.buildings, "key", $currentPassage.key)>> <<set $associatedNPC to $npcs[$building.associatedNPC.key]>> <<set $buildingTree to $building.graveyardTree>> <<include "PrintImage">> <h1 class="interactive-only">$building.name</h1><span @id="$building.key"></span><p>You walk down <<print lib.createTippyFull($town.roads[$building.road].description, $town.roads[$building.road].name)>> to the $building.wordNoun. It is $building.location, and is $building.size. You enter the $building.wordNoun $building.entrance. It's mostly $building.cleanliness. As you enter you notice $building.feature.</p> <section><h3>Gravedigger</h3><p>$building.gravediggerLook. The gravedigger greets you as you come near, and introduces $associatedNPC.himherself as <<profile $associatedNPC>>. <<print $associatedNPC.heshe.toUpperFirst()>> says $building.gravediggerChat.</p></section> <span class="temporarily-removed click-and-remove-link"><<link "Visit a grave">><<run lib.logger.info("Creating a grave!")>><<set $building.grave to setup.graveStone.create($town)>><<update>><<print $building.grave.readout>><</link>></span> <<liveblock>><<if $building.grave>><div id="grave">$building.grave.readout</div><</if>><</liveblock>> <<details "GraveyardTable" "People Buried Here">>
<<nobr>><<liveblock>> <table> <tr> <th>Name</th> <th>Race</th> <th>Profession</th> <span class="interactive-only"><th>Delete</th></span> </tr> <<for _i, _npc range $npcs>><<capture _i, _npc>><<set _profession to lib.toTitleCase(_npc.profession)>><<if _npc.passageName is "NPCDeadProfile">> <tr><td><<profile $npcs[_npc.key]>></td> <td><<print _npc.race.toUpperFirst()>></td> <td><<print lib.createTippyFull(lib.professions[_npc.profession].description.toUpperFirst(), _profession)>></td> <span class="interactive-only"><td><<link "x" TownOutput>><<set _deleted to $npcs[_npc.key]>><<run setup.deleteNPC($npcs[_npc.key])>><<notify 5000>>Deleted _deleted.name<</notify>><</link>></td></span> </tr> <</if>><</capture>><</for>> </table><</liveblock>><</nobr>>
<<set $currentPassage to lib.findInArray($town.buildings, "key", $currentPassage.key)>> <<set $currentFaction to lib.getPolice($town.factions)>> <<set $associatedNPC to $npcs[$currentPassage.associatedNPC.key]>> <<set $currentPassage.availableRelationships to lib.guardhouseCustomers>> <<include "PrintImage">> <h1 class="interactive-only">$currentPassage.name</h1><span @id="$currentPassage.key"></span><p>You make your way down <<print lib.createTippyFull($town.roads[$currentPassage.road].description, $town.roads[$currentPassage.road].name)>>, and enter $currentPassage.name $currentPassage.structure.descriptor. $currentPassage.name is known for $currentPassage.notableFeature</p> <p>It is run by <<profile $town.factions[$currentFaction.key]>>, who are $currentPassage.expertise. At the moment, <<print lib.weightedRandomFetcher($town, lib.guardhouseData.get.event($town, $currentPassage), $currentPassage, undefined)>></p> <<include "PolicingFaction">> <section><h4>Chief's Office</h4><p>The person in charge is <<profile $currentPassage.associatedNPC>>, <<print lib.articles.output($npcs[$currentPassage.associatedNPC.key].calmTrait)>> <<print $npcs[$currentPassage.associatedNPC.key].descriptor>>. <<print lib.closestMatch(lib.guardhouseData.get.officeDescription($currentPassage), 'description', 'size', 'wealth', $currentPassage.roll.size, $currentPassage.roll.wealth)>></p> <<link '<h4>Evidence Locker</h4>'>><<set $currentPassage.evidence to lib.weightedRandomFetcher($town, lib.guardhouseData.evidenceLocker.items, $currentPassage, undefined)>><<update>><</link>> <<liveblock>><<if $currentPassage.evidence>><p>In the evidence locker is $currentPassage.evidence</p><</if>><</liveblock>> The guardhouse is $currentPassage.cleanliness</section> <section><<link '<h4>Holding Cell</h4>'>><<set $currentPassage.holdingCell to lib.weightedRandomFetcher($town, lib.guardhouseData.get.holdingCell, $currentPassage, undefined, 'object')>><<set $currentPassage.prisoner to setup.createNPC($town, $currentPassage.holdingCell.base)>><<update>><</link>> <<liveblock>><<if $currentPassage.holdingCell>>In the holding cell is <<profile $currentPassage.prisoner $currentPassage.holdingCell.reason>><</if>> <</liveblock>></section> <<details "CreateNewNpc" "People Around">>
<<set $building to lib.findInArray($town.buildings, "key", $currentPassage.key)>> <<set _marketData to lib.market>><<if ndef $building.sellers>><<set $building.sellers to {}>><<run setup.createMarketSellers($town, $building.sellers, 4)>> <<set $building.magicSeller to setup.createNPC($town, {background: ["commoner", "commoner", "commoner", "noble"].random(), profession: "merchant"})>><</if>> <<include "PrintImage">> <h1 class="interactive-only">$building.name</h1><span @id="$building.key"></span><p>You wander through the streets of $town.name, and come across the market, which is located in $building.location. It seems that the vendors are organised by $building.organisation. The market is known for $building.draw, and is $building.cleanliness. Today the market is $building.crowd.</p> <section><h3>Merchants</h3> <<for _key, _seller range $building.sellers>> <p><<print ["A ", "Nearby, a ", "Off to the side, a "].random()>> <<profile $npcs[_key] $npcs[_key].descriptor>> is _seller.selling from a _seller.tent.</p> <</for>></section> <span class="interactive-only"><<link "<h4>Who else is there?</h4>">> <<set $building.moreSeller to setup.createNPC($town, { profession: "merchant", hasClass: false, isThrowaway: true})>><<set $building.moreSeller.merchant to {}>><<set $building.moreSeller.merchant.selling to lib.market.vendors.selling.random()>><<set $building.moreSeller.merchant.tent to lib.market.vendors.tent.random()>> <<update>> <</link>><<liveblock>><<if ndef $building.moreSeller>><span id="moreSellers"></span><<else>>There's <<profile $building.moreSeller $building.moreSeller.descriptor true>> selling $building.moreSeller.merchant.selling from <<print lib.articles.output($building.moreSeller.merchant.tent)>>.<</if>><</liveblock>></span> <span class="interactive-only">A <<profile $building.magicSeller $building.magicSeller.descriptor>> is selling magical trinkets from a <<print _marketData.vendors.tent.random()>>. Or, at least, they're supposedly magical items. <br> <label name="Type of magic" for="listbox-buildingmagicselection"><<listbox "$building.magicSelection">> <<option "Ring" "ring">><<option "Weapon" "weapon">><<option "Armour" "armour">><<option "Trinket" "trinket">> <</listbox>></label> -- <<button "Create a magic item!">> <<set $building.magicType to $building.magicSelection>> <<switch $building.magicType>> <<case "ring">><<nobr>> <<set $building.magic to lib.createRing()>> <<replace "#magic">>The $building.magicSeller.manwoman beckons you over to $building.magicSeller.hisher stall, and introduces $building.magicSeller.himherself as <<profile $building.magicSeller>>, a purveyor of fine goods. The $building.magicSeller.raceNote asks you for your ring size, and then $building.magicSeller.heshe reaches below $building.magicSeller.hisher table, and procures a ring... <div class="descriptive"> <h3><<print lib.articles.output($building.magic.material).toUpperFirst()>> ring</h3> $building.magic.firstOutputs</div> <</replace>><<link "<h4>Look at another ring</h4>">><<set $building.magic to lib.createRing()>> <<replace "#magic">> <div class="descriptive"> <h3><<print lib.articles.output($building.magic.material).toUpperFirst()>> ring</h3> $building.magic.secondOutputs</div><</replace>><</link>><<run console.log($building.magic)>><</nobr>> <<default>> <<set $building.magic to lib.createMagic($building.magicType)>><<run console.log($building.magic)>> <<replace "#magic">><div class='descriptive'><h3>$building.magic.name</h3>$building.magic.description</div><</replace>> <</switch>> <</button>> <br> <span id="magic"></span> </span> <br> <span class="interactive-only"> <<button "Create market event">> <<set _marketEvent to lib.weightedRandomFetcher($town, setup.marketEvent.event, $building)>> <<update>> <</button>> <section> <<liveblock>> <<if _marketEvent>> _marketEvent <</if>> <</liveblock>> </section> </span>
<<button "Generate a poster">> <<if def _newNPC>> <<run setup.deleteNPC($npcs[_newNPC])>> <</if>> <<set $poster to lib.weightedRandomFetcher($town, setup.plothooks, null, setup.misc.town.type.paper)>> <<update>><</button>> <section id='poster'><<liveblock>><<if def $poster>><div class='descriptive'>$poster</div><</if>><</liveblock>></section>
<<set $building to lib.findInArray($town.buildings, "key", $currentPassage.key)>> <<set $associatedNPC to $npcs[$building.associatedNPC.key]>> <<set $currentPassage.availableRelationships to lib.templeCustomers>> <<include "PrintImage">> <h1 class="interactive-only">$building.name</h1><span @id="$building.key"></span><p>You come across $building.structure.templeDescriptor. $building.guardReadout</p> <p>$building.interiorReadout</p> <p>$building.aboutReadout</p> <h3>Priest</h3> <p>$building.priestLook. The priest greets you, and introduces $associatedNPC.himherself as <<profile $associatedNPC>>. $building.priestChat.</p> <<linkreplace "<h4>Get Temple Blessing</h4>" t8n>><h6>Blessing</h6> <p class="descriptive">$building.blessing</p><</linkreplace>> <<linkappend "<h4>Buy something</h4>" t8n>> <<switch $town.type>> <<case "city">> <<set _availability to 4>> <<case "town">> <<set _availability to 3>> <<case "village">> <<set _availability to 2>> <<case "hamlet">> <<set _availability to 1>> <<default>> <<set _availability to 3>> <</switch>> <<set _selling to { category: { 'adventuring gear': 'adventuring gear', 'tools': 'tools' } }>> <<shop $building, _selling>> <</linkappend>> <<details "CreateNewNpc" "Church Attendees">>
<<set $building to lib.findInArray($town.buildings, "key", $currentPassage.key)>> <<include "PrintImage">> <h1 class="interactive-only">The Town Square</h1><span @id="$building.key"></span><p>You're in the town square. It's $building.size, and $building.cleanliness. It features $building.feature As you <<print setup.townSquare.passageData.enter.random()>>, you see <<print lib.weightedRandomFetcher($town, setup.townSquare.crowd, $building)>></p> <p>There's <<print lib.weightedRandomFetcher($town, setup.townSquare.vignettes)>> There's also a noticeboard, which has various posters, requests, and announcements tacked to it.</p> <span class="interactive-only"> <<button "Who else is there?">> <<if def $building.associatedNPC>> <<run setup.deleteNPC($npcs[$building.associatedNPC])>> <</if>> <<set $building.associatedNPC to lib.weightedRandomFetcher($town, setup.townSquare.crowd, $building)>> <<update>> <</button>> <section> <<liveblock>> <<if def $building.associatedNPC>> <<print setup.townSquare.passageData.subsequentViews.random()>> $building.associatedNPC <</if>> <</liveblock>> </section> </span> <<include "Posters">>
/* <<set $adventureGorn to setup.createNPC($town, {age: "venerable", firstName: "Gorn"})>> <<set $adventureBandit to setup.createNPC($town, {gender: "man", profession: "bandit", profession: "fighter", note: "$adventureBandit is currently drunk."})>> <blockquote><h3>The Premise</h3> The characters are coerced into delivering the corpse of a man to a crypt 3 days distant. There are factions who oppose this and will try and stop the party from completing their task.</blockquote> <h3>The Hook</h3> <<linkreplace "For new parties">> You are an adventuring band who have each received a letter to attend a meeting of the band in a familiar tavern, the $building.name, and upon arriving, discover that none of you sent the invitation. Intrigued and a little annoyed, a local urchin boy delivers another letter to your table, after many pints and conversation have been spent wondering why you have all been called together. The letter is written in the High Speech, a language your company knows very well, it being the dialect of a people whose tombs you have raided on more than one occasion. The letter says that each of you have had a family member taken from you and that in order to ransom them back to you, you must travel to the Palace at Ockshirock, and meet with a man named Gorn, seneschal of this tiny barony.<</linkreplace>> <<linkreplace "For continuing parties">>A local urchin boy delivers a letter to your table, after many pints and conversation have been spent wondering why you have all been called together. The letter is written in the High Speech, a language your company knows very well, it being the dialect of a people whose tombs you have raided on more than one occasion. The letter says that each of you have had a family member taken from you and that in order to ransom them back to you, you must travel to the Palace at Ockshirock, and meet with a man named <<profile $adventureGorn "Gorn">>, seneschal of this tiny barony. Outraged, you each rush home to discover that the letter's contents were true. Each of you is missing someone you love. <blockquote>DM NOTE: Record each character's choice of missing family member, as you will need to roleplay their distress very soon.</blockquote> You reassemble at the tavern and decide to set out immediately for this unknown barony, and your journeys keep you on the road for weeks. <h3>The Arrival</h3> The time is mid-morning on an Autumn day that threatens rain. The wind is fast and cold and all the game has bedded down in shelter. Looming ahead of you is a gothic pile, a structure that cannot decide if it is a house, a fort, or a rambling inn. There are so many outbuildings it is hard to determine where the main foyer would be located, but you are met by a dour looking man, old in his years, and stooped with some affliction. His eyes are sharp and clear, however, and he leans on an iron crosier with a centerpiece that mimics the inward-facing profiles of 8 crows with their beaks ajar. Without a word, the man turns and slowly walks towards an open door nestled in the lee of one of the tilted walls. Upon passing through the door you find yourself in a great hall, bare of any furniture or adornments, beyond the horrifying sight of your loved ones dangling in brass cages from chains in the ceiling. Your loved ones shout out to you as you enter, begging for help, and the old man takes his place between 2 huge constructs in the shape of giant humanoids, 15' tall. Each is silent and unarmed, but strange geometries have been scraped into their limbs and bodies, and some are lit with some false fire. The man speaks, ignoring the cries of his captives, and his voice is far stronger than his frail body would have you believe. "You will carry out a task for me, one of the greatest importance. You will deliver the body of our beloved Father Brown to his family crypt on the other side of the Hamlet of Bausk. For your troubles, your family will be released, unharmed, and a small bounty paid to you for your inconvenience. Forgive my ill-manners, but these are trying times and one does what one must to achieve one's goals, don't you think?" Gorn unfurls a contract, written in the High Speech, and says the party must sign, individually. If they balk, he will threaten the party's family, and even hurt them if met with insults and empty posturing. After they sign he gives them the key to the basement, where Father Brown's body awaits. He says a wagon has been hitched to a draft team, and they need only follow the King's Road for 3 days and 3 nights and pass through the Hamlet, whereupon they will see the family crypt. The party is instructed to deliver the body to the interior, place the coffin in the sarcophagus and exit, closing the tomb door behind them. The party is then told that they must complete a ritual to seal the tomb from disturbance, the details of which are written on a scroll in the Low Common and passed over. <span id="descriptive">Gorn finishes with "Oh, and just one more thing. If you fail in your task, not only will your family die, knowing you failed them, but I will send these creatures to kill you all." and he gestures to the Stone Golems that flank him.</span> Gorn will answer no questions on the identity of Father Brown or any information on what lies between here and the family crypt. If pressed, he says, "Your time is slipping away, as is the time remaining on the lives of your dear families." and will remain resolutely silent after that. <blockquote>Impress upon the party that doing the task is preferable to a fight with Gorn and his Golems. Increase the numbers if you wish, or have some active magic effects showing on them to discourage using this possibility.</blockquote> <<linkreplace "If the party attacks him anyway">>If he is attacked he will order the Stone Golems to attack and will activate a magic ring he is wearing that subjects the contents of all the brass cages to a Disintegrate spell cast at 12th level. He will then flee to the basement using a duplicate key to the one he gave the party and attempt to teleport himself and the coffin of Father Brown to a destination far beyond the known borders of the Empire. This is included only if you wish to use this in a campaign setting and things go terribly wrong. <</linkreplace>> <blockquote><h3>DMs Background</h3>Father Brown was a cleric that served Baron Hauscha, a brutal dictator (until his assassination last year) who kept unsavory company with unclean entities. Father Brown was the servant of one of those entities, and was on his way to becoming a Lich, when he was slain by a miscast ritual, but his body was preserved through the ministrations of Gorn (a cleric/wizard of formidable power, being level 8/12). Gorn wants to send Brown to his crypt and seal it, so it can be revivified once his superior arrives from across the ocean. Knowing that there are those who would use Brown for their own ends, or threaten his physical remains, Gorn takes the extreme step of coercing a band of tough adventurers to do the dangerous job for him. There will be 4 factions that threaten this journey - A Hag and her Quickling minions; A group of Clerics of Purity; A ragtag bunch of bandits; and the citizens of the Hamlet of Bausk. Their motivations will be detailed, below.</blockquote> <h3The Journey</h3> The journey to the crypt takes 3 days and 3 nights. The Hamlet of Bossk is reached on the afternoon of the 3rd day. The crypt is reached after the sun sets on the 3rd day. There are 5 sections to explain. <h3The Forest (Day 1/Night 1)</h3> In the forest is the Hag, Yumbatimba, and a pack of 6 Quicklings who serve her. She wants the body of Father Brown to use as a vessel for a summoned demon that owes her a few favors. The corpse has certain protections and advantages tattooed on it in the form of sigils and spell-chains and she sends her minions to steal it. Only 4 are needed to carry the coffin, and the other 2 will be used to create a diversion. The Quicklings do not want battle, only to steal the coffin and run, but if 3 are killed, then the remaining 3 will do all they can to kill the party as fast as they can. Yumba will be watching, of course, and if the party follows the Quicklings, she will be waiting in a trapped area to lure them into ambush. The traps are either confining or damaging, your choice, with the intention the party is put off of interfering with the Hag's summoning, which will take 1 hour after the coffin is secured inside her hut. She will not be adverse to destroying her home with fire and curses if the party brings the heat, however, she would rather lose everything before losing her life. The Quicklings should observe the party for a while, and strike when they are distracted (or roleplaying) and attempt to distract with misdirection and feints. <h3The Hills (Day 2/Night 2)</h3> In the hills is a cult of the Deity of Purity and they are hardcore extremists, who believe that the body of a dead person is equivalent to a radioactive chunk of uranium and of the highest danger to the survival of humanity. They see the wagon and the coffin and immediately connect the party to the Palace and will do everything in their power to destroy Father Brown's corpse. There are twice as many cultists as there are party members, and the cultists have no leader. What they do have, however, are Rods of Radiant Purity, a beam-weapon that does 1d2 radiant damage to any corpse. They will send a few of their numbers to dump the body onto the ground (out of the coffin) and zap it with the Radiant Rods while they hit and run like a wolfpack. If they do 10 points of radiant damage to the corpse, it will be irrevocably destroyed. If the cult cannot destroy the corpse here, they will try to steal it and take it back to their nearby shrine. <h3The Road (Day 3)</h3> A group of outlaws, who have been living large since the death of the Baron, have taken up residence across the Kings Road leading to the Hamlet. There are nearly 40 of them, strewn out, hippie-camp-style, and having a good old time. They know who Father Brown was and they want to piss on his corpse, and will threaten and insult the party until they hand over the coffin. This technically doesn't break the contract, and if the party agrees, the bandits will consider them mates, and let them pass unharmed, but the party must convince the bandits to let them take the body of Father Brown with them. The leader, Bastard Tim, will do the negotiating, and he is drunk and will not be able to lose face in front of his criminal band. <h3The Hamlet of Bausk (Day 3)</h3> The villagers of Bausk suffered until the cruel yoke of the Baron for decades and celebrated his death with the Winter cider being renamed, "The Baron's Blood" and drained to the lees. They will try and stop the party from entering the village with the body of Father Brown, whom they all call, "The Executioner" and have many stories about his cruelties and perversions. The villagers will become belligerent and angry if the party persists and someone will throw a rock or threaten with a spear or pitchfork and then the crowd will riot. They will attempt to take the coffin and tear Father Brown's body to pieces, stomping on it and doing whatever they can to destroy it. If more than 3 villagers are killed by the party, the crowd will flee in terror, but the party will be subject to ambush bow-attack 3 times before they cross the boundary on the far side of the Hamlet, where the orchards end and the scrub of the Lonely Plains begins. <h3The City of the Dead (Night 3)</h3> This is a vast cemetery, housing thousands of graves, crypts and crypt, and surrounded by a high wall of granite. A caretakers hut sits near the massive wrought-iron gates and directs the party to the Brown Family crypt off in the distance. Once the crypt is reached and the coffin placed inside, and the door closed, the party can now read the ritual to seal the tomb. The ritual takes 10 minutes, and during this time, there will be one last ditch effort to stop the party's activities. A ghost of a fallen Paladin who is buried in the City will manifest in outrage and attempt to disrupt the ritual and/or destroy the scroll. She may have minions in the form of Celestial Animals (treat as ghost-like normal animals). Locations: <h3The Hag's Hut</h3> For a low-level party use a Green Hag. For mid-level party use an Annis or Bheur Hag. For a high-level party use a Night Hag. The Quicklings can have their AC and HP adjusted for whatever level the party is to provide a stronger fight, or you can simply use a lot more of them and require more of them to be able to lift the coffin (instead of 4, maybe its 12, or whatever). <h3The Shrine of Purity</h3> The Cultists can be tailored to the party's level, or maybe a level higher. Use your own discretion. <h3The Hamlet of Bausk</h3> The Villagers are normal humans and are easily killed. I tend to give villagers 3 HP. <h3The Brown Family Crypt</h3> For mid-level or high-level parties, reskin a Wraith or an Alhoon and give it ghost-like qualities. <h3Quest's End</h3> Once the ritual has been successfully completed, the tomb will seal with an arcane crackle. The party's family members will be waiting for them when they exit the City of the Dead, saying they had been transported by magic. One of them has a small pouch of fine leather, tied at the top, and hands it to their relative, saying that it shouldn't be opened here. Your loved ones embrace you, overjoyed at their release, and it seems Gorn was true to his word, as he has paid his treasure and kept his bargain. Its time to return home, and hope that you never see the dreary skies of this wretched place again. */
<<set $adventure to setup.initialisePoisonedPotioner($town, $adventure)>> <h3>Place #1 - The Murder Site</h3>You come across the body of a woman lying in one of the back alleys near <<profile $adventure.tavern>>. She's well dressed, very pale, and roughly 45 years of age. It is not exactly clear how she died, but her eyes are bulging. <<linkreplace "<h4>Inspect the body (Medicine (Wis) DC 10 Check)</h4>">><h4>Clue #1 - The Woman's Body</h4>It looks as though she was gagging before dying.<</linkreplace>> <h4>Clue #2 - The Bloody Vomit</h4>There are two puddles of vomit and blood, each one next to a body. It is as if their innards were all they could retch. <h4>Clue #3 - The Man's Body</h4>A few feet away from the woman's body you notice an unidentified dead man who looks to have been homeless. His clothes are ratty and reek of alcohol. The body itself clearly hasn't been dead long enough to smell so putrid. No clear signs of how he died though his eyes are bulging wide. <<linkreplace "If the players ask anyone about the man" t8n>><blockquote>The man is a homeless drunkard, who drank the potion thinking it to be alcohol. Purely a red herring, if players get too distracted by him, you can have any NPC recognise poor old Frogtoe Bill, and suggest that he met his end by drinking something stronger than what he could handle.</blockquote><</linkreplace>> <h4>Clue #4 - The Empty Coin Purse</h4>Badly hidden on a bush nearby you find an empty woman's purse. Looks fairly expensive with a gold-leaf embroidery. There is a ledger crumpled up inside with an address to a merchant: 39 Mayfern Street. <<linkreplace "Look near the bodies (Perception (Wis) DC 10)" t8n>><h4>Clue #5 - The Glass Vial</h4>Sitting between the two bodies you see a glass bottle with traces of a red liquid. It has no label.<</linkreplace>> <<linkreplace "Look near the bodies (Perception (Wis) DC 14)" t8n>><h4>Clue #6 - Drops of Red Liquid</h4>A few feet away from the woman's body you see droplets of a strange red liquid. <<linkreplace "Inspect the liquid (Medicine (Wis) DC 10)" t8n>>The liquid smells like a harsh medicinal tonic of some sort.<</linkreplace>> <<linkreplace "Compare it to the glass vial (Investigation (Int) DC 10)" t8n>>The red liquid is the same in both cases.<</linkreplace>><</linkreplace>> <<linkreplace "If your players get lost" t8n>>On your way to the crime scene, you remember that you passed through a merchant stall that had really cheap healing potions for sale and it was located on a street with the same name as the one found in the ledger.<</linkreplace>> <h3>Place #2 - Merchant Stall</h3>This merchant stall is located a few blocks away from the crime scene. Even though it's a small stand, it sells a wide variety of items. There's <<profile $adventure.merchant $adventure.merchant.descriptor>> manning the stall that matches the address you found. He introduces himself as <<profile $adventure.merchant>>, and asks what he can do for you. <h4>Clue #1 - Purses</h4> There are numerous purses for sale, though some are rather expensive. Looks like they use the same gold-leaf embroidery technique as the dead woman's purse. <h4>Clue #2 - Tonics and medicine</h4>There is a variety of tonics for sale, including healing potions for just 25gp! <blockquote>The healing potions here have a base price of 25 gp and heals 1d8-1. Each time the characters drinks this potion the modifier decreases by 1. If the modifier is greater than the number rolled on the dice, the character loses health proportional to the result. Example: If a character drinks this potion 4 times, he's gonna "heal" 1d8-4 if he rolls 2 on the dice, he's gonna lose 2 HP instead of healing.</blockquote> <h4>Clue #3 - Tonic bottle</h4>An exact match to the bottle that you've found at the crime scene, except it's sealed. You can't tell the color of the contents inside. <h4>Clue #4 - Merchant's statement*</h4> <<linkreplace "About the purse:" t8n>>Inspecting the purse, <<profile $adventure.merchant>> says "This purse belonged to my boss, <<profile $adventure.madame>>."<</linkreplace>> <<linkreplace "About his boss:" t8n>>"She hasn't come into work yet."<</linkreplace>> <<linkreplace "About a possible enemy of Madame Beaudry:" t8n>>"Her and <<profile $adventure.murderer>>, the apothecarist had a bitter rivalry... I'd say hateful would be a mild way to sum up their relationship."<</linkreplace>> <<linkreplace "About the bottle:" t8n>>"We sell plenty of potions, but without the label I can't tell you if this one is ours."<</linkreplace>> <<linkreplace "About someone who could know more about the bottle:" t8n>>Our alchemist <<profile $adventure.alchemist>> would know if it's ours- he's at the workshop preparing this week's potions. Or you could talk to <<profile $adventure.murderer>>, the apothecarist. He sells the same potion.<</linkreplace>> <blockquote>After the merchant's statement (Clue #4) the characters have two possible locations to visit. The Apothecary (Place #3) and Madame Beaudry's Workshop (Place #4). The critical clue that the characters need to solve the whole mystery is at the Workshop.</blockquote> <h3>Place #3 - <<profile $adventure.murderer $adventure.murderer.firstName>>'s Apothecary Stand</h3>This stand is relatively bigger than the first one but the items on display here are more focused on potions and concoctions. <h4>Clue #1 - Pestle and mortar</h4>A pestle and mortar set to pulverize ingredients. <<linkreplace "On a Medicine (Wis) DC 10 check: (advantage if the player passed on the same test on Clue #6 at the Alley)" t8n>>It's giving off a strong smell of a harsh medicinal tonic.<</linkreplace>> <<linkreplace "On an Investigation (Int) DC 10 check:" t8n>>It seems like they were freshly used.<</linkreplace>> <h4>Clue #2 - Medicinal tonics</h4>All of the bottle look like they were carefully crafted by hand. <<linkreplace "On a Medicine (Wis) DC 10 check: (advantage if the player passed on the same test on Clue #6 at the Alley or Clue #1 from here)" t8n>>They give off a strong smell of medicinal herbs and potions.<</linkreplace>> <h4>Clue #3 - Supplies</h4>Empty bottle, pots, and pans. Essentially anything an alchemist would need to make potions. <<linkreplace "On an Investigation (Int) DC 10 check:" t8n>>It seems like they were recently used.<</linkreplace>> <h4>Clue #4 - Alchemist books and journals</h4>A variety of alchemist and medical journals. Provides for extensive reading. <h4>Clue #5 - <<profile $adventure.murderer $adventure.murderer.name>>'s statement</h4><<linkreplace "About the bottle:" t8n>>"That's not my tonic. You see the cork there? I always mark mine when counting stock. No mark means it's Madame Beaudry's."<</linkreplace>> <<linkreplace "If the players don't give a hint that they know that Mme. Beaudry is dead and if $adventure.murderer.firstName thinks that the players are trustworthy, or if they convince him that they are only asking this because they had a problem with one of the Mme. Beaudry's products. $adventure.murderer.firstName confesses this to them:" t8n>>"That wretched merchant dilutes her potions with all sorts of ingredients to keep her prices down. It's putting me out of business! You take too much of that poison and you'll be a dead man."<</linkreplace>> <h3>Place #4 - Madame Beaudry's Workshop</h3>This workshop is only a block away from the crime scene. <<profile $adventure.alchemist>> is stacking some boxes when you arrive, and welcomes you. <h4>Clue #1 - Pestle and mortar</h4>A pestle and mortar set to pulverize ingredients. <<linkreplace "On an Investigation (Int) DC 10 check:" t8n>>It seems like they were freshly used.<</linkreplace>> <h4>Clue #2 - Medicine bottles</h4>More medicinal tonic bottles that match the bottle that you've found at the crime scene. <h4>Clue #3 - Notebook</h4>A notebook filled with ingredient compositions and potions recipes. A few of them look to be mixtures that can easily be cut with sugar. <<linkappend "<h4>Clue #4 - Letter from $adventure.murderer.name*</h4>" t8n>> Mme. Beaudry, I realize our last few conversations have been quite unpleasant for the both of us. But as good faith gesture, here is a fresh new potion for you to try. It is included in the package. I am certain you will enjoy it even more than the usual mixture. Best, <<profile $adventure.murderer $adventure.murderer.name>> <</linkappend>> <h4>Clue #5 - <<profile $adventure.alchemist $adventure.alchemist.firstName>>'s statement</h4><<linkreplace "About the bottle:" t8n>>"Yeah that bottle is from Madame Beaudry's shop, I'd recognize it anywhere."<</linkreplace>> <<linkreplace "If the players don't tell him that Mme. Beaudry is dead, he confesses to them this:" t8n>>"Thank the gods that awful wretch isn't here today. She pays me nothing and I've had enough!"<</linkreplace>> <<linkreplace "If the players ask where he is going after quitting the job at Beaudry's shop, he says this:" t8n>>"$adventure.murderer.firstName's given me a better offer. I just need to procure myself a little potion first."<</linkreplace>>
<<set _randomNPC to { gender: ["man", "woman"].random(), race: Object.keys(lib.raceTraits).random(), ageStage: ["young adult", "young adult", "young adult", "settled adult", "settled adult", "settled adult", "elderly"].random(), profession: Object.keys(lib.professions).random() }>> <span class="interactive-only hide-on-print flex-line"> <label name="NPC gender" for="listbox-randomnpcgender"><<listbox "_randomNPC.gender" autoselect>><<option "" null>><<optionsfrom ["man", "woman"]>><</listbox>></label> <label name="NPC age stage" for="listbox-randomnpcagestage"><<listbox "_randomNPC.ageStage" autoselect>><<option "" null>><<optionsfrom ["young adult", "settled adult", "settled adult", "settled adult", "elderly"]>><</listbox>></label> <label name="NPC race" for="listbox-randomnpcrace"><<listbox "_randomNPC.race" autoselect>><<option "" null>><<optionsfrom Object.keys(lib.raceTraits)>><</listbox>></label> <label name="NPC profession" for="listbox-randomnpcprofession"><<listbox "_randomNPC.profession" autoselect>><<option "" null>><<optionsfrom Object.keys(lib.professions)>><</listbox>></label> <<button "Create NPC">> <<if def _newNPC>> <<run setup.deleteNPC($npcs[_newNPC])>> <</if>> <<set $toolbox.randomNPC to setup.createNPC($town, _randomNPC)>> <<replace "#NPC">> <div class="descriptive"> <h3>$toolbox.randomNPC.name</h3> <<print lib.articles.output($toolbox.randomNPC.descriptor).toUpperFirst()>> is currently <<print $toolbox.randomNPC.idle.random()>> in the corner. You strike up conversation with $toolbox.randomNPC.himher, and the $toolbox.randomNPC.descriptor introduces $toolbox.randomNPC.himherself as <<profile $toolbox.randomNPC>>, <<print lib.articles.find($toolbox.randomNPC.profession)>> <<print lib.createTippyFull(lib.professions[$toolbox.randomNPC.profession].description.toUpperFirst(), $toolbox.randomNPC.profession)>>. </div> <</replace>> <</button>> </span> <div id="NPC"></div>
<h3>Conditions</h3><span class="tip" data-tippy-content="A blinded creature can’t see and automatically fails any ability check that requires sight. Attack rolls against the creature have advantage, and the creature’s Attack rolls have disadvantage.">Blinded</span> <span class="tip" data-tippy-content="A charmed creature can’t Attack the charmer or target the charmer with harmful Abilities or magical effects. The charmer has advantage on any ability check to interact socially with the creature.">Charmed</span> <span class="tip" data-tippy-content="A deafened creature can’t hear and automatically fails any ability check that requires hearing.">Deafened</span> <span class="tip" data-tippy-content="A frightened creature has disadvantage on Ability Checks and Attack rolls while the source of its fear is within line of sight. The creature can’t willingly move closer to the source of its fear.">Frightened</span> <span class="tip" data-tippy-content="A grappled creature’s speed becomes 0, and it can’t benefit from any bonus to its speed. The condition ends if the Grappler is incapacitated (see the condition). The condition also ends if an effect removes the grappled creature from the reach of the Grappler or Grappling effect, such as when a creature is hurled away by the Thunderwave spell.">Grappled</span> <span class="tip" data-tippy-content="An incapacitated creature can’t take actions or reactions.">Incapacitated</span> <span class="tip" data-tippy-content="An invisible creature is impossible to see without the aid of magic or a Special sense. For the purpose of Hiding, the creature is heavily obscured. The creature’s location can be detected by any noise it makes or any tracks it leaves. Attack rolls against the creature have disadvantage, and the creature’s Attack rolls have advantage. ">Invisible</span> <span class="tip" data-tippy-content="A paralyzed creature is incapacitated (see the condition) and can’t move or speak. The creature automatically fails Strength and Dexterity Saving Throws. Attack rolls against the creature have advantage. Any Attack that hits the creature is a critical hit if the attacker is within 5 feet of the creature.">Paralyzed</span> <span class="tip" data-tippy-content="A petrified creature is transformed, along with any nonmagical object it is wearing or carrying, into a solid inanimate substance (usually stone). Its weight increases by a factor of ten, and it ceases aging. The creature is incapacitated (see the condition), can’t move or speak, and is unaware of its surroundings. Attack rolls against the creature have advantage. The creature automatically fails Strength and Dexterity Saving Throws. The creature has Resistance to all damage. The creature is immune to poison and disease, although a poison or disease already in its system is suspended, not neutralized.">Petrified</span> <span class="tip" data-tippy-content="A poisoned creature has disadvantage on Attack rolls and Ability Checks.">Poisoned</span> <span class="tip" data-tippy-content="A prone creature’s only Movement option is to crawl, unless it stands up and thereby ends the condition. The creature has disadvantage on Attack rolls. An Attack roll against the creature has advantage if the attacker is within 5 feet of the creature. Otherwise, the Attack roll has disadvantage.">Prone</span> <span class="tip" data-tippy-content="A restrained creature’s speed becomes 0, and it can’t benefit from any bonus to its speed. Attack rolls against the creature have advantage, and the creature’s Attack rolls have disadvantage. The creature has disadvantage on Dexterity Saving Throws.">Restrained</span> <span class="tip" data-tippy-content="A stunned creature is incapacitated (see the condition), can’t move, and can speak only falteringly. The creature automatically fails Strength and Dexterity Saving Throws. Attack rolls against the creature have advantage. ">Stunned</span> <span class="tip" data-tippy-content="An unconscious creature is incapacitated (see the condition), can’t move or speak, and is unaware of its surroundings The creature drops whatever it’s holding and falls prone. The creature automatically fails Strength and Dexterity Saving Throws. Attack rolls against the creature have advantage. Any Attack that hits the creature is a critical hit if the attacker is within 5 feet of the creature.">Unconscious</span>
<<nobr>><<link "<h4>Roll up an adventure!</h4>">><<set $adventure to setup.adventure.create($town)>><</link>> <<if $adventure>> <span id="adventure"> <blockquote> <h3>Another campaign</h3> The players must $adventure.goal Their patron, a <<print setup.profile($adventure.patron, $adventure.patronType)>>, has also requested that they $adventure.sidequest Acting against the party's interests is $adventure.villainType, who will accomplish $adventure.villain.hisher goal $adventure.villainActions. The adventurers are aided by a <<print setup.profile($adventure.ally, $adventure.allyType)>>. They do this on the backdrop of <<print lib.articles.output($adventure.backdrop)>>. $adventure.introduction $adventure.climax $adventure.twist </blockquote> </span> <</if>><</nobr>>
<<nobr>> <<linkreplace "<h6>Create a ring!</h6>" t8n>> <<set $randomRing to lib.createRing()>> <<replace "#ring">> <div class='descriptive'> <h3> <<print lib.articles.output(lib.toTitleCase($randomRing.material))>> ring </h3> $randomRing.firstOutputs </div> <</replace>> <<link "<h6>Create another ring</h6>">> <<set $randomRing to lib.createRing()>> <<replace "#ring">> <div class='descriptive'> <h3> <<print lib.articles.output(lib.toTitleCase($randomRing.material))>> ring </h3> $randomRing.secondOutputs </div> <</replace>> <</link>> <</linkreplace>> <span id="ring"></span> <</nobr>>
<<liveblock>> <<include "CreateRandomNPC">> /* <<include "RandomAdventure">> */ <<include "CreateScenario">> <<include "CreateFaction">><<if _tempFaction>><p><<profile _tempFaction>></p><</if>> <h4><<link "One Shot - The Poisoned Potioner" AdventureOutput>><<run setup.history({passageName: "AdventureOutput", linkDescription: "Adventure", name: "Adventure"})>><</link>></h4> <<link "Generate a random roleplaying question for players">><<set $toolbox.question to lib.createRoleplayQuestion()>><<update>><</link>><<if $toolbox.question>><p><div class='descriptive'>$toolbox.question</div></p><</if>> <<link "<h6>Generate some mercenaries</h6>">><<set $toolbox.mercenaries to setup.createMercenaries($town)>><<update>><</link>><<if $toolbox.mercenaries>><div class='descriptive'><h6>$toolbox.mercenaries.name</h6>$toolbox.mercenaries.readout</div><</if>> <<link "<h6>Generate some raiders</h6>">><<set $toolbox.raiders to setup.createRaiders($town)>><<update>><</link>><<if $toolbox.raiders>><div class='descriptive'>$toolbox.raiders</div><blockquote>Original list by <a href='https://redd.it/7ti1g9'>/u/Zazulio</a></blockquote><</if>> <<include "RandomPotion">> <<include "RandomRing">> <<link "<h6>Create a magic weapon</h6>">><<set $toolbox.weapon to lib.createMagic("weapon")>><<update>><</link>><<if $toolbox.weapon>><div class='descriptive'><h6>$toolbox.weapon.name</h6>$toolbox.weapon.description</div><</if>> <<link "<h6>Create magic armour</h6>">><<set $toolbox.armour to lib.createMagic("armour")>><<update>><</link>><<if $toolbox.armour>><div class='descriptive'><h6>$toolbox.armour.name</h6>$toolbox.armour.description</div><</if>> <<link "<h6>Create a magic trinket</h6>">><<set $toolbox.trinket to lib.createMagic("trinket")>><<update>><</link>><<if $toolbox.trinket>><div class='descriptive'><h6>$toolbox.trinket.name</h6>$toolbox.trinket.description</div><</if>> <<link "<h6>Create a trap</h6>">><<set $toolbox.trap to lib.createTrap()>><<update>><</link>><<if $toolbox.trap>><div class='descriptive'>$toolbox.trap.description</div><</if>> <<link "<h6>Create a pub rumour</h6>">><<set $toolbox.pubRumour to lib.createPubRumour()>><<update>><</link>><<if $toolbox.pubRumour>><div class='descriptive'>$toolbox.pubRumour</div><</if>> <<link "<h6>Generate a nightmare</h6>">><<set $toolbox.nightmare to setup.createNightmare()>><<update>><</link>><<if $toolbox.nightmare>><div class='descriptive'>$toolbox.nightmare</div><</if>> <<link "<h6>Generate a road</h6>">><<set $toolbox.road to lib.createRoad()>><<update>><</link>><<if $toolbox.road>><div class='descriptive'>$toolbox.road</div><</if>> <</liveblock>>
<<set _buildingRelationships to lib.findReciprocalRelationships($town, null, $npcs[$currentNPC.key], 'building')>> <<if _buildingRelationships.length > 0>> <tr> <th>Building Name</th> <th>Building Type</th> <<if $currentPassage.deleteEnabled>><th>Relationship</th><th>Delete</th><<else>> <th></th> <th>Relationship</th><</if>> </tr> <<for _buildingRelationship range _buildingRelationships>> <<set _building to lib.findBuilding($town, _buildingRelationship.otherKey)>> <<capture _building>> <tr> <td><<profile _building _building.name>></td> <td><<print lib.toTitleCase(_building.wordNoun)>></td> <<if !$currentPassage.deleteEnabled>> <td></td> <</if>> <td><<if _buildingRelationship.description || _buildingRelationship.reciprocalRelationship>> <<print lib.createTippyFull( _buildingRelationship.description || lib.findInArray($town.buildings, "key", _buildingRelationship.otherKey).name + " is " + lib.articles.output(_buildingRelationship.reciprocalRelationship) + " to " + $npcs[_buildingRelationship.npcKey].firstName, lib.toTitleCase(_buildingRelationship.reciprocalRelationship || _buildingRelationship.relationship))>> <<else>> <<print lib.toTitleCase(_buildingRelationship.reciprocalRelationship || _buildingRelationship.relationship)>> <</if>></td> <<if $currentPassage.deleteEnabled>> <td><<link "x">><<run setup.deleteBuilding($town, _building)>><<update>><</link>></td> <</if>> </tr> <</capture>><</for>> <</if>>
<section><details> <summary>Early Life</summary> <div class='descriptive'>I was born $currentNPC.birthplace, and was raised by $currentNPC.familyUnit<<if $currentNPC.siblingNumber == 0>> as an only child.<<elseif $currentNPC.siblingNumber == 1>> with my brother.<<else>>, along with my $currentNPC.siblingNumber siblings.<</if>> I had <<print lib.articles.output($currentNPC.familyLifestyle)>> upbringing in $currentNPC.familyHome. <<if $currentNPC.knewParents is false>>I didn't know my parents growing up.<</if>><<if $currentNPC.parentalLineage>>$currentNPC.parentalLineage.<</if>> $currentNPC.childhoodMemories.</div> </details></section> <section><details> <summary>Becoming <<print lib.articles.output($currentNPC.profession)>></summary> <div class='descriptive'>"$currentNPC.backgroundOrigin $currentNPC.professionOrigin $currentNPC.bond $currentNPC.ideal"</div> </details></section>
<<set _factionRelationships to lib.findReciprocalRelationships($town, null, $npcs[$currentNPC.key], 'faction')>> <<if _factionRelationships.length > 0>> <tr> <th>Faction Name</th> <th>Faction Type</th> <<if $currentPassage.deleteEnabled>><th>Relationship</th><th>Delete</th><<else>> <th></th> <th>Relationship</th><</if>> </tr> <<for _factionRelationship range _factionRelationships>> <<set _faction to $town.factions[_factionRelationship.otherKey]>> <<capture _faction>> <tr> <td><<profile _faction _faction.name>></td> <td><<print lib.toTitleCase(_faction.wordNoun)>></td> <<if !$currentPassage.deleteEnabled>> <td></td> <</if>> <td><<if _factionRelationship.description || _factionRelationship.reciprocalRelationship>> <<print lib.createTippyFull( _factionRelationship.description || lib.findInContainer($town.factions, "key", _factionRelationship.otherKey).name + " is " + lib.articles.output(_factionRelationship.reciprocalRelationship) + " to " + $npcs[_factionRelationship.npcKey].firstName, lib.toTitleCase(_factionRelationship.reciprocalRelationship || _factionRelationship.relationship))>> <<else>> <<print lib.toTitleCase(_factionRelationship.reciprocalRelationship || _factionRelationship.relationship)>> <</if>></td> <<if $currentPassage.deleteEnabled>> <td><<link "x">><<run setup.deleteFaction($town, _faction)>><<update>><</link>></td> <</if>> </tr> <</capture>><</for>> <</if>>
<<if $npcs[$currentPassage.key].isShallow === true || $npcs[$currentPassage.key].hasHistory === false>> <<run setup.expandNPC($town, $npcs[$currentPassage.key])>> <</if>> <<include "NPCSaveEdits">> <<set $currentNPC to $npcs[$currentPassage.key]>> <<set $currentImage to $currentPassage.customImage || null>> <<run lib.logger.openGroup($currentNPC.name)>> <h1 class="interactive-only"> <<if $currentNPC.title !== "Mr" && $currentNPC.title !== "Ms">> $currentNPC.title <</if>> $currentNPC.name </h1> <span class='flex-line'> <<if def $currentNPC.key>> <<button "Edit $currentNPC.name">> <<run setup.openDialog({ header: `Editing ${$currentNPC.name}`, passage: 'NPCProfileEdit', rerender: true, dialogOpts: $npcs[$currentPassage.key], func: setup.preparePlaceholders })>> <</button>> <</if>> <<if $currentNPC.isThrowaway === true>><span class='hide-on-print'> -- </span> <<button "Save to NPCs list">> <<set $npcs[$currentPassage.key].isThrowaway to false>> <<run lib.logger.info("Saved " + $currentNPC.name + " to the NPCs list.")>> <<notify 5000>>Saved $currentNPC.name to the NPCs list.<</notify>> <<run lib.logger.info($currentNPC)>> <<goto "NPCProfile">> <</button>> <</if>> </span> <<if $currentImage>> <span style=" position: relative; float: right; width: 50%; z-index: -2;" > <<print lib.getCustomImage($currentImage, $currentNPC.firstName + '-image').outerHTML>> </span> <</if>>
<<nobr>> <section>$currentNPC.firstName currently earns <<if setup.money(lib.npcNetIncome($town, $currentNPC)) < 1>>next to nothing <<else>><<print setup.money(lib.npcNetIncome($town, $currentNPC))>><</if>> per day. <details><summary>Finances</summary> <table> <tr> <th>Type</th> <th>Amount</th> </tr> <tr><td>Gross Income</td><td><<print setup.money(lib.npcGrossIncome($town, $currentNPC))>></td></tr> <tr><td><i>Tax</i></td><td><i><<print setup.money(lib.npcTaxRate($town))>></i></td></tr> <tr><td>Net Income</td><td><<print setup.money(lib.npcNetIncome($town, $currentNPC))>></td></tr> <tr><td>Total Lifestyle Expenses (<<print lib.createTippyFull($currentNPC.firstName + " " + lib.npcLifestyleStandard($town, $currentNPC).lifestyleDescription, lib.npcLifestyleStandard($town, $currentNPC).lifestyleStandard)>>)</td><td><<print setup.money(lib.npcTotalLifestyleExpenses($town, $currentNPC))>></td></tr> <tr><td>Profit</td><td><<print setup.money(lib.npcProfit($town, $currentNPC))>></td></tr> <<for _creditorKey, _debtOwed range $currentNPC.finances.creditors>> <tr><td>Money owed to <<profile $npcs[_creditorKey]>></td><td><<print setup.money(_debtOwed)>></td></tr> <</for>> <<for _debtorKey, _debtOwed range $currentNPC.finances.debtors>> <tr><td>Money owed by <<profile $npcs[_debtorKey]>></td><td><<print setup.money(_debtOwed)>></td></tr> <</for>> </table></details></section><</nobr>>
<details><summary>Life Events</summary><div class='descriptive'>Well, I certainly have a tale or two... \<<print setup.getLifeEvents($currentNPC)>></div></details>
<tr> <th>Name</th> <th>Race</th> <th>Occupation</th> <th>Relationship</th> <<if $currentPassage.deleteEnabled>><th>Delete</th><</if>> </tr> <<for _relationship range $town.npcRelations[$currentNPC.key]>><<set _profession to lib.findProfession($town, $npcs[_relationship.targetNpcKey])>><<set _description to $currentNPC.firstName + " is " + lib.articles.output($town.npcRelations[_relationship.targetNpcKey].filter(r => r.targetNpcKey === $currentNPC.key)[0].relation) + " to " + $npcs[_relationship.targetNpcKey].firstName>> <tr> <td><<profile $npcs[_relationship.targetNpcKey]>></td> <td><<print $npcs[_relationship.targetNpcKey].race.toUpperFirst()>></td> <td><<print lib.createTippyFull(_profession.description.toUpperFirst(), $npcs[_relationship.targetNpcKey].profession.toUpperFirst())>></td> <td><<print lib.createTippyFull((_relationship.description || _description), _relationship.relation.toUpperFirst())>></td> <<if $currentPassage.deleteEnabled>><td><<link "x">><<set _deleted to $npcs[_relationship.targetNpcKey]>><<run setup.deleteNPC($npcs[_relationship.targetNpcKey])>><<notify 5000>>Deleted _deleted.name<</notify>><<update>><</link>></td><</if>> </tr> <</for>>
<<nobr>> <<if $town.npcRelations[$currentNPC.key]>> <div class='classTable'> <details><summary>Relationships</summary> <table> <<include "NPCRelationships">> <<include "NPCBuildingRelationships">> <<include "NPCFactionRelationships">> </table> </details></div> <</if>> <</nobr>>
<<nobr>> <<if $town.npcRelations[$currentNPC.key]>> <div class='classTable'> <details><summary>Relationships</summary> <table> <tr> <th>Name</th> <th>Race</th> <th>Occupation</th> <th>Relationship</th> <<if $currentPassage.deleteEnabled>><th>Delete</th><</if>> </tr> <<for _relationship range $town.npcRelations[$currentNPC.key]>><<set _profession to lib.findProfession($town, $npcs[_relationship.targetNpcKey])>><<set _description to $currentNPC.firstName + " is " + lib.articles.output($town.npcRelations[_relationship.targetNpcKey].filter(r => r.targetNpcKey === $currentNPC.key)[0].relation) + " to " + $npcs[_relationship.targetNpcKey].firstName>> <tr> <td><<profile $npcs[_relationship.targetNpcKey]>></td> <td><<print $npcs[_relationship.targetNpcKey].race.toUpperFirst()>></td> <td><<print lib.createTippyFull(_profession.description.toUpperFirst(), $npcs[_relationship.targetNpcKey].profession.toUpperFirst())>></td> <td><<print lib.createTippyFull((_relationship.description || _description), _relationship.relation.toUpperFirst())>></td> <<if $currentPassage.deleteEnabled>><td><<link "x">><<set _deleted to $npcs[_relationship.targetNpcKey]>><<run setup.deleteNPC($npcs[_relationship.targetNpcKey])>><<notify 5000>>Deleted _deleted.name<</notify>><<update>><</link>></td><</if>> </tr> <</for>> <<set _buildingRelationships to lib.findReciprocalRelationships($town, null, $npcs[$currentNPC.key], 'building')>> <<if _buildingRelationships.length > 0>> <tr> <th>Building Name</th> <th>Building Type</th> <<if $currentPassage.deleteEnabled>><th>Relationship</th><th>Delete</th><<else>> <th></th> <th>Relationship</th><</if>> </tr> <<for _buildingRelationship range _buildingRelationships>> <<set _building to lib.findBuilding($town, _buildingRelationship.otherKey)>> <<capture _building>> <tr> <td><<profile _building _building.name>></td> <td><<print lib.toTitleCase(_building.wordNoun)>></td> <<if !$currentPassage.deleteEnabled>> <td></td> <</if>> <td><<if _buildingRelationship.description || _buildingRelationship.reciprocalRelationship>> <<print lib.createTippyFull( _buildingRelationship.description || lib.findInArray($town.buildings, "key", _buildingRelationship.otherKey).name + " is " + lib.articles.output(_buildingRelationship.reciprocalRelationship) + " to " + $npcs[_buildingRelationship.npcKey].firstName, lib.toTitleCase(_buildingRelationship.reciprocalRelationship || _buildingRelationship.relationship))>> <<else>> <<print lib.toTitleCase(_buildingRelationship.reciprocalRelationship || _buildingRelationship.relationship)>> <</if>></td> <<if $currentPassage.deleteEnabled>> <td><<link "x">><<run setup.deleteBuilding($town, _building)>><<update>><</link>></td> <</if>> </tr> <</capture>><</for>> <</if>> <<set _factionRelationships to lib.findReciprocalRelationships($town, null, $npcs[$currentNPC.key], 'faction')>> <<if _factionRelationships.length > 0>> <tr> <th>Faction Name</th> <th>Faction Type</th> <<if $currentPassage.deleteEnabled>><th>Relationship</th><th>Delete</th><<else>> <th></th> <th>Relationship</th><</if>> </tr> <<for _factionRelationship range _factionRelationships>> <<set _faction to $town.factions[_factionRelationship.otherKey]>> <<capture _faction>> <tr> <td><<profile _faction _faction.name>></td> <td><<print lib.toTitleCase(_faction.wordNoun)>></td> <<if !$currentPassage.deleteEnabled>> <td></td> <</if>> <td><<if _factionRelationship.description || _factionRelationship.reciprocalRelationship>> <<print lib.createTippyFull( _factionRelationship.description || lib.findInContainer($town.factions, "key", _factionRelationship.otherKey).name + " is " + lib.articles.output(_factionRelationship.reciprocalRelationship) + " to " + $npcs[_factionRelationship.npcKey].firstName, lib.toTitleCase(_factionRelationship.reciprocalRelationship || _factionRelationship.relationship))>> <<else>> <<print lib.toTitleCase(_factionRelationship.reciprocalRelationship || _factionRelationship.relationship)>> <</if>></td> <<if $currentPassage.deleteEnabled>> <td><<link "x">><<run setup.deleteFaction($town, _faction)>><<update>><</link>></td> <</if>> </tr> <</capture>><</for>> <</if>> </table> </details></div> <</if>> <</nobr>>
<<include "NPCHeader">> <<if $currentNPC.title !== "Mr" && $currentNPC.title !== "Ms">>$currentNPC.title <</if>>$currentNPC.name was <<print lib.createTippyFull($currentNPC.ageYears + " years old, to be exact.", lib.articles.output($currentNPC.age))>> $currentNPC.malefemale $currentNPC.race. <<print $currentNPC.heshe.toUpperFirst()>> was <<print lib.createTippyFull(setup.metricHeight($currentNPC.heightInches, settings.showMetric), $currentNPC.height)>> and <<print lib.createTippyFull(setup.metricWeight($currentNPC, settings.showMetric), $currentNPC.weight)>>, and had $currentNPC.eyes eyes<<if def $currentNPC.beard>> and <<print lib.articles.output($currentNPC.beard)>>,<</if>> with $currentNPC.skinColour skin. The most notable physical trait of $currentNPC.firstName was that $currentNPC.heshe had $currentNPC.physicalTrait. When $currentNPC.heshe was relaxed, $currentNPC.heshe was $currentNPC.calmTrait. In moments of stress, $currentNPC.heshe became $currentNPC.stressTrait. <<if $currentNPC.knownLanguages.length == 2>>$currentNPC.firstName knew $currentNPC.knownLanguages[0] and $currentNPC.knownLanguages[1].<</if>> $currentNPC.firstName was <<print lib.articles.output($currentNPC.profession)>>, with a background of being <<print lib.articles.output($currentNPC.background)>>. $currentNPC.firstName had $currentNPC.pockets in $currentNPC.hisher pockets at $currentNPC.hisher time of death, and <<money $currentNPC.wealth>> to $currentNPC.hisher name. <<if $currentNPC.sexuality !== 'heterosexual'>> <<print $currentNPC.heshe.toUpperFirst()>> was $currentNPC.sexuality <<if $currentNPC.partnerID>> , and is survived by $currentNPC.hisher <<print $npcs[$currentNPC.partnerID].marriageNoun>>, <<profile $npcs[$currentNPC.partnerID]>>. <</if>> <<elseif $currentNPC.partnerID>>$currentNPC.firstName is survived by $currentNPC.hisher <<print $npcs[$currentNPC.partnerID].marriageNoun>>, <<profile $npcs[$currentNPC.partnerID]>>. <</if>> <<if def $currentNPC.note >>$currentNPC.note<</if>> <h4>Death</h4> $currentNPC.death.cause $currentNPC.death.burialConditions <<linkreplace "Early Life" t8n>><h5>Early Life</h5><div class='descriptive'>$currentNPC.firstName was born $currentNPC.birthplace, and was raised by $currentNPC.familyUnit<<if $currentNPC.siblingNumber == 0>> as an only child.<<elseif $currentNPC.siblingNumber == 1>> with $currentNPC.hisher brother.<<else>>, along with $currentNPC.hisher $currentNPC.siblingNumber siblings.<</if>> $currentNPC..heshe.toUpperFirst() had <<print lib.articles.output($currentNPC.familyLifestyle)>> upbringing in $currentNPC.familyHome. <<if $currentNPC.knewParents is false>>$currentNPC.heshe.toUpperFirst() didn't know $currentNPC.hisher parents growing up.<</if>><<if $currentNPC.parentalLineage>>$currentNPC.parentalLineage.<</if>> $currentNPC.childhoodMemories.</div><</linkreplace>> <<linkreplace "Becoming a $currentNPC.profession" t8n>><h5>Becoming <<print lib.articles.output($currentNPC.profession)>></h5><div class='descriptive'>"$currentNPC.backgroundOrigin $currentNPC.professionOrigin $currentNPC.bond $currentNPC.ideal"</div><</linkreplace>> <<include "NPCRelationshipsTable">>
<<set $currentNPC to $npcs[$currentPassage.key]>> <<liveblock>> <span class="interactive-only"> <<include "NPCHeader">> </span> <<set _deity to lib.getDeity($town, $currentNPC.religion.deity, State.metadata.get('pantheon'))>> <quote> <<print lib.extractIpa($currentNPC.name)>> </quote> <p class='no-indent'> <span @id="$currentNPC.key"> <<if $currentNPC.title !== "Mr" && $currentNPC.title !== "Ms">> <<print lib.firstCharacter($currentNPC.title)>> $currentNPC.name <<else>> <<print lib.firstCharacter($currentNPC.name)>> <</if>> </span> is <<print lib.createTippyFull($currentNPC.ageYears + " years old, to be exact.", lib.articles.output($currentNPC.age))>> $currentNPC.malefemale $currentNPC.race. <<print $currentNPC.heshe.toUpperFirst()>> is <<print lib.createTippyFull(setup.metricHeight($currentNPC.heightInches, settings.showMetric), $currentNPC.height)>> and <<print lib.createTippyFull(setup.metricWeight($currentNPC, settings.showMetric), $currentNPC.weight)>>, and has $currentNPC.eyes eyes <<if def $currentNPC.beard>> and <<print lib.articles.output($currentNPC.beard)>>, <</if>> with $currentNPC.skinColour skin. The most notable physical trait of $currentNPC.firstName is that $currentNPC.heshe has $currentNPC.physicalTrait. </p> <p> $currentNPC.firstName $currentNPC.trait. <<if def $currentNPC.vocalPattern>> <<print $currentNPC.heshe.toUpperFirst()>> $currentNPC.vocalPattern. <</if>> <<if typeof lib.getTraitsReadout($currentNPC) === 'string'>> <<print $currentNPC.heshe.toUpperFirst()>> is <<print lib.getTraitsReadout($currentNPC)>> <</if>> When $currentNPC.heshe is relaxed, $currentNPC.heshe is $currentNPC.calmTrait. In moments of stress, $currentNPC.heshe becomes $currentNPC.stressTrait. Religion-wise, $currentNPC.firstName is <<print lib.createTippyFull(lib.getReligiosityDescription($town, $currentNPC), lib.articles.output($currentNPC.religion.strength))>><<if _deity>> of <<profile _deity>><</if>>. <p> <<if lib.isBreakingGenderNorms($town, $currentNPC) && $currentNPC.roll.professionLuck > 0>> Despite sexism against $currentNPC.himher, <<elseif lib.isBreakingGenderNorms($town, $currentNPC) && $currentNPC.roll.professionLuck < 0>> Perhaps due to sexism, <</if>> $currentNPC.professionSuccess, with a background of being <<print lib.articles.output($currentNPC.background)>>. <<if lib.socialClass[$currentNPC.socialClass].lifestyle.includes(lib.npcLifestyleStandard($town, $currentNPC).lifestyleStandard)>> <<print $currentNPC.heshe.toUpperFirst()>> belongs to the <<print lib.createTippyFull($currentNPC.firstName + " " + lib.npcLifestyleStandard($town, $currentNPC).lifestyleDescription, $currentNPC.socialClass)>> social class. <<else>> <<print $currentNPC.heshe.toUpperFirst()>> belongs to the $currentNPC.socialClass social class, but <<print lib.npcLifestyleStandard($town, $currentNPC).lifestyleDescription>>. <</if>> </p> <p> $currentNPC.firstName currently has $currentNPC.pockets in $currentNPC.hisher pockets, and <<money $currentNPC.wealth>> to $currentNPC.hisher name. <<if $town.roads[$town.families[$currentNPC.family].home.road]>> <<print $currentNPC.heshe.toUpperFirst()>> lives on <<profile $town.roads[$town.families[$currentNPC.family].home.road]>>. <</if>> <<if $currentNPC.weapon>> In combat, $currentNPC.heshe uses $currentNPC.weapon. <</if>> <<if $currentNPC.knownLanguages.length == 2>> $currentNPC.firstName knows $currentNPC.knownLanguages[0] and <<print $currentNPC.knownLanguages[1]>>. <</if>> <<if def $currentNPC.doesnt>> $currentNPC.doesnt <</if>> <<if $currentNPC.sexuality !== 'heterosexual'>> <<print $currentNPC.heshe.toUpperFirst()>> $currentNPC.sexuality <<if $currentNPC.partnerID>> , and has <<print lib.articles.output($npcs[$currentNPC.partnerID].marriageNoun)>>, <<profile $npcs[$currentNPC.partnerID]>>. <</if>> <<elseif $currentNPC.partnerID>> $currentNPC.firstName has <<print lib.articles.output($npcs[$currentNPC.partnerID].marriageNoun)>>, <<profile $npcs[$currentNPC.partnerID]>>. <</if>> </p> <<include "NPCIncomeTable">> <<if def $currentNPC.note>> <p> $currentNPC.note </p> <</if>> <<include "NPCEarlyLife">> <<include "NPCLifeEvents">> <<include "NPCRelationshipsTable">><</liveblock>> <<run lib.logger.closeGroup()>> <<run lib.logger.info($currentNPC)>>
<<set _sentiment to "that " + ['funny', 'nice', 'annoying', 'mean', 'weird', 'kind', 'scary'].random() + " " + $currentNPC.race>> NPC to add a relationship with: <label name="add relationship" for="listbox-swapnpc"> <<listbox "_newRelationshipNpcName" autoselect>> <<optionsfrom Object.values($npcs).map(el => el.name)>> <<optionsfrom lib.patreonCharacters>> <</listbox>></label> Relationship: <<textbox "_newRelationship" "old friend">> Reciprocal relationship: <<textbox "_newReciprocal" _sentiment>> Description: <<textbox "_newDescription" "">> <<button "Create Relationship">> <<run setup.createRelationship($town, $npcs[$currentNPC.key], lib.findInContainer($npcs)('name', _newRelationshipNpcName), { relationship: _newRelationship, reciprocalRelationship: _newReciprocal, description: _newDescription })>> <<update>> <<notify 5000>>Added _newRelationshipNpcName's relationship of _newRelationship.<</notify>> <</button>>
Social Class: <<listbox "$npcs[$currentPassage.key].socialClass" autoselect>> <<optionsfrom Object.keys(lib.socialClass)>> <</listbox>> <br> Lifestyle standard: <<listbox "$npcs[$currentPassage.key].lifestyleStandard" autoselect>> <<optionsfrom Object.keys(lib.lifestyleStandards)>> <</listbox>> <br> <label for="numberbox-npcs-currentpassage-key-heightInches">Height (in inches):</label> <<numberbox "$npcs[$currentPassage.key].heightInches" $npcs[$currentPassage.key].heightInches>> /* : <<lh $npcs[$currentPassage.key].height>> */ <br> <label for="numberbox-npcs-currentpassage-key-weightPounds">Weight (in pounds):</label> <<numberbox "$npcs[$currentPassage.key].weightPounds" $npcs[$currentPassage.key].weightPounds>> /* : <<lh $npcs[$currentPassage.key].weight>> */ <br> <h4>Personality Traits (5-95 range)</h4> <<for _key, _obj range $npcs[$currentPassage.key].roll.traits>> <<set _trait to $npcs[$currentPassage.key].roll.traits[_key]>> <<set _label to "numberbox-npcs-currentpassage-key-roll-traits-" + _key>> <<capture _key, _obj, _trait, _label>> <label @for=_label><<lh _key.toUpperFirst()>></label>: <<numberbox "$npcs[$currentPassage.key].roll.traits[_key]" _trait>> <br> <</capture>> <</for>>
<span class="auto-update"> <h1> <<include "NPCSaveEdits">> <<if def $npcs[$currentPassage.key].title>> <<print $npcs[$currentPassage.key].title>> <</if>> <label name="name"> <<textbox "$npcs[$currentPassage.key].name" $npcs[$currentPassage.key].name>> </label> </h1> </span> <<set $currentPassage.deleteEnabled to true>> <span class='flex-line'> <label name="replace NPC" for="listbox-replacenpc"> <<listbox "$npcs[$currentPassage.key]" autoselect>> <<optionsfrom $npcs>> <<optionsfrom lib.patreonCharacters>> <</listbox>> </label> <<button "Replace NPC">> <<set $npcs[$currentPassage.key] to setup.createNPC($town, _replaceNpc)>> <</button>> <<button "Delete this NPC" Start>> <<run setup.deleteNPC($npcs[$currentPassage.key])>> <</button>> /* <<button "Reroll name">> <<set $npcs[$currentPassage.key].firstName to lib.createName({ race: $npcs[$currentPassage.key].race, gender: $npcs[$currentPassage.key].gender, firstOrLast: 'firstName' })>> <<set $npcs[$currentPassage.key].lastName to lib.createName({ race: $npcs[$currentPassage.key].race, gender: $npcs[$currentPassage.key].gender, firstOrLast: 'lastName' })>> <<update>> <</button>> */ </span> <br> <<if def $npcs[$currentPassage.key].title>> <<print $npcs[$currentPassage.key].title>> <</if>><<print $npcs[$currentPassage.key].name>> is <<print lib.articles.find($npcs[$currentPassage.key].age)>> <span @id="_age" class="tip"> <label name="age"> <<textbox "$npcs[$currentPassage.key].age" $npcs[$currentPassage.key].age>> </label> </span> <span class="auto-update"> <span class="tip" title="Non binary options coming soon!"> <label name="gender" for="listbox-npcscurrentpassagekeygender"> <<listbox "$npcs[$currentPassage.key].gender" autoselect>> <<optionsfrom Object.keys(lib.genderData)>> <</listbox>> </label> </span> <label name="race" for="listbox-npcscurrentpassagekeyrace"> <<listbox "$npcs[$currentPassage.key].race" autoselect>> <<optionsfrom Object.keys(lib.raceTraits)>> <</listbox>> </label> </span>. <<print $npcs[$currentPassage.key].heshe.toUpperFirst()>> is <span @id="_height" class="tip"> <label name="height"> <<textbox "$npcs[$currentPassage.key].height" $npcs[$currentPassage.key].height>> </label> </span> and <span @id="_weight" class="tip"> <label name="weight"> <<textbox "$npcs[$currentPassage.key].weight" $npcs[$currentPassage.key].weight>> </label> </span>, and has <label name="eyes"> <<textbox "$npcs[$currentPassage.key].eyes" $npcs[$currentPassage.key].eyes>> </label> eyes<<if def $npcs[$currentPassage.key].beard>> and <<print lib.articles.find($npcs[$currentPassage.key].beard)>> <label name="beard"><<textbox "$npcs[$currentPassage.key].beard" $npcs[$currentPassage.key].beard>></label>,<</if>> with <label name="skinColour"><<textbox "$npcs[$currentPassage.key].skinColour" $npcs[$currentPassage.key].skinColour>></label> skin. The most notable physical trait of <<print $npcs[$currentPassage.key].firstName>> is that <<print $npcs[$currentPassage.key].heshe>> has <label name="physicalTrait"><<textbox "$npcs[$currentPassage.key].physicalTrait" $npcs[$currentPassage.key].physicalTrait>></label>. <<print $npcs[$currentPassage.key].firstName>> <label name="trait"><<textbox "$npcs[$currentPassage.key].trait" $npcs[$currentPassage.key].trait>></label>. <<if def $npcs[$currentPassage.key].vocalPattern>><<print $npcs[$currentPassage.key].heshe.toUpperFirst()>> <label name="vocalPattern"><<textbox "$npcs[$currentPassage.key].vocalPattern" $npcs[$currentPassage.key].vocalPattern>></label>. <</if>>When <<print $npcs[$currentPassage.key].heshe>> is relaxed, <<print $npcs[$currentPassage.key].heshe>> is <label name="calmTrait"><<textbox "$npcs[$currentPassage.key].calmTrait" $npcs[$currentPassage.key].calmTrait>></label>. In moments of stress, <<print $npcs[$currentPassage.key].heshe>> becomes <label name="stressTrait"><<textbox "$npcs[$currentPassage.key].stressTrait" $npcs[$currentPassage.key].stressTrait>></label>. <<if $npcs[$currentPassage.key].knownLanguages.length == 2>><<print $npcs[$currentPassage.key].firstName>> knows <<print $npcs[$currentPassage.key].knownLanguages[0]>> and <<print $npcs[$currentPassage.key].knownLanguages[1]>>.<</if>> <<print $npcs[$currentPassage.key].firstName>> is <<print lib.articles.find($npcs[$currentPassage.key].profession)>> <<listbox "$npcs[$currentPassage.key].profession" autoselect>> <<optionsfrom Object.keys(lib.professions)>> <</listbox>> , with a background of being <<print lib.articles.find($npcs[$currentPassage.key].background)>> <<listbox "$npcs[$currentPassage.key].background" autoselect>> <<optionsfrom Object.keys(lib.backgroundTraits)>> <</listbox>>. <<print $npcs[$currentPassage.key].firstName>> currently has <label name="pockets"> <<textbox "$npcs[$currentPassage.key].pockets" $npcs[$currentPassage.key].pockets>> </label> in <<print $npcs[$currentPassage.key].hisher>> pockets, and <<money $npcs[$currentPassage.key].wealth>> to <<print $npcs[$currentPassage.key].hisher>> name. /* In combat, <<print $npcs[$currentPassage.key].heshe>> uses <label name="weapon"><<textbox "$npcs[$currentPassage.key].weapon" $npcs[$currentPassage.key].weapon>></label>. */ <br> <span class="tip" data-tippy-content="Add notes here"><<textarea "$npcs[$currentPassage.key].note" $npcs[$currentPassage.key].note>></span> <br> <details><summary>Edit Stats</summary><<include "NPCEditStats">></details> <br> <details><summary>Early Life</summary><div class='descriptive'>I was born <label name="birthplace"><<textbox "$npcs[$currentPassage.key].birthplace" $npcs[$currentPassage.key].birthplace>></label>, and was raised by <label name="familyUnit"><<textbox "$npcs[$currentPassage.key].familyUnit" $npcs[$currentPassage.key].familyUnit>></label><<if $npcs[$currentPassage.key].siblingNumber == 0>> as an only child.<<elseif $npcs[$currentPassage.key].siblingNumber == 1>> with my brother.<<else>>, along with my $npcs[$currentPassage.key].siblingNumber siblings.<</if>> I had <<print lib.articles.find($npcs[$currentPassage.key].familyLifestyle)>> <label name="familyLifestyle"><<textbox "$npcs[$currentPassage.key].familyLifestyle" $npcs[$currentPassage.key].familyLifestyle>></label> upbringing in <label name="familyHome"><<textbox "$npcs[$currentPassage.key].familyHome" $npcs[$currentPassage.key].familyHome>></label>. <<if !$npcs[$currentPassage.key].knewParents>>I didn't know my parents growing up.<</if>><<if $npcs[$currentPassage.key].parentalLineage>>$npcs[$currentPassage.key].parentalLineage.<</if>> <label name="childhoodMemories"><<textbox "$npcs[$currentPassage.key].childhoodMemories" $npcs[$currentPassage.key].childhoodMemories>></label>.</div></details> <br> <details><summary>Becoming a <<print $npcs[$currentPassage.key].profession>></summary><div class='descriptive'>"<<textarea "$npcs[$currentPassage.key].backgroundOrigin" $npcs[$currentPassage.key].backgroundOrigin>> <<textarea "$npcs[$currentPassage.key].professionOrigin" $npcs[$currentPassage.key].professionOrigin>> That's how I became <<print lib.articles.output($npcs[$currentPassage.key].profession)>>. <<textarea "$npcs[$currentPassage.key].bond" $npcs[$currentPassage.key].bond>> <<textarea "$npcs[$currentPassage.key].ideal" $npcs[$currentPassage.key].ideal>>"</div></details> <br> <details><summary>Life Events</summary><div class='descriptive'>Well, I certainly have a tale or two... <<print setup.getLifeEvents($currentNPC)>></div></details> <<if def $npcs[$currentPassage.key].partnerID>><<print $npcs[$currentPassage.key].firstName>> has a partner, <<profile $npcs[$currentPassage.key].partnerID>><</if>> <<include "NPCRelationshipsTableEdit">> <<button "Save changes" NPCProfile>> <<include "NPCSaveEdits">> <</button>>
<<nobr>> <div class='classTable'> <details><summary>Relationships</summary> <<details "NPCAddRelationship" "Add new relationship">> <table> <tr> <th>Name</th> <th>Occupation</th> <th>Relationship</th> <th>Reciprocal Relationship</th> <th>Description of Relationship</th> <th>Delete Relationship</th> </tr> <<for _index, _relationship range $town.npcRelations[$currentNPC.key]>> <<set _profession to lib.findProfession($town, $npcs[_relationship.targetNpcKey])>> <<set _reciprocalIndex to $town.npcRelations[_relationship.targetNpcKey].findIndex(r => r.targetNpcKey === $currentNPC.key)>> <<set _reciprocal to $town.npcRelations[_relationship.targetNpcKey].filter(r => r.targetNpcKey === $currentNPC.key)[0]>> <<capture _index, _reciprocal, _reciprocalIndex>> <tr> <td><<profile $npcs[_relationship.targetNpcKey]>></td> <td><<print lib.createTippyFull(_profession.description.toUpperFirst(), $npcs[_relationship.targetNpcKey].profession.toUpperFirst())>></td> <td><<textbox "$town.npcRelations[$currentNPC.key][_index].relation" _relationship.relation>></td> <td><<textbox "$town.npcRelations[_relationship.targetNpcKey][_reciprocalIndex].relation" _reciprocal.relation>></td> <td><<textbox "$town.npcRelations[$currentNPC.key][_index].description" _relationship.description>></td> <td><<link "x">><<set _deleted to $npcs[_relationship.targetNpcKey]>><<run $town.npcRelations[_relationship.targetNpcKey].splice(_reciprocalIndex, 1)>><<run $town.npcRelations[$currentNPC.key].splice(_index, 1)>><<notify 5000>>Deleted _deleted.name's relationship.<</notify>><<update>><</link>></td> </tr> <</capture>> <</for>> <<set _entityRelationships to lib.findReciprocalRelationships($town, null, $npcs[$currentNPC.key], 'building')>> <<if _entityRelationships.length > 0>> <tr> <th>Building Name</th> <th>Building Type</th> <th>Relationship</th> <th>Delete Relationship</th> </tr> <<for _buildingIndex, _buildingRelationship range _entityRelationships>> <<set _building to lib.findBuilding($town, _buildingRelationship.otherKey)>> <<capture _building, _buildingIndex>> <td><<profile _building _building.name>></td> <td><<print lib.toTitleCase(_building.wordNoun)>></td> <td><<if _buildingRelationship.description || _buildingRelationship.reciprocalRelationship>> <<print lib.createTippyFull( _buildingRelationship.description || lib.findInArray($town.buildings, "key", _buildingRelationship.otherKey).name + " is " + lib.articles.output(_buildingRelationship.reciprocalRelationship) + " to " + $npcs[_buildingRelationship.npcKey].firstName, lib.toTitleCase(_buildingRelationship.reciprocalRelationship || _buildingRelationship.relationship))>> <<else>> <<print lib.toTitleCase(_buildingRelationship.reciprocalRelationship || _buildingRelationship.relationship)>> <</if>></td> <td><<link "x">><<run $town.buildingRelations.splice(lib.findIndexOfReciprocalRelationship($town, _building, $currentNPC), 1)>><<update>><</link>></td> <</capture>><</for>> <</if>> </table> </details></div> <</nobr>>
<<run lib.logger.info('Saving changes to ' + $npcs[$currentPassage.key].name + '...')>> <<set $npcs[$currentPassage.key].gender to $npcs[$currentPassage.key].gender>> <<run Object.assign($npcs[$currentPassage.key], lib.genderData[$npcs[$currentPassage.key].gender])>> <<run Object.assign($npcs[$currentPassage.key], lib.raceTraits[$npcs[$currentPassage.key].race].raceWords)>>
<label name="swap NPC" for="listbox-swapnpc"> <<listbox "_swapNpc" autoselect>> <<optionsfrom Object.values($npcs).map(el => el.name)>> <<optionsfrom lib.patreonCharacters>> <</listbox>></label> -- <<button "Swap NPC" NPCProfileEdit>> <<run lib.logger.openGroup('Swapping NPC!')>> <<set _npc1 to Object.assign($npcs[$currentPassage.key], { isThrowaway: false, keyIsAlreadyDefined: true })>> <<set _npc2 to Object.assign(lib.findInContainer($npcs)('name', _swapNpc), { isThrowaway: false, keyIsAlreadyDefined: true })>> <<set _temp to Object.assign($npcs[$currentPassage.key], { isThrowaway: false, keyIsAlreadyDefined: true })>> <<run lib.logger.info(_npc1, _npc2)>> <<run lib.logger.info(_temp)>> <<run lib.swapNPCs($town, $npcs, $currentPassage.key, _npc2.key)>> <<set $npcs[_npc2.key] to setup.createNPC($town, _temp)>> <<set $npcs[$currentPassage.key] to setup.createNPC($town, _npc2)>> <<run lib.logger.info('Done!')>> <<run lib.logger.info(_npc1, _npc2)>> <<run lib.logger.info(_temp)>> <<run lib.logger.closeGroup()>> <<include "NPCSaveEdits">> <</button>>
<<if $currentPassage.aliases>> <<print lib.firstCharacter($currentPassage.name.slice(0, 1))>><<print lib.createTippyFull('Who is also known as ' + lib.makeList($currentPassage.aliases), $currentPassage.name.slice(1) + '.')>> <<else>> <<print lib.firstCharacter($currentPassage.name)>> <</if>>
<<idp $currentPassage.channelDivinity>> <<if $currentPassage.domains>> Clerics of $currentPassage.name are typically of the <<print lib.makeList($currentPassage.domains)>> domain<<if $currentPassage.domains.length > 1>>s<</if>>. <</if>>
<<nobr>> <<if $currentPassage.relationships.length > 0>> <h2>Relationships</h2> <table> <tr> <th>Person</th> <th>Relationship</th> </tr> <<for _relationship range $currentPassage.relationships>> <<set _deity to lib.findInArray(lib.getPantheonDeities($town, State.metadata.get('pantheon')), 'name', _relationship.name.toUpperFirst())>> <<capture _deity, _relationship>> <tr> <td> <<if _deity>> <<profile _deity _deity.toUpperFirst()>> <<else>> <<print _relationship.name.toUpperFirst()>> <</if>> </td> <td colspan="4"><<if _relationship.description>> <<print lib.createTippyFull(_relationship.description.toUpperFirst(), _relationship.relationship.toUpperFirst())>> <<else>><<print lib.toTitleCase(_relationship.relationship)>><</if>></td> </tr> <</capture>> <</for>> </table> <</if>> <</nobr>>
<<set _religionPercentageContent to lib.getAllPantheonPercentages($town, State.metadata.get('pantheon'))[$currentPassage.key].toFixed(2)>> <h2>Followers</h2> <<set _followers to Object.fromEntries(Object.entries($npcs).filter(([key, npc]) => npc.religion.deity === $currentPassage.name))>> <<include "Maxim">> $currentPassage.name makes up <<include "ReligionPercentageList">>% of worshipped deities in [[$town.name|TownOutput]]. <<idp $currentPassage.followers.description>> <<idp $currentPassage.followers.adherents "<<print lib.makeList($currentPassage.followers.adherents)>>">> <<if $currentPassage.followers.favouredWeapon>> In combat, <<print lib.genderData[$currentPassage.gender].hisher>> followers favour the $currentPassage.followers.favouredWeapon.<</if>> /* <<if $currentPassage.followers.holyDays['earth'].length > 0>><h3>Holy Days</h3><<print lib.printInformation($currentPassage.followers.holyDays['earth'])>><</if>> */ <<idp $currentPassage.followers.excluded.description "<h3>Excluded</h3>$currentPassage.followers.excluded.description">> <<include "ClericDomains">> <<if Object.keys(_followers).length > 0>> <div class='classTable'> <h3>NPCs</h3> <table> <tr> <th>Name</th> <th>Race</th> <th>Occupation</th> <th>Strength</th> <<if $currentPassage.deleteEnabled>><th>Delete</th><</if>> </tr> <<for _key, _npc range _followers>> <<set _profession to lib.findProfession($town, $npcs[_npc.key])>> <tr> <td><<profile $npcs[_npc.key]>></td> <td><<print $npcs[_npc.key].race.toUpperFirst()>></td> <td><<print lib.createTippyFull(_profession.description.toUpperFirst(), $npcs[_npc.key].profession.toUpperFirst())>></td> <td><<print lib.createTippyFull(lib.getReligiosityDescription($town, $npcs[_npc.key]), lib.articles.output($npcs[_npc.key].religion.strength))>></td> <<if $currentPassage.deleteEnabled>><td><<link "x">><<set _deleted to $npcs[_npc.key]>><<run setup.deleteNPC($npcs[_npc.key])>><<notify 5000>>Deleted _deleted.name<</notify>><<update>><</link>></td><</if>> </tr> <</for>> </table> </div> <</if>>
<<if $currentPassage.associations.animals.length > 0 || $currentPassage.associations.plants.length > 0 || $currentPassage.associations.monsters.length > 0 >><h2>Manifestations</h2> <<idp $currentPassage.associations.avatars>> <<if Object.keys($currentPassage.associations).length >= 1>> <table> <tr> <th>Type</th> <th>Appears as</th> </tr> <<for _key, _array range $currentPassage.associations>> <<if _key === 'avatars'>><<continue>><</if>> <<if $currentPassage.associations[_key].length === 0>><<continue>><</if>> <tr> <td><<print _key.toUpperFirst()>></td> <td colspan="4"><<print lib.makeList($currentPassage.associations[_key]).toUpperFirst()>></td> </tr> <</for>> </table> <</if>> <</if>>
<<if $currentPassage.maxim>><<set _rand to $currentPassage.maxim.random()>> <blockquote><<if _rand.title>><h4>_rand.title</h4><</if>>_rand.description<<idp _rand.author "-- _rand.author">></blockquote> <</if>>
<<if $currentPassage.portfolios>> <<if $currentPassage.portfolios.length <= 2>> <<print lib.makeList($currentPassage.portfolios)>> <<else>> <<for _portfolio range $currentPassage.portfolios.slice(1, 3)>> _portfolio, <</for>> and <<if $currentPassage.portfolios.length > 4>> <<set _temp to 'As well as ' + lib.makeList($currentPassage.portfolios.slice(5, -1), {noAnd: true}) + ' and ' + $currentPassage.portfolios[$currentPassage.portfolios.length - 1] + '.'>> <<print lib.createTippyFull(_temp, $currentPassage.portfolios[4])>> <<else>> <<print $currentPassage.portfolios[4]>> <</if>> <</if>> <</if>>
<<if $currentPassage.possessions.length > 0>><h3>Possessions</h3><<print lib.printInformation($currentPassage.possessions)>><</if>>
<<if $currentPassage.blessings || $currentPassage.blessings || $currentPassage.curses>><h2>Powers</h2><</if>> <<idp $currentPassage.powers>> <<idp $currentPassage.blessings>> <<idp $currentPassage.curses>>
<<if $currentPassage.quotes>><<set _rand to $currentPassage.quotes.random()>> <blockquote>_rand.description<<idp _rand.author "-- _rand.author">></blockquote> <</if>>
<<print lib.genderData[$currentPassage.gender].hisher.toUpperFirst()>> symbol <<print lib.deityIsWas($currentPassage.status)>> \<<if typeof $currentPassage.symbol === 'array'>><<print lib.articles.find($currentPassage.symbol[0])>> <<print lib.createTippyFull('As well as ' + lib.makeList($currentPassage.symbol.slice(1)), $currentPassage.symbol[0])>> \<<else>><<print lib.articles.output($currentPassage.symbol)>><</if>>.
<<set $currentPassage to lib.findInArray(lib.getPantheonDeities($town, State.metadata.get('pantheon')), 'name', $currentPassage.name)>> <<if recall('customImages.deity.' + $currentPassage.key) || $currentPassage.customImage>> <div class="illustration-buffer"> <<print lib.getCustomImage(recall('customImages.deity.' + $currentPassage.key).outerHTML || $currentPassage.customImage)>></div><img id="paper" src=./static/grunge-top-01.png top=15vw display=block> <</if>> <button type='button' title='Coming to an update near you soon!' data-tippy-content='Coming to an update near you soon!' id="deity-edit" disabled>Edit $currentPassage.name</button> <h1 class="interactive-only">$currentPassage.name</h1><span @id="$currentPassage.key"></span> /* <<button "Edit $currentPassage.name" DeitySliders>> <<run setup.history($currentPassage, "DeitySliders", "Editing " + $currentPassage.name)>> <</button>> */ <<include "Quotes">> /* <<include "AlsoKnownAs">> */ <<if $currentPassage.aliases && $currentPassage.aliases.length > 0>> <<print lib.createTippyFull('Who is also known as ' + lib.makeList($currentPassage.aliases) + '.', $currentPassage.name)>> <<else>> $currentPassage.name <</if>> , <<print lib.createTippyFull(lib.makeList($currentPassage.titles.slice(1)), $currentPassage.titles[0])>>, <<print lib.deityIsWas($currentPassage.status)>> the <<print ($currentPassage.wordNoun || lib.genderData[$currentPassage.gender].godgoddess)>> of <<include "Portfolios">>. <<print lib.deityStatus($currentPassage)>> <<include "Symbol">> <<idp $currentPassage.description>> <<idp $currentPassage.appearance>> <<if $currentPassage.realm>>$currentPassage.name resides in $currentPassage.realm<</if>> <<if $currentPassage.rank>><<print lib.genderData[$currentPassage.gender].heshe.toUpperFirst()>> is <<print lib.articles.output($currentPassage.rank)>> in the pantheon.<</if>> <<if $currentPassage.alignment>><<print lib.genderData[$currentPassage.gender].heshe.toUpperFirst()>> is $currentPassage.alignment.<</if>> <<idp $currentPassage.history>> <<include "Powers">> <<idp $currentPassage.possessions>> <<include "Followers">> <<idp $currentPassage.beliefs>> <<idp $currentPassage.heresies>> <<idp $currentPassage.paragraphs>> <<idp $currentPassage.combat>> <<include "Manifestations">> <<include "DeityRelationships">> <<idp $currentPassage.allies>> <<idp $currentPassage.enemies>>
Under construction.
<<run $("body").on("change", ["button", 'input'], function() { $(document).trigger(":liveupdate"); });>> <<lb>><span class='flex-line'><<if State.metadata.get('patreonPass') === $_>><<set _isPatron to true>><<import '$town.religion._customPantheon' 'json' 'Import Custom Pantheon'>><<else>><<set _isPatron to false>><<if recall('seenReligionThanks') !== true>><<button "Import">><<run memorize('seenReligionThanks', true)>><<run setup.openDialog({ header: 'A Brief Message', passage: 'PatreonThanks', rerender: true })>><</button>><<else>><<import '$town.religion._customPantheon' 'json' 'Import Custom Pantheon'>><</if>><</if>> <<listbox "$town.religion.pantheon" autoselect>><<optionsfrom lib.getPantheonNames($town, State.metadata.get('pantheon'))>><</listbox>> <<if def $town.religion._customPantheon or State.metadata.has('pantheon')>> <<run State.metadata.set('pantheon', $town.religion._customPantheon)>> <<button "Delete custom pantheon">> <<if lib.isUsingCustomPantheon($town, State.metadata.get('pantheon'))>> <<set $town.religion.pantheon to 'greek'>> <</if>> <<set $town.religion._customPantheon to {}>><<update>> <<run State.metadata.delete('pantheon')>> <</button>> <</if>><<button "Refresh">><<update>><</button>></span> <ol> <<if _isPatron === false>><li>Join the Patreon at the Hero or higher tier [[here|https://www.patreon.com/join/eigengrausgenerator?]]</li> <li>Find the code in the [[welcome note|https://www.patreon.com/eigengrausgenerator/membership]]</li> <li>Enter it <<link "here">><<run setup.openDialog({ header: 'Patreon Only', passage: 'ImportPatreon', rerender: true })>><</link>></li> <</if>> <li>Import custom pantheon as a valid {{{.json}}} file. You can access a template of the pantheon {{{.json}}} file online, or in our Discord server.</li> <li>Select desired pantheon from the drop-down menu.</li> </ol> <hr> <<if lib.isUsingCustomPantheon($town, State.metadata.get('pantheon')) === false>><details style="height:270px; overflow:auto"><summary>Current Pantheon Data for <b><<print lib.getPantheon($town, State.metadata.get('pantheon')).name>></b> loaded:</summary> <<print JSON.stringify(lib.getPantheon($town, State.metadata.get('pantheon')))>> </details> <hr><</if>> Custom patheon name: <<if lib.seeIfCustomPantheonExists($town, State.metadata.get('pantheon'))>><b><<print lib.getCustomPantheon($town, State.metadata.get('pantheon')).name>></b> <details style="height:90%; overflow:auto"><summary>Custom Pantheon Data:</summary> <<print JSON.stringify(lib.getCustomPantheon($town, State.metadata.get('pantheon')))>> </details> <<else>>none currently loaded<</if>> <</lb>>
/* <<set $currentPassage to lib.findInContainer($town.roads, "key", $currentPassage.key)>> */ <<include "PrintImage">> <h1 class="interactive-only">$currentPassage.name</h1><span @id="$currentPassage.key"></span> <p>$currentPassage.description</p> <<liveblock>><span class="interactive-only"><label name="Select new building type" for="listbox-newbuilding"><<listbox "$newBuilding">><<optionsfrom Object.keys(setup.createBuildingKeys).filter(building => { const nsfwItems = ['Brothel']; if (!settings.disableNSFW) return building; return !building.includes(nsfwItems); })>> <</listbox>></label> -- <<button "Create New Building">> <<run lib.logger.info('Creating a new ' + $newBuilding)>> <<set _latestBuilding to setup.createNewBuilding($town, $newBuilding, { road: $currentPassage.key })>> <<update>> <<run $('.' + _latestBuilding.key).parent().addClass('highlight')>> <</button>> </span> <<include "RoadTables">><</liveblock>>
<table> <tr> <th>Name</th> <th>Type</th> <th>Associated NPC</th> <th class="interactive-only">Delete</th> </tr> <<for _key, _throw range $town.roads[$currentPassage.key].inhabitants.buildings>><<set _building to lib.findInArray($town.buildings, 'key', _key)>><<capture _key, _building>> <<if _building>><tr><td><<profile _building>></td> <td><<print lib.toTitleCase(_building.wordNoun || _building.type)>></td> <td><<if _building.associatedNPC>><<profile _building.associatedNPC>><</if>></td> <td class="interactive-only"><<link "x">><<run setup.deleteBuilding($town, _building)>><<update>><</link>></td> </tr><</if>> <</capture>><<set _previousBuilding to _building>> <</for>> <<if Object.keys($town.roads[$currentPassage.key].inhabitants.npcs).length > 0>> <tr> <th>Name</th> <th>Race</th> <th>Profession</th> /* <th>Relationship</th> */ <th class="interactive-only">Delete</th> </tr> <<for _npcKey, _npcRelationship range $town.roads[$currentPassage.key].inhabitants.npcs>> <<set _profession to lib.toTitleCase($npcs[_npcKey].profession)>> <tr> <td><<profile _npcKey>></td> <td><<print $npcs[_npcKey].race.toUpperFirst()>></td> <td><<print lib.createTippyFull(lib.professions[$npcs[_npcKey].profession].description.toUpperFirst(), _profession)>></td> /* <td><<print _npcRelationship.toUpperFirst()>></td> */ <td class="interactive-only"><<link "x">><<run setup.deleteNPC($town, $npcs[_npcKey])>><<update>><</link>></td> </tr> <</for>> <</if>></table>
<<nosp>><<liveblock>> <nav> <span id='town-permalink'><<link "The $town.type of $town.name">><<if $town.generated === 'biome'>><<set $town to setup.createTown($town)>><</if>><<unset $currentPassage>><<unset $currentNPC>><<set $history to []>><<run history.pushState('', document.title, window.location.pathname + window.location.search)>><<goto "Start">><</link>></span> </nav> <<for _i, _passage range $history>><<set _link to lib.toTitleCase(_passage.linkDescription)>><<capture _i, _passage, _link>><<if _i < $history.length - 1>><nav class="breadcrumb-link"><<link _link _passage.passageName>><<set $currentPassage to _passage>><<run $history.length = _i>><<run setup.history(_passage, _passage.passageName, _passage.linkDescription)>><</link>></nav><</if>><</capture>><</for>><<if def $history.last()>><nav class="breadcrumb-link last"><<print lib.toTitleCase($history.last().linkDescription)>></nav><</if>> <</liveblock>><</nosp>>
<<set _objectTypes to { 'npc': 'npc', 'deity': 'deity', 'specific building': 'building', 'building type': 'buildingType', 'specific faction': 'faction', 'faction type': 'factionType', 'road': 'road', 'town': 'town' }>> <<set $customImagesData to Object.assign({ 'deity': {}, 'buildingType': {}, 'factionType': {} }, State.metadata.get('customImages'))>> <<button "Edit Existing Images" ShowAllSaved>> <</button>> <<button "Refresh">> <<update>> <</button>> <label name="Select building type" for="listbox-target"> <<listbox "$targetType">> <<optionsfrom _objectTypes>> <</listbox>> </label> /* <<run $("body").on("change", ['#listbox-target', '#listbox-targettype'], function() { $(document).trigger(":liveupdate"); });>> */ <<liveblock>> <<switch $targetType>> <<case 'buildingType'>> <label name="Select building type" for="listbox-target"> <<listbox "$target" autoselect>><<optionsfrom setup.buildingTypes>><</listbox>> </label> <<case 'building'>> <label name="Select specific building" for="listbox-target"> <<listbox "$target" autoselect>> <<optionsfrom Object.values($town.buildings).reduce( (obj, building) => Object.assign(obj, { [building.name]: building.key }), {})>> <</listbox>> </label> <<case 'road'>> <label name="Select road" for="listbox-target"> <<listbox "$target" autoselect>> <<optionsfrom Object.values($town.roads).reduce( (obj, road) => Object.assign(obj, { [road.name]: road.key }), {})>> <</listbox>> </label> <<case 'deity'>> <label name="Select deity" for="listbox-target"> <<listbox "$target" autoselect>> <<optionsfrom lib.getPantheonDeities($town, State.metadata.get('pantheon')).reduce( (obj, deity) => Object.assign(obj, { [deity.name]: deity.key }), {})>> <</listbox>> </label> <<case 'npc'>> <label name="Select npc" for="listbox-target"> <<listbox "$target" autoselect>> <<optionsfrom Object.values($npcs).reduce( (obj, npc) => Object.assign(obj, { [npc.name]: npc.key }), {})>> <</listbox>> </label> <<case 'faction'>> <label name="Select faction" for="listbox-target"> <<listbox "$target" autoselect>> <<optionsfrom Object.values($town.factions).reduce( (obj, faction) => Object.assign(obj, { [faction.name]: faction.key }), {})>> <</listbox>> </label> <<case 'factionType'>> <label name="Select faction type" for="listbox-target"> <<listbox "$target" autoselect>> <<optionsfrom Object.keys(lib.factionData.types)>> <</listbox>> </label> <<case 'town'>>the $town.type of $town.name <<default>>Select a type <</switch>> <<switch $targetType>> <<case "npc">> <<set _temp to $npcs[$target].customImage || 'url goes here'>> <<case "building">> <<set _temp to $town.buildings.find(building => building.key === $target).customImage || 'url goes here'>> <<case "faction">> <<set _temp to $town.factions[$target].customImage || 'url goes here'>> <<case "road">> <<set _temp to $town.roads[$target].customImage || 'url goes here'>> <<case "town">> <<set _temp to $town.customImage || 'url goes here'>> <<default>> <<set _temp to $customImagesData[$targetType][$target] || 'url goes here'>> <</switch>> -- <<textbox "$url" _temp>> -- <<button "Save">> <<set _outputImage to $url>> <<switch $targetType>> <<case "npc">> <<set $npcs[$target].customImage to $url>> <<case "building">> <<set $town.buildings.find(building => building.key === $target).customImage to $url>> <<case "faction">> <<set $town.factions[$target].customImage to $url>> <<case "road">> <<set $town.roads[$target].customImage to $url>> <<case "town">> <<set $town.customImage to $url>> <<default>> <<set $customImagesData[$targetType][$target] to $url>> <</switch>> <<run State.metadata.set('customImages', $customImagesData)>> <<update>> <</button>> <br> Great sources include:<ul> <li>[[ArtBreeder|https://www.artbreeder.com/create]]</li> <li>[[This gallery of watercolour concept art|https://imgur.com/gallery/5WcP9pn]]</li> </ul> <br> Loaded URL: <<if _outputImage>>_outputImage<</if>> <br><</liveblock>> <br> <<include "ShowAllSaved">>
<<set $customImages to recall('customImages')>> <<set $currentImageElement to null>> <<set $currentImage to $currentPassage.customImage || /* $customImages[$currentPassage.objectType][$currentPassage.key] || */ $customImages.buildingType[$currentPassage.buildingType] || $customImages.factionType[$currentPassage.factionType] || null>> <<if $currentImage>> <<set $currentImageElement to lib.getCustomImage($currentImage)>> <<print $currentImageElement.outerHTML>> /* Resort to the fallback local images */ <<elseif $currentPassage.localImage>> <<set $currentImageElement to lib.getLocalImage($currentPassage.localImage)>> <<print $currentImageElement.outerHTML>> <</if>> /* This handles which covering image to use. */ <<if $currentImageElement && $currentImageElement.classList.contains('landscape')>> <div class="illustration-buffer" /> <img class="paper vertical-offset" src="./static/grunge-top-01.png" top='15vh'> <<elseif $currentImageElement>> <div class="illustration-buffer float-right" /> <img class="paper" src="./static/grunge-middle-02.png" > <</if>>
<details><summary>Saved Images</summary> <<liveblock>><<run lib.logger.info(State.metadata.get('customImages'))>> <<set _temp to State.metadata.get('customImages')>> <<button "Delete all">> <<run State.metadata.delete('customImages')>> <<set $customImagesData to { 'deity': {}, 'buildingType': {}, 'factionType': {} }>> <<run State.metadata.set('customImages', $customImagesData)>> <<update>> <</button>> (NPCs and other specifics are flushed on restart) <table> <tr> <th>Object</th> <th>Name</th> <th>File</th> <th>Delete</th> </tr> <<for _typeKey, _type range _temp>><<capture _typeKey, _type>> <<if Object.keys(_type).length > 0>><<set _length to Object.keys(_type).length + 1>> <tr> <th @rowspan=_length>_typeKey</th> </tr> <<for _itemKey, _item range _type>><<capture _itemKey, _item>> <tr> <td>_itemKey</td> <td>_item</td> <td><<link 'x'>><<run delete _temp[_typeKey][_itemKey]>><<run State.metadata.set('customImages', _temp)>><<update>> <</link>> </td> </tr><</capture>> <</for>> <</if>><</capture>> <</for>> </table> <</liveblock>></details>
<footer class="footer"><<link "Credits" Credits>><<set $history.length to 0>><<run setup.history({passageName: "Credits", linkDescription: "Credits", name: "Credits"})>><</link>> <<link "Legal" Legal>><<set $history.length to 0>><<run setup.history({passageName: "Legal", linkDescription: "Legal", name: "Legal"})>><</link>> [[Patreon|https://patreon.com/eigengrausgenerator/join]] [[GitHub|https://github.com/ryceg/Eigengrau-s-Essential-Establishment-Generator]] [[Press Kit|https://priceless-darwin-158c20.netlify.app/press-kit/]] <<link "Newsletter">><<run setup.openDialog({ header: 'Sign up to the newsletter to be notified about updates', passage: 'EmailSignUp' })>><</link>></footer> <<if !State.metadata.has('cookiePopupWasShown')>> <<include "CookieConsent">> <<run State.metadata.set('cookiePopupWasShown', true)>> <</if>>
<header id="passage-header"><span id="passage-header-nav"><<include "Breadcrumb">></span><span class='restart' id='new-town' data-tippy-content="Discard the current town and generate a new town!"> <<link "Roll a new town">> <<run UI.restart()>> <</link>></span></header> <<done>><<run tippy('.restart')>><</done>>
Created by <span class='tip .sidebarAds' data-tippy-content="Please send complaints to 5 Adelaide Ave, Deakin ACT 2600"><a style='color:var(--text-normal)' href="https://reddit.com/u/rcgy">/u/rcgy</a></span> <span class="version">v[[setup.data.versionNumber|https://github.com/ryceg/Eigengrau-s-Essential-Establishment-Generator/blob/master/CHANGELOG.md]]</span> <<nobr>><<if settings.hideAds !== true>> <span class="tip sidebarAds" data-tippy-content="Thank you for your continued support!" id="patreon"> <<print lib.createBadge(lib.badges.stats.patreonSupporters, {imgArgs: 'style=width:100%'})>> /* , imgArgs: "style='width:100%'" */ </span> <span class="tip sidebarAds" data-tippy-content="Come join our community!" id='discord'> <<print lib.createBadge(lib.badges.stats.discordOnline, {imgArgs: 'style=width:100%'})>> </span> <span class="tip sidebarAds" data-tippy-content="Check out the repository!" id='github'> <<print setup.data.badges.github>> </span> <span id="fun-container"><<print setup.data.badges.fun>></span> <<run tippy('.sidebarAds')>> <</if>><</nobr>>
<<link "Toolbox" Toolbox>><<set $history.length to 0>><<run setup.history({passageName: "Toolbox", linkDescription: "Toolbox", name: "Toolbox"})>><</link>> <<link "Export to Foundry or GMBinder" OutputEverything>><<set $history.length to 0>><<run setup.history({passageName: "OutputEverything", linkDescription: "Export", name: "Export"})>><</link>> [[Submit a bug or suggestion|https://github.com/ryceg/Eigengrau-s-Essential-Establishment-Generator/issues/new/choose]]
<a href="https://twitter.com/intent/tweet?url=www.eigengrausgenerator.com&text=Check out Eigengrau's Generator, it's a tabletop role play town generator unlike any other! https://eigengrausgenerator.com/&via=rhyscgray&hashtags=dnd,rpg,ttrpg,opensource,dnd5e,procgen">Twitter</a> <a href="https://www.facebook.com/sharer/sharer.php?u=www.eigengrausgenerator.com&src=sdkpreparse&display=popup">Facebook</a> <a href="https://www.reddit.com/submit?url=https://eigengrausgenerator.com&title=Eigengraus%20Generator:%20A%20Generator%20Unlike%20Any%20Other">Reddit</a>
<<if State.variables._ === $tovvn>><<set setup.data.isPatron to true>><<run State.metadata.set('patreonPass', $tovvn)>><<notify 5000>>Patreon features unlocked! Thank you :)<</notify>><</if>>
<section id="brief-description"> <h1 class='town-name' @data-town-type=$town.type @data-town-name=$town.name> The <span @data-town-type=$town.type> <<print $town.type.toUpperFirst()>> </span> of <span @data-town-name=$town.name> $town.name </span> </h1> <p role="main"> <span @data-town-type=$town.type @data-town-name=$town.name> $town.name </span> is <<print lib.articles.output(lib.createTippyFull("With a population of " + $town.population + ".", $town.type))>> located <<print lib.terrain[$town.terrain].location[$town.location].preposition>> the $town.terrain $town.location, where the vegetation is $town.vegetation. $town.name grew around $town.origin, and is comprised <<include "RacesPercentageList">>. They are <<if $town.ignoreGender is false>> <<print lib.articles.find($town.equality)>> <<print lib.createTippyFull($town.equalityDescription, $town.equality)>> <<else>> <<print lib.articles.find($town.economicIdeologyIST)>> <</if>> /* this gets the article, not the economic ideology! */ <<print lib.createTippyFull(setup.politicsDescription($town, "economicIdeology"), $town.economicIdeologyIST)>> <<print lib.createTippyFull(setup.politicsDescription($town, "politicalIdeology"), $town.politicalIdeologyIC)>> <<print lib.createTippyFull(setup.politicsDescription($town, "politicalSource"), $town.politicalSource)>> that <<include "ReligionPercentageList">>.</p></section>
/* This is currently not being used */ <<for _key, _building range $town.buildings>><<capture _key, _building>> <p @id="_building.name"><<set _road to $town.roads[_building.road]>> <<if def _previousBuilding && _previousBuilding.road === _building.road>> <br> <<print ["Also on _road.name is ", "Down from _previousBuilding.name is ", "Nearby is "].random()>> <<else>> <<print ["On ", "Along ", "Over on "].random()>> <<profile $town.roads[_road.key]>> is <</if>> <<if _building.needsWordNoun !== false>> the _building.wordNoun <</if>> <<profile _building>></p> <</capture>><<set _previousBuilding to _building>> <</for>>
<span class="interactive-only hide-on-print"><label name="Select new building type" for="listbox-newbuilding"><<listbox "$newBuilding">><<optionsfrom Object.keys(setup.createBuildingKeys).filter(building => { const nsfwItems = ['Brothel']; if (!settings.disableNSFW) return building; return !building.includes(nsfwItems); })>> <</listbox>></label> -- <<button "Create New Building">> <<run lib.logger.info('Creating a new ' + $newBuilding)>> <<set _latestBuilding to setup.createNewBuilding($town, $newBuilding)>> <<run lib.logger.info(_latestBuilding)>> <<if document.getElementById("buildings") isnot null>> <<replace "#buildings">><<include "RoadsList">> <</replace>> <</if>> <<run $('.' + _latestBuilding.key).parent().addClass('highlight')>> /* State.create() is an undocumented feature that adds a Moment (the current state) to the story's History. Notably, it may not play nicely with the Back and Forward navigation. This has been implemented to ensure that the creation of new buildings is saved and no data is lost. */ <<run State.create(passage())>> <<update>> <</button>></span>
<span style='display:flex'><<liveblock>> <<link "reroll">> <<set $town.name to lib.createTownName($town)>><<update>> <</link>> -- <label class="auto-update" name="Town name"><<textbox "$town.name" $town.name>></label> <</liveblock>></span> <hr> <<include "BiomeGenerationRefresh">> <hr> <<include "EditSliders">> <hr> <<include "EditRaces">> <<button "Save Changes" Start>><<set $town to setup.createTown($town)>> <</button>>
<<run $('body').on('change', 'select', () => { let { terrain, location } = State.variables.town; if (!lib.terrain?.[terrain]?.location?.[location]?.origin) { State.variables.town.location = Object.keys(lib.terrain[terrain].location).random(); $(document).trigger({ type: ':notify', message: 'There are no origins for a ' + terrain + ' ' + location + '.', time: false, classes: false }); location = State.variables.town.location; State.variables.town.origin = Object.keys(lib.terrain[terrain].location[location].origin).random(); } State.variables.listboxes = { terrain: Object.keys(lib.terrain), location: Object.keys(lib.terrain[terrain].location), vegetation: ['thick', 'lush', 'sparse', 'desolate'], origin: lib.terrain[terrain].location[location].origin, primaryCrop: lib.townData.misc.primaryCrop, primaryExport: lib.townData.misc.primaryExport, season: Object.keys(lib.terrain.temperate.weather.season), economicIdeology: lib.economicPairs, politicalIdeology: lib.politicalIdeologyPairs, politicalSource: ['absolute monarchy', 'constitutional monarchy', 'republic', 'anarchy'] }; $(document).trigger(':liveupdate'); })>> <<liveblock>><details open><summary>Basics</summary><<include "ListboxOptions">><span class="auto-update"> <<print lib.firstCharacter($town.name)>> is located in the <label name="Terrain" for="listbox-townterrain"> <<listbox "$town.terrain" autoselect>> <<optionsfrom $listboxes.terrain>> <</listbox>> </label> <label class="tip" data-tippy-content="This changes based on the terrain- refresh to update" name="Location" for="listbox-townlocation"> <<listbox "$town.location" autoselect>> <<optionsfrom $listboxes.location>> <</listbox>> </label> , where the vegetation is <label name="Vegetation" for="listbox-townvegetation"> <<listbox "$town.vegetation" autoselect>> <<optionsfrom $listboxes.vegetation>> <</listbox>> </label>. $town.name grew around <label class="tip" data-tippy-content="This changes based on the terrain and location- refresh to update" name="Origin of town" for="listbox-townorigin"> <<listbox "$town.origin" autoselect>> <<optionsfrom $listboxes.origin>> <</listbox>> </label>. They harvest <label name="Primary Crop" for="listbox-townprimarycrop"> <<listbox "$town.primaryCrop" autoselect>> <<optionsfrom $listboxes.primaryCrop>> <</listbox>> </label> , and nearby is a region rich with <label name="Primary Export" for="listbox-townprimaryexport"> <<listbox "$town.primaryExport" autoselect>> <<optionsfrom $listboxes.primaryExport>> <</listbox>> </label>. It is currently <label name="Currentseason" for="listbox-towncurrentseason"> <<listbox "$town.currentSeason" autoselect>> <<optionsfrom $listboxes.season>> <</listbox>> </label>. Politically, they are a <<include "EditSociopolitics">></span></details><<done>><<run tippy('[data-tippy-content]')>><</done>><</liveblock>>
<<set $listboxes to { terrain: Object.keys(lib.terrain), location: Object.keys(lib.terrain[$town.terrain].location), vegetation: ["thick", "lush", "sparse", "desolate"], origin: lib.terrain[$town.terrain].location[$town.location].origin, primaryCrop: lib.townData.misc.primaryCrop, primaryExport: lib.townData.misc.primaryExport, season: Object.keys(lib.terrain.temperate.weather.season), economicIdeology: lib.economicPairs, politicalIdeology: lib.politicalIdeologyPairs, politicalSource: ["absolute monarchy", "constitutional monarchy", "republic", "anarchy"] }>> <<set $town.roll.wealth to $town.roll.wealth.clamp(1, 100), $town.roll.economics to $town.roll.economics.clamp(1, 100), $town.roll.welfare to $town.roll.welfare.clamp(1, 100), $town.roll.military to $town.roll.military.clamp(1, 100), $town.roll.arcana to $town.roll.arcana.clamp(1, 100), $town.roll.law to $town.roll.law.clamp(1, 100), $town.roll.equality to $town.roll.equality.clamp(1, 100) >>
<<if _exportType is 'novelai'>><<print lib.getPredominantRaceFromBase($town.baseDemographics).amountDescriptive>> <<else>><span class='tippy-races-percentage'></span><<done>><<run setup.createRaceHTML($town.baseDemographics, 'tippy-races-percentage')>><</done>><</if>>
<<nosp>> <<if _exportType is 'novelai'>><<print lib.getPredominantReligion($town, lib.getPantheonPercentages($town, State.metadata.get('pantheon'))).amountDescriptive>> <<else>><span class='tippy-religion-percentage'></span> <<done>> <<run setup.createReligionHTML( lib.getPantheonPercentages($town, State.metadata.get('pantheon')), 'tippy-religion-percentage', _religionPercentageContent )>> <</done>><</if>><</nosp>>
<<for _roadKey, _road range $town.roads>> <<capture _roadKey, _road>> <<for _buildingKey, _obj range _road.inhabitants.buildings>> <<set _building to lib.findInArray($town.buildings, "key", _buildingKey)>> <<capture _buildingKey, _building>> <<if !Object.keys(_road.inhabitants.buildings).length>><<continue>><</if>> <<if !_building>><<continue>><</if>> <<if _building.objectType === 'room' || _building.parentKey>><<continue>><</if>> <p role="listitem"> <<if def _previousBuilding && _previousBuilding.road === _roadKey>> <<print ["Also on _road.name is ", "Down from _previousBuilding.name is ", "Nearby is "].random()>> <<elseif _road.precedingText>> <br> _road.precedingText <<else>> <br> <<print ["On ", "Along ", "Over on "].random()>> <<profile $town.roads[_roadKey]>> <</if>> <<if _building.needsWordNoun>> the _building.wordNoun <</if>> <<profile _building>> </p> <<set _previousBuilding to _building>> <</capture>> <</for>> <</capture>> <</for>>
<<run window.history.pushState({ key: undefined, objectType: undefined, passageName: "Start", linkDescription: $town.name || 'Home' }, 'Start')>> <<set $currentPassage to $town>><<if !_isTextOutput>><div id="illustration-banner" class='hide-on-print'><img src="./static/banner.svg"></div><<include "PrintImage">><</if>> <<include "BriefDescription">> <span id='detailed-description' class="tip hide-on-print" tabindex="0" data-tippy-content="Find the overview of the town and its sociopolitical structure here!"><<link "Detailed description of $town.name" TownOutput>><<set $currentPassage to $town>><<run setup.history({passageName: "TownOutput", linkDescription: $town.name, name: $town.name})>><</link>></span> <br> <<include "CreateBuilding">> -- <<button "Edit the $town.type" TownEdit>><</button>> <span id="buildings" role="list"><<include "RoadsList">></span> <<run lib.logger.info($town)>> <<if !_isTextOutput>><section id='quick-scenario-generator'><h3 class='hide-on-print'>Quick Scenario Generator</h3><<include "CreateScenario">></section><</if>> <<include "Popup">><<done>><<run tippy('#detailed-description')>><</done>> <<if State.metadata.get('cookiePopupWasShown') !== true && settings.disableAnalytics !== true>> <<run setup.openDialog({ header: `Cookies`, passage: 'CookieConsent', rerender: true })>> <<elseif State.metadata.get("neverShowWelcome") !== true && setup.data.hasSeenWelcome !== true>> <<run setup.openDialog({ header: `Welcome`, passage: 'Welcome' })>> <</if>>
<<run lib.logger.info("Hello world!")>> <<run setup.urlSeed()>> <<run setup.init()>> <<set setup.data to { versionNumber: "2.9", badges: { fun: lib.createFunBadge(), github: lib.createBadge([ lib.badges.stats.githubForks, lib.badges.stats.githubStars, lib.badges.stats.monthlyActivity, lib.badges.stats.timeSinceLastCommit, lib.badges.stats.linesOfCode].random(), {imgArgs: 'style=width:100%', link: 'https://github.com/ryceg/Eigengrau-s-Essential-Establishment-Generator/' }) } }>> <<if !State.metadata.has('customImages')>> <<set $customImagesData to { 'deity': {}, 'buildingType': {}, 'factionType': {} }>> <<run State.metadata.set('customImages', $customImagesData)>> <</if>> <<set Config.history.maxStates to 15>> <<set $npcs to {}>> <<set $toolbox to {}>> <<set $history to []>> <<set $town to setup.createTown()>> <<if recall("neverShowWelcome") is true && recall("showBiomeGeneration") is true>> <<set Config.passages.start = "BiomeGeneration">> <<else>> <<set Config.passages.start = "Start">> <</if>> <<set _t to lib.books.normal>><<set $_ to ''>> <<for _i = 0; _i < 16; _i++>><<set State.variables._ += _t[_i][7] !== ' ' ? _t[_i][7] : '$'>><</for>> <<if lib.isLocalCopy()>><<set settings.disableAnalytics to true>><<run memorize('patreonPass', $_)>><<notify 10000>>Patreon features are unlocked since you're running locally.<</notify>> <<elseif location.hash.includes('npt')>><<run memorize('patreonPass', $_)>><<set settings.hideAds to true>> <</if>> <<run lib.shareMenu()>> <<run setup.navigateToObj()>>
<p> Eigengrau's Generator procedurally generates towns that are ready out-of-the-box to read to players, complete with sociopolitics, descriptions, and those little touches of creativity that separate a hand-crafted tavern from the drudgery of improvising your umpteenth tavern on the spot. </p> <br> <p><<fadein 1s 2s>> This generator is different because it generates text that you can read out to players. Instead of "Dirty tavern, with a half orc bartender", you get whole paragraphs of text to immerse your players in the world that you've created. <</fadein>></p> <br> <p><<fadein 1200ms 6s>> It creates towns from the ground up, with the biome impacting types of building material that are available, a town's wealth and population changing what establishments are featured, and sociopolitics and economic modeling influencing the types of people that inhabit the town. <</fadein>></p> <br> <p><<fadein 1400ms 10s>> Eigengrau's Generator is built much like a website, with hyperlinks taking you to different pages. It also has <span class="tip dotted block" tabindex="0" data-tippy-content="Like this!">tooltips</span>, which provide <span class="tip dotted block" tabindex="0" data-tippy-content="Additional DM notes are displayed in tooltips that look like this!">quick additional information</span> that can be <span class="tip dotted description" tabindex="0" id="tutorial-example" data-tippy-content="Stuff that can be read out to players looks like this!">read out to players</span>.<<done>><<run tippy('.block')>><<run tippy('.description', { theme: 'descriptive'})>><</done>> <</fadein>></p> <br> <p><<fadein 1500ms 14s>> If you get lost, you can always navigate via the breadcrumb menu in the top left, which currently reads <b>"The $town.type of $town.name"</b>. That link will take you to the homepage. <</fadein>></p> <br> <p><<fadein 500ms 18s>> Eigengrau's Generator is open source, and totally free to use. If you enjoy using this tool, please consider supporting me on [[Patreon|"https://www.patreon.com/eigengrausgenerator"]], which unlocks exporting to Foundry, custom pantheons, and more! <</fadein>></p> <br> <p><<fadein 500ms 22s>> Also, join us on [[Discord|https://discord.gg/MphCTJY]], and [[Reddit|https://www.reddit.com/r/EigengrausGenerator]]! I don't bite. <</fadein>></p> <br> <p><<fadein 500ms 25s>> <details><summary>A (Brief) List of Features</summary> <ul> <li>Export functionality to [[Foundry, GMBinder and Homebrewery|OutputEverything]]</li> <li>Detailed NPC backstories</li> <li>Cohesive town, generated with interconnected inhabitants</li> <li><span id='detailed-description' class="tip hide-on-print" tabindex="0" data-tippy-content="Find the overview of the town and its sociopolitical structure here!"><<link "Town racial, religion, and social demographic modeling" TownOutput>><<set $currentPassage to $town>><<run setup.history({passageName: "TownOutput", linkDescription: $town.name, name: $town.name})>><</link>></span></li> <li>Many different building types ready to read out to players</li> <li>Custom pantheons (Hero tier Patron feature)</li> </ul> </details> <</fadein>></p> <br> <<fadein 500ms 26s>> <<button "Let's start!" Start>> <<set $town to setup.createTown($town)>> <<run State.metadata.set("hasSeenTutorial", true)>> <</button>> -- <<button "Customise the town first" BiomeGeneration>> <<run State.metadata.set("hasSeenTutorial", true)>> <<run setup.addGtagEvent({ event_category: 'customise', event_action: 'clicked', event_label: 'customise during tutorial' })>> <</button>> -- <span id="remember"> <<button "Please don't show me the tutorial again.">> <<run State.metadata.set("hasSeenTutorial", true)>> <<replace "#remember">>Okay, got it! You can always revisit the tutorial in the settings page.<</replace>> <</button>></span> <</fadein>>
<<set setup.data.hasSeenWelcome to true>> <<if State.metadata.get("hasSeenTutorial") === true>> Welcome! Is this your first time using Eigengrau's Essential Establishment Generator? <<else>> Welcome! It looks like this is your first time using Eigengrau's Essential Establishment Generator- we would suggest that you take the quick tutorial to get the most out of the generator! <</if>> <br> <span class='flex-line'><<button "yes, take me to the tutorial" Tutorial>><<if Dialog.isOpen()>><<run Dialog.close()>><</if>><</button>> <<button "no, let me customise the town first" BiomeGeneration>><<notify 5000>><<include "RevisitTutorialReminder">><</notify>><<run State.metadata.set("hasSeenTutorial", true)>><<if Dialog.isOpen()>><<run Dialog.close()>><</if>><</button>> <span id="remember"> <<button "Never show me this screen again.">> <<run State.metadata.set("hasSeenTutorial", true)>> <<run State.metadata.set("neverShowWelcome", true)>> <<if Dialog.isOpen()>> <<notify 5000>><<include "RevisitTutorialReminder">><</notify>> <<run Dialog.close()>> <<else>> <<replace "#remember">><<include "RevisitTutorialReminder">><</replace>> <</if>> <</button>> </span></span>
Okay, got it! You can always revisit the tutorial in the settings page if you need.
<<linkreplace "<h4>Talk with $associatedNPC.name</h4>" t8n>> <<set _shortage to random(1, 10)>> <<if $building.roll.wealth gt 80>> <<set _shortage += 4>> <<elseif $building.roll.wealth gt 70>> <<set _shortage += 3>> <<elseif $building.roll.wealth gt 60>> <<set _shortage += 2>> <<elseif $building.roll.wealth gt 50>> <<set _shortage += 1>> <<elseif $building.roll.wealth gt 40>> <<set _shortage -= 1>> <<elseif $building.roll.wealth gt 30>> <<set _shortage -= 2>> <<elseif $building.roll.wealth gt 20>> <<set _shortage -= 3>> <<elseif $building.roll.wealth lte 20>> <<set _shortage -= 4>> <</if>> <<if _shortage gt 7, $building.roll.activity gt 70>> <<set _talk to ["We're lucky that I bought that extra case of grog... I get the feeling that we're gonna need it.", "I'm glad that Breiga kept an eye on the stock levels, otherwise we'd have sold out of <<print $building.shortages.random()>>."].random()>> <<elseif _shortage gt 7>> <<set _talk to ["Business is alright, in honesty I think that I prefer running out of supplies than not... At the moment, it looks like I'll not be running out any time soon.", "I think that I may have oversupplied on <<print $building.shortages.random()>>..."].random()>> <<elseif _shortage gt 4>> <<set _talk to ["We've currently got a bit of a shortage of <<print $building.shortages.random()>>, but hopefully we'll be getting a cartload overmorrow.", "We're currently running out of <<print $building.shortages.random()>>, but with any luck, we'll be getting some more before the night is out.", "I don't know how it happened, but we're running out of <<print $building.shortages.random()>>... To be honest, I reckon the barmaid is to blame.", "I swear that I ordered a cartload last week, but I'll be damned if we're not running low on <<print $building.shortages.random()>> somehow.", "I told the barmaid to order some, but wouldn't you know, I find myself low on <<print $building.shortages.random()>>!"].random()>> <<elseif _shortage lte 4, $building.population gt 70>> <<set _talk to ["Things are honestly in a mess at the moment around the $building.name, we've got a full house, and I'm low on <<print $building.shortages.random()>> AND <<print $building.shortages.random()>>! How does that even happen!?", "You can tell that business is booming, but there's only one more case of <<print $building.shortages.random()>>, and we're completely out of <<print $building.shortages.random()>>!"].random()>> <<elseif _shortage lte 4, $building.roll.activity lt 40>> <<set _talk to ["Well, you can probably guess that business isn't the best right at this moment. Maybe the lack of <<print $building.shortages.random()>> has something to do with it...", "Business is slow, I'm not gonna lie. We've had no cartments of <<print $building.shortages.random()>> for almost a week, certainly isn't helping things."].random()>> <<elseif _shortage lte 4>> <<set _talk to "Well, business could be better. We've got a bit of a shortage on <<print $building.shortages.random()>>, and that certainly can't be helping things, but I suppose there's always some bad days; they're just not spending the coin, is all.">> <</if>> <p> <h3>$associatedNPC.name</h3> $associatedNPC.name looks <<print setup.npcData.currentMood.random()>>, but seems pleased with the welcome distraction from the patrons, who $associatedNPC.heshe tells you are for the most part <<print lib.articles.output(lib.getTavernSin($building))>> lot. $associatedNPC.firstName pours $associatedNPC.himherself a drink while you talk with the $associatedNPC.weight $associatedNPC.manwoman about the regular goings on in $town.name, and $building.name, and $associatedNPC.firstName says "_talk" </p> <span class="interactive-only"> <<linkreplace "Talk more with $associatedNPC.name" t8n>> <p> $associatedNPC.firstName tells you that $associatedNPC.heshe <<print ["loves working in $building.name", "hates working in $building.name", "finds working in the $building.wordNoun boring", "finds working in the $building.wordNoun fun"].random()>>. $associatedNPC.firstName says "Before I took over $building.name, I was <<print lib.articles.output($associatedNPC.background)>>. $associatedNPC.backgroundOrigin <<if $associatedNPC.profession isnot "bartender">>Then, I took up adventuring as <<print lib.articles.output($associatedNPC.profession)>>;<</if>> $associatedNPC.professionOrigin </p> <</linkreplace>> </span> <</linkreplace>>
<<silently>><<set $currentPassage.index to lib.findIndexInArray($town.buildings, "key", $currentPassage.key)>> <<run lib.clampRolls($town.buildings[$currentPassage.index].roll)>> <<set $town.buildings[$currentPassage.index].priceModifier to Math.clamp($town.buildings[$currentPassage.index].priceModifier, -10, 10)>> <<set _name to $town.buildings[$currentPassage.index].name>><</silently>> <<nobr>><<link "reroll">> <<replace "#tavernname">> <<set $town.buildings[$currentPassage.index].name to lib.createTavernName()>> <label name="Rename the tavern"><<textbox "$town.buildings[$currentPassage.index].name" _name>></label> <</replace>><</link>> <span id="tavernname"><<textbox "$town.buildings[$currentPassage.index].name" _name>></span><</nobr>> <<button "Delete _name" Start>><<run setup.deleteBuilding($town, $town.buildings[$currentPassage.index])>><</button>> <span class="tip" data-tippy-content="How wealthy is the patronage?"><label for="numberslider-input-townbuildingscurrentpassageindexrollwealth">Tavern Wealth</label>: <<numberslider "$town.buildings[$currentPassage.index].roll.wealth" $town.buildings[$currentPassage.index].roll.wealth 1 100 1>></span> <span class="tip" data-tippy-content="How crowded is the tavern?"><label for="numberslider-input-townbuildingscurrentpassageindexrollpopulation">Tavern Population</label>: <<numberslider "$town.buildings[$currentPassage.index].roll.activity" $town.buildings[$currentPassage.index].roll.activity 1 100 1>></span> <span class="tip" data-tippy-content="How large is the tavern?"><label for="numberslider-input-townbuildingscurrentpassageindexrollsize">Tavern Size</label>: <<numberslider "$town.buildings[$currentPassage.index].roll.size" $town.buildings[$currentPassage.index].roll.size 1 100 1>></span> <span class="tip" data-tippy-content="How well known is this place? Does it have a legendary ale, or is it just another hole in the wall?"><label for="numberslider-input-townbuildingscurrentpassageindexrollreputation">Tavern Reputation</label>: <<numberslider "$town.buildings[$currentPassage.index].roll.reputation" $town.buildings[$currentPassage.index].roll.reputation 1 100 1>></span> <span class="tip" data-tippy-content="How likely is there to be a fight?"><label for="numberslider-input-townbuildingscurrentpassageindexrollroughness">Tavern Roughness</label>: <<numberslider "$town.buildings[$currentPassage.index].roll.roughness" $town.buildings[$currentPassage.index].roll.roughness 1 100 1>></span> <span class="tip" data-tippy-content="Is it a respectable venue, or a wretched hive of scum and villainy?"><label for="numberslider-input-townbuildingscurrentpassageindexrollsin">Tavern Sin</label>: <<numberslider "$town.buildings[$currentPassage.index].roll.sin" $town.buildings[$currentPassage.index].roll.sin 1 100 1>></span> <span class="tip" data-tippy-content="Is it clean?"><label for="numberslider-input-townbuildingscurrentpassageindexrollcleanliness">Tavern Cleanliness</label>: <<numberslider "$town.buildings[$currentPassage.index].roll.cleanliness" $town.buildings[$currentPassage.index].roll.cleanliness 1 100 1>></span> <span class="tip" data-tippy-content="How expensive is the tavern?">Tavern <label for="numberslider-input-townbuildingscurrentpassageindexrollmodifier">Price Modifier</label>: <<numberslider "$town.buildings[$currentPassage.index].priceModifier" $town.buildings[$currentPassage.index].priceModifier -10 10 1>></span> When you're ready, here's the [[output|TavernOutput]]
<<nobr>> <details><summary>On offer today...</summary> <<print lib.closestMatch(setup.tavern.get.menu($building), 'note', 'wealth', 'roughness', $building.roll.wealth, $building.roll.roughness)>> <<if $building.roll.wealth gte 50 && ndef $building.specialBrew>> <<set $building.specialBrew to setup.tavern.specialBrew.random()>> <div class='descriptive'> <h3>$building.specialBrew.name</h3> The special brew of the week is <<print lib.articles.output($building.specialBrew.type)>> called $building.specialBrew.name, selling for <<money $building.specialBrew.cost>>. $building.specialBrew.description </div> <</if>> The <<set _cheese to lib.cheese.create()>><<link "house cheese ">><<set _cheese to lib.cheese.create()>><<replace #cheese>> is <<money _cheese.cost>>, and is <<print lib.articles.output(_cheese.colour)>> cheese which is _cheese.texture. It smells _cheese.smell, and tastes _cheese.taste.<</replace>><</link>><span id="cheese">is <<money _cheese.cost>> and is a _cheese.colour cheese which is _cheese.texture. It smells _cheese.smell, and tastes _cheese.taste.</span> </details> <</nobr>>
<<linkreplace "<h4>Anything interesting in $building.name?</h4>" t8n>> <h4>Interesting things in $building.name...</h4>You see a noticeboard next to the $building.wordNoun's $building.material.noun bar. Walking over to it, you see a number of barter requests nailed to the noticeboard; simple things like <<print ["'have chook, want grain, have grain, want chook'", "'need an exterminator'", "'need help with rat problems'", "'hungry, don't want to walk'", "'need my roof cleaned'", "'I want an apple'", "'need a date for an upcoming wedding'"].random()>>. <<include "Posters">><</linkreplace>>
<<nobr>><details open><summary>Accomodation</summary> <<set _freeRooms to lib.tavernRooms($building)>> <<if _freeRooms <= 0>> <span id="room-cost">You talk with the <<print setup.profile($building.barmaid, "barmaid")>> about lodgings, and <<print $building.barmaid.heshe>> says that there are no free rooms in $building.name at the moment...</span> <<elseif _freeRooms == 1>> <<linkreplace "<h4>Rent a room for the night</h4>" t8n>> <<replace "#room-cost">><<print lib.tavernSleep($building)>> <<print setup.getWakeUpByWealth($building)>><</replace>> <</linkreplace>> <span id="room-cost">$building.name has just one available room, which is $building.size and <<print setup.tavern.get.bedCleanliness($building)>>. Because this $building.wordNoun is priced for $building.wealth patrons, it will cost <<money setup.tavern.get.lodging($building)>> to stay here for the night.</span> <<elseif _freeRooms >= 2>> <<linkreplace "<h4>Rent a room for the night</h4>" t8n>> <<replace "#room-cost">><<print lib.tavernSleep($building)>> <<print setup.getWakeUpByWealth($building)>><</replace>> <</linkreplace>> <span id="room-cost">$building.name has _freeRooms available rooms, which are $building.size and <<print setup.tavern.get.bedCleanliness($building)>>. Because this $building.wordNoun is priced for $building.wealth patrons, it will cost <<money setup.tavern.get.lodging($building)>> to stay here for the night.</span> <</if>></details> <</nobr>>
<<nobr>><<if $building.hasBrothel is true>> <<linkreplace "Go upstairs to the brothel" t8n>><h3>The Brothel</h3>When people talk about $building.name, they say that $building.talk. Apparently, it specialises in $building.speciality. You go up the $building.material staircase to the brothel section, and up there, you notice $building.notice. Rumours abound in whorehouses, and $building.name is no different; apparently, $building.rumour <</linkreplace>><</if>><</nobr>>
<span class="interactive-only"><<link "<h4>Meet one of the patrons!</h4>">><<if def _newNPC>> <<run setup.deleteNPC($npcs[_newNPC])>> <</if>> <<set $toolbox.drinker to setup.createNPC($town, { isThrowaway: true })>><<update>> <</link>></span> <<liveblock>> <<if def $toolbox.drinker>> <div class="descriptive"><h3>$toolbox.drinker.name</h3> <<print lib.articles.output($toolbox.drinker.descriptor)>> is currently <<print $toolbox.drinker.idle.random()>> in the corner. You strike up conversation with $toolbox.drinker.himher, and the $toolbox.drinker.descriptor introduces $toolbox.drinker.himherself as <<profile $toolbox.drinker>>, <<print lib.articles.output($toolbox.drinker.profession)>> who is <<print $toolbox.drinker.adventure || "looking for a drinking buddy">>. You talk about your backgrounds, and $toolbox.drinker.heshe says "I was <<print lib.articles.output($toolbox.drinker.background)>> before I was <<print lib.articles.output($toolbox.drinker.profession)>>. $toolbox.drinker.backgroundOrigin $toolbox.drinker.bond" </div><</if>><</liveblock>>
<<nobr>> <<set $building.gamer to setup.createNPC($town, { isShallow: true })>> <<set $building.gameBanker to setup.createNPC($town, { isShallow: true })>> <div class="descriptive"> <h3>$building.game.name</h3> $building.game.description <<linkreplace "Ask to play" t8n>> <br>The <<profile $building.gamer $building.gamer.descriptor>> that was playing says "$building.game.rules The current bet is <<money $building.game.bet>>." <</linkreplace>> </div> <</nobr>>
<<set $building to lib.findInArray($town.buildings, "key", $currentPassage.key)>><<run lib.tavernTypeFix($building)>> <<set $associatedNPC to $npcs[$building.associatedNPC.key]>> <<set $currentPassage.availableRelationships to lib.tavernCustomers>> <<include "PrintImage">> <<button "Edit $building.name" $building.initPassage>><</button>> <section><h1 class="interactive-only">$building.name</h1><span @id="$building.key"></span><p>You make your way through the $town.type of $town.name until you come to <<print lib.articles.output($building.wordNoun)>>, which is called <strong>$building.name</strong>, <<print lib.articles.output($building.size)>> $building.structure.descriptor that the locals know best for its $building.draw.</p></section> <section><h3>The Tavern</h3> <p><<print lib.getTavernDescription($building)>> $building.feature <<print lib.closestMatch(setup.tavern.get.lookAround($building), 'note', 'population', 'roughness', $building.roll.activity, $building.roll.roughness)>></p> <<include "TavernPatrons">> <<include "TavernDrinker">></section> <section><h3>The Bar</h3><p>Behind the counter is who you would assume to be the bartender, <<print lib.articles.output($associatedNPC.descriptor)>> who is currently <<print $associatedNPC.idle.random()>>. You walk up to the bar, and strike up conversation with the $associatedNPC.manwoman, who introduces $associatedNPC.himherself as <<print setup.profile($associatedNPC)>>, the $associatedNPC.owner of $building.name.</p> <<include "BartenderTalk">> <<include "Menu">> <<include "TavernSell">></section> <section><h3>Tavern Entertainment</h3> $building.entertainment <<include "TavernGames">> <span class="interactive-only hide-on-print"><<link "Go on a bender">><<set $building.carousing to setup.tavern.get.carousing($town, $building)>><<update>><</link>> <<liveblock>><<if def $building.carousing>><p id="carousing">$building.carousing</p><</if>><</liveblock>></span> <<include "Noticeboard">></section> <<details "CreateNewNpc" "Customers">> <<include "Rooms">>
<span class="click-and-remove-link"><<link "<h4>Who else is here?</h4>">><<set $building.patron to setup.tavern.get.patrons($town, $building)>><<update>><</link>></span> <<liveblock>><<if $building.patron>>$building.patron<</if>><</liveblock>>
<<nobr>> <details><summary>Menu</summary><table> <<if $building.roll.wealth > 60 && $building.roll.roughness < 30 or $building.roll.wealth > 60 && $associatedNPC.race === "elf">> <<set _dietary to ["veg"]>> <<elseif $building.roll.wealth > 60 && $building.roll.roughness > 80 or $building.roll.wealth > 60 && $associatedNPC.race === "half-orc">> <<set _dietary to ["carni"]>> <<else>> <<set _dietary to ["omni", "veg", "carni"]>> <</if>> <<switch $town.type>> <<case "city">> <<set _availability to 4>> <<case "town">> <<set _availability to 3>> <<case "village">> <<set _availability to 2>> <<case "hamlet">> <<set _availability to 1>> <<default>> <<set _availability to 3>> <</switch>> <tr> <th>Dish</th> <th>Cost</th> </tr> <<for _i, _item range lib.inventory.filter(function (item) { return item.availability <= _availability && item.availabilityLocation.includes("tavern") && _dietary.includes(item.dietary); })>><tr><td>_item.name</td> <td><<money _item.cost $building>></td></tr> <</for>> </table> </details> <</nobr>>
<h3>Installation</h3> <ol> <li>Go in the ''Add-on Modules'' section in __Foundry__.</li> <li>Click ''Install Module''</li> <li>Search for ''EEEG-Importer'' or paste the manifest ({{{.json}}} file) from https://github.com/HadaIonut/EEEG-importer/releases/ and install the module.</li></ol> <h3>Usage</h3> <ol><li>Go into the journal entries directory</li> <li>Click on the EEEG-import button located at the top of the tab.</li> <li>Copy the above text into the text box window in Foundry</li> <li>Click the ''import'' button</li> <li>Enjoy!</li></ol> <h4>Additional features</h4> * Importing NPCs as actors: With this feature you can make all NPCs into actors (these actors don't have stats generated, so they only have the name and the biography added). * Configuring locations for NPCs as journal entries: In case anyone wants to have a single folder with all the actors then they can do so * Configuring locations for NPCs as actors: Select a folder (or the main directory) where the actors are placed
Copy paste the above into a [[GMBinder|https://www.gmbinder.com/]] document. You can likely use any {{{.html}}} renderer, but this is optimized for GMBinder. You need to set the document to not use the default GMBinder theme in the document settings in order for it to look like the DMG. Please note that you will need to add pagebreaks ({{{\pagebreakNum}}}) to ensure that info is not being pushed off the page.
Copy paste the above into a [[Homebrewery|https://homebrewery.naturalcrit.com/]] document. You can likely use any {{{.html}}} renderer, but this is optimized for Homebrewery. Hyperlinks in the text only work when you have downloaded the PDF; otherwise, they will register as links on the Homebrewery domain. Please note that you will need to add pagebreaks ({{{\page}}}) to ensure that info is not being pushed off the page.
<label name="Export type" for="listbox-exporttype"><<listbox "_exportType">> <<option "GMBinder">> <<option "Homebrewery">> <<option "Foundry">> <<option "NovelAI">> <<option "Raw JSON">> <</listbox>></label> -- <span class="tip" data-tippy-content="This can take a while."><<button "Export">> <<run setup.addGtagEvent({ event_category: 'export', event_action: 'loaded', event_label: _exportType })>> <<switch _exportType>> <<case "GMBinder">> <<set _exportContent to setup.outputGMBinder()>> <<replace "#tutorial">><<include "GMBinderTutorial">><</replace>> <<case "Homebrewery">> <<set _exportContent to setup.outputGMBinder()>> <<replace "#tutorial">><<include "HomebreweryTutorial">><</replace>> <<case "Raw JSON">> <<set _exportContent to JSON.stringify({ town: $town, npcs: $npcs, deities: lib.getPantheonDeities($town, State.metadata.get('pantheon')) })>> <<replace "#tutorial">>This is all of the data from the {{{$town}}} and {{{$npcs}}} objects, for whatever purposes you may desire.<</replace>> <<case "NovelAI">> <<include "Auth">><<if State.metadata.get('patreonPass') !== $_>><<run setup.openDialog({ header: 'Patreon Only', passage: 'ImportPatreon', rerender: true })>><<else>> <<set _exportContent to JSON.stringify(setup.exportToNovelAI($town, $npcs))>> <<set _link to "https://novelai.net/#/stories/" + $town.key>> <<set _title to $town.type + ' of ' + $town.name + '.scenario'>> <<replace "#tutorial">>Download the `_title` file, and then import the scenario [[here|_link]].<</replace>> <</if>> <<case "Foundry">> <<include "Auth">><<if State.metadata.get('patreonPass') !== $_>><<run setup.openDialog({ header: 'Patreon Only', passage: 'ImportPatreon', rerender: true })>><<else>> <<set _exportContent to JSON.stringify(setup.outputEverything())>> <<replace "#tutorial">><<include "FoundryTutorial">><</replace>><</if>> <</switch>> <<replace "#everything">> <<button "Copy">><<run setup.copyText()>><</button>> -- <<textbox "$outputEverything" _exportContent>> <</replace>> <</button>> <<checkbox "$addPantheon" false true autocheck>> Include Pantheon?</span> <span id="everything"></span> <span id="tutorial"><<include "GoalBasedUnlockingText">></span>
<<widget "goods">> <<nobr>> <<set _obj to $args[0]>> <<set _target to $args[1]>> <<capture _obj, _target>> <table> <tr> <th>Goods</th> <th>Cost</th> </tr> <<for _item range _target>> <<if ndef _item.exclusions>> <<set _isValid to true>> <<elseif (typeof _item.exclusions === "function")>> <<set _isValid to _item.exclusions($town, _obj)>> <</if>> <<if _isValid === true>> <<set _name to _item.name || _item.summary>> <<set _name to _name.toUpperFirst()>> <tr> <td> <<if def _item.description>> <<tooltip _name _item.description>> <<else>> <<print _item.name || _item.summary>> <</if>> </td> <td><<money _item.cost _obj>></td> </tr> <</if>><<unset _isValid>><</for>> </table> <</capture>> <</nobr>> <</widget>> \<<widget "shop">> \<<set _building to $args[0]>> \<<set _tableHeaders to $args[1].tableHeaders || { left: "item", right: "cost" }>> \<<set _title to $args[1].title || null>> \<table><tr> \<th><<print lib.toTitleCase(_tableHeaders.left)>></th> \<th><<print lib.toTitleCase(_tableHeaders.right)>></th></tr> \<<for _i, _type range Object.keys($args[1].category)>> \<tr><th><<print lib.toTitleCase(_type)>></th><th></th></tr> \<<set _availableItems to lib.inventory.filter(function (item) { return item.availability <= _availability && item.type === _type && item.availabilityLocation.includes($building.buildingType) })>> \<<if _availableItems.length > 0>> \<<for _i, _item range _availableItems>> \<tr><td>_item.name</td><td><<money _item.cost $building>></td></tr> \<</for>><</if>> \<</for>></table><</widget>> \<<widget "sell">> \<<set _building to $args[0]>> \<<set _title to $args[1]>> \<<set _key to $args[2]>> \<<if ndef _key>><<set _key to _title>><</if>> \<<set _availableItems to lib.inventory.filter(function (item) { return item.availability <= _availability && item.type === _key && item.availabilityLocation.includes($building.buildingType) })>> <tr><th><<print lib.toTitleCase(_title)>></th><th>Cost</th></tr> \<<for _i, _item range _availableItems>> <tr><td>_item.name</td><td><<money _item.cost $building>></td></tr> \<</for>> <</widget>> <<widget "tooltip">><<nobr>> <<set _target to $args[0]>> <<set _description to $args[1] || $args[0]>> <<print lib.createTippyFull(_description, _target)>> <</nobr>><</widget>> <<widget "money">><<print setup.money($args[0], $args[1])>><</widget>> <<widget "factionResource">><<if $args[1] === true>><<print $args[0].amount.toUpperFirst()>> $args[0].type<<else>><<print $args[0].amount>> $args[0].type<</if>><</widget>> <<widget "temp">> <<nobr>> <<set _temp = $args[0]>> <<capture _temp>> <<if $showCelsius is true>> <<set _temp -= 32>> <<set _temp to Math.trunc(_temp *= 0.5556)>>_temp Celsius <<else>> _temp Fahrenheit <</if>> <</capture>> <</nobr>> <</widget>>
<<nobr>><span class="tip" data-tippy-content="This changes each time you click."><<link "landmark">><<set $town.landmark to lib.townData.misc.landmark.random()>> <<replace "#random">> $town.landmark <</replace>><</link>></span> is <span id="random">$town.landmark</span><</nobr>>
<<if random(1, 3) > 2>><blockquote class="interactive-only"> As you make your way along <<if $currentPassage.road>> <<print lib.createTippyFull($town.roads[$currentPassage.road].description, $town.roads[$currentPassage.road].name)>> <<else>> the road, <</if>> <<print lib.townData.misc.microEvent.random()>></blockquote><</if>>
<<nobr>><span class="interactive-only"><<button "Edit $town.name" TownEdit>><<run setup.history($town, 'TownOutput', "Editing town")>><</button>></span> <<include "BriefDescription">> <p>The nearest <<include "TownLandmarkRandomizer">> A population of $town.population, the denizens live <<print lib.articles.output(lib.getTownWealth($town.roll.wealth))>> existence. <<print $town.currentEvent.toUpperFirst()>> is currently taking place.</p> <article><h3>Government in $town.name</h3><<print $town.economicIdeologyDescription($town)>> <<print setup.getPoliticalSourceDescription($town)>></article> <article><h4>Economics</h4><<print lib.getTownEconomics($town)>> <<print lib.getTownWelfare($town)>></article> <article><h4>Law and Order</h4><<print setup.getTownMilitary($town)>> <<print lib.getTownLaw($town)>> <<print lib.getTownArcana($town)>></article><</nobr>> <<details "TownListRaces" "Racial Demographics">> <<details "TownListReligion" "Religious Demographics">> <<details "TownListFactions" "List of Factions">> <<details "TownListBuildings" "List of Buildings">> <<if _exportType === 'GMBinder'>> \\pagebreakNum <<elseif _exportType === 'Homebrewery'>> \\pagebreak <</if>> <<nobr>><<details "TownListNpcs" "List of NPCs">> <<details "TownListThrowaway" "List of Throwaway NPCs">> <span class='interactive-only show-on-print'><<details "TownListProfessions" "Professions in $town.name">></span><</nobr>>
<span class="interactive-only" class='hide-on-print'><<silently>> <<set _listbox to '<label name="Faction type" for="listbox-newfaction"><<listbox "$newFaction">>'>> <<for _key range Object.keys(lib.factionData.types)>> <<set _listbox += '<<option "' + _key + '" "' + _key + '">>'>> <</for>> <<set _listbox += '<</listbox>></label>'>> <</silently>> _listbox -- <<button "Create new faction">> <<run lib.logger.info('Creating a new ' + $newFaction)>> <<set _tempFaction to setup.createFaction($town, { type: $newFaction })>> <<set $town.factions[_tempFaction.key] = _tempFaction>> <<run lib.logger.info($town)>> <<update>> <</button>></span>
<<include "CreateBuilding">> <<liveblock>> <table> <tr> <th>Name</th> <th>Type</th> <th>Associated NPC</th> <span class="interactive-only"><th>Delete</th></span> </tr> <<for _key, _building range $town.buildings>><<capture _key, _building>> <<set _building.tooltip = "tip-" + Util.slugify(_building.name) + "-" + Math.floor(randomFloat(1).toString(16))>> <tr><td><span @id="_building.name"><<profile _building>></span></td> <td><<print lib.toTitleCase(_building.wordNoun || _building.type)>></td> <td><<if _building.associatedNPC>><<profile _building.associatedNPC>><</if>></td> <span class="interactive-only"><td><<link "x">><<run setup.deleteBuilding($town, _building)>><<update>><</link>></td></span> </tr> <</capture>><<set _previousBuilding to _building>> <</for>> </table><</liveblock>>
<<include "CreateFaction">> <<liveblock>><div class='classTable'><<nobr>><table> <tr> <th>Name</th> <th>Type</th> <th>Size</th> <span class="interactive-only"><th>Delete</th></span> /* <th>Raw Number</th> */ </tr> <<for _key, _faction range $town.factions>><<capture _key, _faction>> <tr> <td><<profile _faction>></td> <td><<print _faction.type.toUpperFirst()>> <<print _faction.wordNoun.toUpperFirst()>></td> <td><<print lib.toTitleCase(_faction.size)>></td> <span class="interactive-only"><td><<link "x">><<set $deleted to setup.deleteFaction($town, _key)>><<update>><<notify 5000>>Deleted $deleted.name<</notify>><</link>></td></span> </tr> /* - A _faction.size _faction.type _faction.wordNoun */ <</capture>><</for>></table><</nobr>></div><</liveblock>>
<<liveblock>><<nobr>> <table id="npctable"> <tr> <th onclick="sortTable('npctable',0)">Name</th> <th onclick="sortTable('npctable',1)">Race</th> <th onclick="sortTable('npctable',2)">Profession</th> <span class="interactive-only"><th>Delete</th></span> </tr> <<for _i, _npc range $npcs>><<capture _i, _npc>><<set _profession to lib.toTitleCase(_npc.profession)>><<if !_npc.isThrowaway>> <tr><td><<profile $npcs[_npc.key]>></td> <td><<print _npc.race.toUpperFirst()>></td> <td><<print lib.createTippyFull(lib.professions[_npc.profession].description.toUpperFirst(), _profession)>></td> <span class="interactive-only"><td><<link "x">><<set _deleted to $npcs[_npc.key]>><<run setup.deleteNPC($npcs[_npc.key])>><<notify 5000>>Deleted _deleted.name<</notify>><<update>><</link>></td></span> </tr> <</if>><</capture>><</for>> </table><</nobr>><</liveblock>> <script> function sortTable(title, n) { var rows, i, x, y, shouldSwitch, switchcount = 0; var table = document.getElementById(title); var switching = true; var dir = "asc"; while (switching) { switching = false; rows = table.rows; for (i = 1; i < (rows.length - 1); i++) { shouldSwitch = false; x = rows[i].getElementsByTagName("TD")[n]; y = rows[i + 1].getElementsByTagName("TD")[n]; if (dir == "asc") { if (x.innerHTML.toLowerCase() > y.innerHTML.toLowerCase()) { shouldSwitch = true; break; } } else if (dir == "desc") { if (x.innerHTML.toLowerCase() < y.innerHTML.toLowerCase()) { shouldSwitch = true; break; } } } if (shouldSwitch) { rows[i].parentNode.insertBefore(rows[i + 1], rows[i]); switching = true; switchcount ++; } else { if (switchcount == 0 && dir == "asc") { dir = "desc"; switching = true; } } } } </script>
<<set _count to 0>> <table class='show-on-print'> <tr> <th>Profession</th> <th>Number</th> <th>Percentage</th> <th>NPC</th> </tr> <<set $town.professions to lib.fetchProfessions($town)>> <<for _name, _profession range $town.professions>><<nobr>> <<capture _name, _profession>> <<set _count += ((_profession.population / $town.population) * 100)>> <<set _article to lib.articles.output(_name)>> <<set _npcs to lib.filterNpcByProfession($npcs, _name)>> <<set _nameTitle to lib.toTitleCase(_name)>> <tr><td><<print lib.createTippyFull(lib.professions[_name].description.toUpperFirst(), _nameTitle)>></td> <td>_profession.population</td> <td><<print ((_profession.population / $town.population) * 100).toFixed(2)>></td> <td><<if _npcs.length === 1>> <<profile _npcs[0]>> <<elseif _npcs.length > 1>> <<for _i, _npc range _npcs>> <<profile _npc>><</for>> <<else>><span class="interactive-only hide-on-print"><<link "Create _article" $currentPassage.passageName>><<set $currentPassage to setup.createNPC($town, { profession: _name, isThrowaway: true })>><</link>></span> <</if>></td></tr><</capture>><</nobr>><</for>> </table>Total: <<print _count.toFixed(2)>>% of the total population.
<div class='classTable' id="races"> $town.name is comprised <<lh lib.getPredominantRace(lib.getDemographicPercentile($town)).amountDescriptive>>. <table> <tr> <th>Race</th> <th>Population</th> <th>Percentage</th> </tr> <<for _race, _percentage range lib.getDemographicPercentile($town)>> <tr> <td><<print _race.toUpperFirst()>></td> <td><<print Math.trunc($town.population * (_percentage / 100))>></td> <td><<print _percentage.toFixed(2)>></td> </tr> <</for>> </table> </div>
<div class='classTable' id="religion"> <<liveblock>> <<set _religionPercentages to lib.getPantheonPercentages($town, State.metadata.get('pantheon'))>> $town.name <<lh lib.getPredominantReligion($town, _religionPercentages).amountDescriptive>>.<</liveblock>> <table> <tr> <th>Deity</th> <th>Population</th> <th>Percentage</th> </tr> <<for _deity, _percentage range _religionPercentages>> <<set _deityData to lib.getDeity($town, _deity)>> <<capture _deity, _percentage, _deityData>> <<if _percentage === 0>><<continue>><</if>> <tr> <td><<profile _deityData>></td> <td><<print Math.trunc($town.population * (_percentage / 100))>></td> <td><<print _percentage.toFixed(2)>></td> </tr> <</capture>> <</for>> </table> </div>
<<liveblock>><<nobr>> <<button "Wipe all throwaway NPCs">> <<run setup.deleteThrowawayNPCs()>> <<update>> <</button>> <table> <tr> <th>Name</th> <th>Race</th> <th>Profession</th> <span class="interactive-only"><th>Delete</th></span> </tr> <<for _i, _npc range $npcs>> <<capture _i, _npc>> <<set _profession to lib.toTitleCase(_npc.profession)>> <<if _npc.isThrowaway>> <tr> <td><<profile $npcs[_npc.key]>></td> <td><<print _npc.race.toUpperFirst()>></td> <td><<print lib.createTippyFull(lib.professions[_npc.profession].description.toUpperFirst(), _profession)>></td> <span class="interactive-only"><td><<link "x">><<set _deleted to $npcs[_npc.key]>><<run setup.deleteNPC($npcs[_npc.key])>><<notify 5000>>Deleted _deleted.name<</notify>><<update>><</link>></td></span> </tr> <</if>> <</capture>> <</for>> </table> <</nobr>><</liveblock>>
<details class="races-slider" open> <summary>Racial Demographics</summary> <<liveblock>> <span class="tip" data-tippy-content="How many people live here?"> Town Population: <<numberslider "$town.population" $town.population 30 12000 10>> </span> <br> <p> <<print lib.getRaceReadout($town)>> </p> <br> <table id='races' style='column-span: all'> <tr> <th>Race</th> <th>Population</th> <th>Percentage</th> <th>Raw Number</th> </tr> <<for _race, _raw range $town.baseDemographics>> <<set _populations to lib.getRacesPopulation($town.baseDemographics, $town.population)>> <<set _percentages to lib.getRacesPercentile($town.baseDemographics)>> <<capture _race, _raw, _populations, _percentages>> <tr> <td><<print _race.toUpperFirst()>></td> <td><<print Math.trunc(_populations[_race])>></td> <td><<print Math.trunc(_percentages[_race])>></td> <td> <span class="tip races-slider" data-tippy-content="This is automatically converted into a percentage; you don't need to worry about it not adding up to 100!"> <<numberslider `"$town.baseDemographics['" + _race + "']"` $town.baseDemographics[_race] 0 100 1>> </span> </td> </tr> <</capture>> <</for>> </table><<run lib.addTippyAccessibility()>> <</liveblock>> </details> /* <</liveblock>> */
<details open> <summary>Religion Demographics</summary> <<liveblock>> <<set $town.religionProbabilities to lib.getAllPantheonPercentages($town, State.metadata.get('pantheon'))>> $town.name <<print lib.getPredominantReligion($town, lib.getAllPantheonPercentages($town, State.metadata.get('pantheon'))).amountDescriptive>>. <table class='classTable religion-slider'> <tr> <th>Deity</th> <th>Population</th> <th>Percentage</th> <th>Base Probability</th> <th>Bonus / Penalty</th> </tr> <<for _deity, _deityPercentage range $town.religionProbabilities>> <<set _deityData to lib.getDeity($town, _deity)>> <<if ndef $town.religion._modifiers[_deity]>> <<set $town.religion._modifiers[_deity] to 0>> <</if>> <<capture _deityData, _deity>> /* <<if _deityPercentage === 0>><<continue>><</if>> */ <tr> <td><<profile _deityData>></td> <td><<print Math.trunc($town.population * (lib.getAllPantheonPercentages($town, State.metadata.get('pantheon'))[_deity] / 100))>></td> <td><<print lib.getAllPantheonPercentages($town, State.metadata.get('pantheon'))[_deity].toFixed(2)>></td> <td> <span class="tip religion-slider" data-tippy-content="This is calculated from the town, and represents the base probability."> <<print lib.getUnalteredTownDeityWeightings($town, lib.getPantheonDeities($town, State.metadata.get('pantheon')))[_deity].toFixed(0)>> </span> </td> <td> <span class="tip religion-slider" data-tippy-content="This is added to the base probability- it doesn't need to add up to a round number."> <<numberslider `"$town.religion._modifiers['" + _deity + "']"` $town.religion._modifiers[_deity] -1000 1000 5>> </span> </td> </tr> <</capture>> <</for>> </table> <</liveblock>> </details>
<details open><summary>Attributes</summary> <table> <tr> <th>Attribute Slider</th> <th>Percentage Value</th> </tr> <<nobr>><<for _key, _obj range lib.townData.rollData>><<capture _key, _obj>><<if _key === 'equality'>><<continue>><</if>><<set _tooltip to lib.townData.rollData[_key].tooltip>> <<if _obj.isHidden>> <<continue>> <<else>> <tr><td><<print lib.createTippyFull(_tooltip, lib.townData.rollData[_key].preceding)>></td><td style="21vw"> <<numberslider "$town.roll[_key]" $town.roll[_key] 1 100 1>></td></tr> <</if>> <</capture>><</for>><</nobr>> <tr><td class="tip" title="How sexist is their society?">Sexist (in favour of <span class="tip" title="Which is the dominant gender? Note that the degree of dominance is determined by its equality, not this dropdown."><label name="Dominant gender" for="listbox-towndominantgender"><<listbox "$town.dominantGender" autoselect>><<option "man">><<option "woman">><</listbox>></label>)</span> -- Total Egalitarianism:</td><td style="21vw"> <<numberslider "$town.roll.equality" $town.roll.equality 1 100 1>></td></tr> </table><<run lib.addTippyAccessibility()>> </details>
<<set _economic to "How does the economy work? \n <ul>" + Object.values(lib.townData.economicIdeology).map(ideology => { return `<li><b>${ideology.descriptors.economicIdeologyIC.toUpperFirst()}</b>: ${ideology.descriptors.tippy}</li>` }).join('\n') + '</ul>'>> <<set _political to "Who leads the people? \n <ul>" + Object.keys(lib.townData.politicalIdeology).map(ideology => { return `<li><b>${ideology.toUpperFirst()}</b>: ${lib.townData.politicalIdeology[ideology].data.description}</li>` }).join('\n') + '</ul>'>> <<include "ListboxOptions">><span class="tip" @data-tippy-content="_economic"><label name="Town economic ideology" for="listbox-towneconomicideology"><<listbox "$town.economicIdeology" autoselect>><<optionsfrom $listboxes.economicIdeology>><</listbox>></label></span> <span class="tip" @data-tippy-content="_political"><label name="Town political ideology" for="listbox-townpoliticalideology"><<listbox "$town.politicalIdeology" autoselect>><<optionsfrom $listboxes.politicalIdeology>><</listbox>></label></span> <span class="tip" data-tippy-content="Why do they rule? Are they elected, or born into power?"><label name="Town political source" for="listbox-townpoliticalsource"><<listbox "$town.politicalSource" autoselect>><<optionsfrom $listboxes.politicalSource>><</listbox>></label></span>
<<liveblock>> <span class='hide-on-print'><<link "reroll">> <<set $town.name to lib.createTownName($town)>><<update>> <</link>> -- </span><label name="Rename town" class="auto-update"><<textbox "$town.name" $town.name>></label> <</liveblock>>
<<run lib.logger.info("Saving changes...")>> <<run lib.logger.info($town)>> <<run setup.createSocioPolitics($town)>> <<run lib.updateTownSocioPolitics($town)>> <<set $npcs to setup.checkRaces($town, $npcs)>> <<run lib.townRender($town)>> <<run lib.logger.info($town)>>
<<run $("body").on("change", [".races-slider", ".religion-slider"], function() { $(document).trigger(":liveupdate"); tippy('.tip'); });>> <<include "EditTownName">> <<button "Refresh">> <<update>> <</button>> <<include "BiomeGenerationRefresh">> <<include "EditSliders">> <hr> <<include "EditRaces">> <<include "EditReligion">><<done>><<run tippy('[data-tippy-content]')>><</done>> <span class='flex-line'><<button "Save Changes" TownOutput>> <<include "Rerender">> <<run setup.history({passageName: "TownOutput", linkDescription: $town.name, name: $town.name}, 'TownOutput', $town.name)>> <</button>></span>
<span class='flex-line'> <label name="Current season" for="listbox-towncurrentseason"> <<listbox "$town.currentSeason" autoselect>> <<option "Summer" "summer">> <<option "Autumn" "autumn">> <<option "Winter" "winter">> <<option "Spring" "spring">> <<option "No weather" "null">> <</listbox>> </label> <label for="scenariotype"> <<listbox "$scenarioType">> <<option "Town Encounter" "town">> <<option "Forest" "forest">> <<option "Desert" "desert">> <<option "Mountain" "mountain">> <<option "Road" "road">> <</listbox>> </label> <<button "Create Scenario">> <<if def _newNPC>> <<run setup.deleteNPC($npcs[_newNPC.key])>> <<unset _newNPC>> <</if>> <<if def $town.previousSeason && $town.previousSeason !== $town.currentSeason>> <<set $town.weather.timer = { temperature: 0, precipitation: 0, cloud: 0 }>> <</if>> <<set $town.previousSeason to $town.currentSeason>> <<set $scenario to setup.misc[$scenarioType].create($town)>> <<if $town.currentSeason !== "null">> <<set $town.weather to setup.createWeather($town, $scenarioType, $town.weather)>> <</if>> <<update>> <</button>> </span> <<liveblock>> <div id="scenario"> <<if def $scenario>> <<if $town.currentSeason !== "null">> <<print setup.getWeatherReadout($town.weather)>> <</if>> $scenario <</if>> </div> <</liveblock>>