Brandon Cornejo
4 years ago
8 changed files with 1316 additions and 637 deletions
-
9acks/templates/base.html
-
141acks/templates/srd/Chapter00.html
-
381acks/templates/wandering_monster.html
-
18acks/views.py
-
267acks/wandering_monster.py
-
1roll20/macros/initiative.macro
-
1roll20/macros/roll-stats.macro
-
11roll20/macros/scavenge.macro
@ -0,0 +1,381 @@ |
|||||
|
{% extends "base.html" %} |
||||
|
{% set active_page = "wandering_monster" %} |
||||
|
|
||||
|
{% block title %}Wandering Monsters{% endblock %} |
||||
|
{% block content %} |
||||
|
<div class="uk-flex uk-flex-center uk-margin-bottom uk-margin-top"> |
||||
|
<h1 class="uk-text-center"><strong>Adventurer Conqueror King</strong>Wandering Monsters</h1> |
||||
|
</div> |
||||
|
|
||||
|
<form id="wandering_form" autocomplete="off"> |
||||
|
<div class="uk-margin uk-text-center"> |
||||
|
<span class="uk-margin-right">Where is the party?</span> |
||||
|
<button class="uk-button uk-button-default" type="button" id="dungeon_select">Dungeon</button> |
||||
|
<button class="uk-button uk-button-default" type="button" id="wilderness_select">Wilderness</button> |
||||
|
</div> |
||||
|
|
||||
|
<div class="uk-margin"> |
||||
|
<div id="dungeon_content" class="uk-hidden"> |
||||
|
<hr class="uk-divider-icon"/> |
||||
|
|
||||
|
<div class="uk-margin" uk-grid> |
||||
|
<div class="uk-width-1-3"> |
||||
|
<h2 class="uk-text-left">Dungeon Monster</h2> |
||||
|
<h3>Configure</h3> |
||||
|
|
||||
|
<div class="uk-margin uk-text-meta"> |
||||
|
<strong>Frequency:</strong> When in a dungeon, the Judge should make an encounter throw every 2 turns. A throw of 6+ on a 1d6 indicates a wandering monster is encountered. |
||||
|
</div> |
||||
|
<div class="uk-margin"> |
||||
|
<div> |
||||
|
<label for="dungeon_level" class="uk-margin-right"><strong>Current dungeon level:</strong></label> |
||||
|
<select name="dungeon_level" id="dungeon_level" class="uk-select uk-width-1-5"> |
||||
|
<option value="1" selected>1</option> |
||||
|
<option value="2">2</option> |
||||
|
<option value="3">3</option> |
||||
|
<option value="4">4</option> |
||||
|
<option value="5">5</option> |
||||
|
<option value="6">6+</option> |
||||
|
</select> |
||||
|
</div> |
||||
|
|
||||
|
<div class="uk-margin"> |
||||
|
<button type="button" class="uk-button uk-button-primary" id="dungeon_generate_button">Roll</button> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="uk-width-1-5"> </div> |
||||
|
<div id="dungeon_output" class="uk-width-expand uk-hidden"> |
||||
|
<div id="roll_result" class="uk-margin uk-text-muted"></div> |
||||
|
<div class="uk-card uk-card-secondary uk-card-body"> |
||||
|
<h3 id="wm_entity" class="uk-card-title uk-text-center uk-margin uk-text-success uk-margin-medium-bottom"></h3> |
||||
|
|
||||
|
<div class="uk-margin uk-column-1-2 uk-column-divider uk-text-meta"> |
||||
|
<div class="uk-margin"><strong>Quantity:</strong> <span id="wm_quantity"/></div> |
||||
|
<div class="uk-margin"><strong>Reaction:</strong> <span id="wm_reaction"/></div> |
||||
|
<div class="uk-margin"><strong>Dungeon Level:</strong> <span id="wm_dungeon_level"/></div> |
||||
|
|
||||
|
<div class="uk-margin"><strong>Distance:</strong> <span id="wm_distance"/></div> |
||||
|
<div class="uk-margin"><strong>Surprise:</strong> <span id="wm_surprise"/></div> |
||||
|
<div class="uk-margin"><strong>Roll/Adjust:</strong> <span id="wm_adjust"/></div> |
||||
|
<div class="uk-margin"><strong>Monster Table:</strong> <span id="wm_monster_table"/></div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<div id="wilderness_content" class="uk-hidden"> |
||||
|
<hr class="uk-divider-icon"> |
||||
|
|
||||
|
<div class="uk-margin" uk-grid> |
||||
|
<div class="uk-width-1-3"> |
||||
|
<h2 class="uk-text-left">Wilderness Monster</h2> |
||||
|
<h3>Configure</h3> |
||||
|
|
||||
|
<div class="uk-margin uk-text-meta"> |
||||
|
<strong>Frequency:</strong> When the characters in the wilderness, the Judge should make an encounter throw once per day if they are stationary or in settled terrain. Otherwise, the Judge should make an encounter throw each time the adventurers enter a new 6-mile hex. |
||||
|
</div> |
||||
|
<div class="uk-margin"> |
||||
|
<div> |
||||
|
<label for="terrain_type" class="uk-margin-right"><strong>Current terrain type:</strong></label> |
||||
|
<select name="terrain_type" id="terrain_type" class="uk-select uk-width-1-1"> |
||||
|
<option value="1" selected>Clear, Grass, Scrub</option> |
||||
|
<option value="2">Woods</option> |
||||
|
<option value="3">River</option> |
||||
|
<option value="4">Swamp</option> |
||||
|
<option value="5">Mountains, Hills</option> |
||||
|
<option value="6">Barren, Desert</option> |
||||
|
<option value="7">Inhabited</option> |
||||
|
<option value="8">City</option> |
||||
|
<option value="9">Ocean</option> |
||||
|
<option value="10">Jungle</option> |
||||
|
</select> |
||||
|
</div> |
||||
|
|
||||
|
<div class="uk-margin"> |
||||
|
<button type="button" class="uk-button uk-button-primary" id="wilderness_generate_button">Roll</button> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="uk-width-1-5"> |
||||
|
|
||||
|
</div> |
||||
|
<div id="wilderness_output" class="uk-width-expand uk-hidden"> |
||||
|
<div id="roll_result" class="uk-margin uk-text-muted"></div> |
||||
|
<div class="uk-card uk-card-secondary uk-card-body"> |
||||
|
<h3 id="wm_entity" class="uk-card-title uk-text-center uk-margin uk-text-success"></h3> |
||||
|
|
||||
|
<div class="uk-margin uk-column-1-2 uk-column-divider uk-text-meta"> |
||||
|
<div><strong>Surprise:</strong> <span id="wm_surprise"/></div> |
||||
|
<div><strong>Reaction:</strong> <span id="wm_reaction"/></div> |
||||
|
<div><strong>Monster Table:</strong> <span id="wm_monster_table"/></div> |
||||
|
<div><strong>Terrain Type:</strong> <span id="wm_terrain_type"/></div> |
||||
|
</div> |
||||
|
<div id="wilderness_distance_table" class="uk-margin uk-text-meta uk-flex uk-flex-center uk-margin-large-top"> |
||||
|
<table> |
||||
|
<thead> |
||||
|
<tr> |
||||
|
<th style="text-align: left">Terrain</th> |
||||
|
<th style="text-align: left">Encounter Distance (yards)</th> |
||||
|
</tr> |
||||
|
</thead> |
||||
|
<tbody> |
||||
|
<tr> |
||||
|
<td style="text-align: left">Badlands</td> |
||||
|
<td style="text-align: left">2d6x10</td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<td style="text-align: left">Desert</td> |
||||
|
<td style="text-align: left">4d6x10</td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<td style="text-align: left">Fields, Fallow</td> |
||||
|
<td style="text-align: left">4d6x10</td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<td style="text-align: left">Fields, Ripe</td> |
||||
|
<td style="text-align: left">5d10</td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<td style="text-align: left">Fields, Wild</td> |
||||
|
<td style="text-align: left">3d6x5</td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<td style="text-align: left">Forest, Heavy or Jungle</td> |
||||
|
<td style="text-align: left">5d4</td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<td style="text-align: left">Forest, Light</td> |
||||
|
<td style="text-align: left">5d8</td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<td style="text-align: left">Marsh</td> |
||||
|
<td style="text-align: left">8d10</td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<td style="text-align: left">Mountains</td> |
||||
|
<td style="text-align: left">4d6x10</td> |
||||
|
</tr> |
||||
|
<tr> |
||||
|
<td style="text-align: left">Plains</td> |
||||
|
<td style="text-align: left">5d20x10</td> |
||||
|
</tr> |
||||
|
</tbody> |
||||
|
</table> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<hr class="uk-divider-icon"> |
||||
|
|
||||
|
<div> |
||||
|
<h3 class="uk-text-center">Roll Modifiers</h3> |
||||
|
<div class="uk-margin-bottom uk-margin-xlarge-left uk-margin-xlarge-right" uk-grid> |
||||
|
<div class="uk-width-1-3 uk-flex-column uk-padding"> |
||||
|
<div class="uk-margin-right">Encounter: <input type="number" id="check_mod" min="-10" max="10" class="uk-input uk-width-1-4 uk-margin-left" value="0" onclick="this.select();"></div> |
||||
|
<div class="uk-text-meta uk-margin-small-top">Adjustment to chance of encountering a wandering monster at all.</div> |
||||
|
</div> |
||||
|
<div class="uk-width-1-3 uk-flex-column uk-padding"> |
||||
|
<div class="uk-margin-right">Surprise: <input type="number" id="surprise_mod" min="-10" max="10" class="uk-input uk-width-1-4 uk-margin-left" value="0" onclick="this.select();"></div> |
||||
|
<div class="uk-text-meta uk-margin-small-top">Adjustments to surprise for vigilance, lighting, noises, and any other bonuses/penalties.</div> |
||||
|
</div> |
||||
|
<div class="uk-width-1-3 uk-flex-column uk-padding"> |
||||
|
<div class="uk-margin-right">Reaction: <input type="number" id="reaction_mod" min="-10" max="10" class="uk-input uk-width-1-4 uk-margin-left" value="0" onclick="this.select()"></div> |
||||
|
<div class="uk-text-meta uk-margin-small-top">Charisma modifier of "lead" character, other situational bonuses/penalties.</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</form> |
||||
|
{% endblock %} |
||||
|
|
||||
|
{% block head %} |
||||
|
<style> |
||||
|
div#wilderness_distance_table { |
||||
|
font-size: .835rem; |
||||
|
} |
||||
|
</style> |
||||
|
{% endblock %} |
||||
|
|
||||
|
{% block scripts %} |
||||
|
<script type="text/javascript"> |
||||
|
window.addEventListener('DOMContentLoaded', () => { |
||||
|
var dungeon_select = document.querySelector('button#dungeon_select'); |
||||
|
var wilderness_select = document.querySelector('button#wilderness_select'); |
||||
|
|
||||
|
dungeon_select.addEventListener("click", toggleLocation); |
||||
|
wilderness_select.addEventListener("click", toggleLocation); |
||||
|
|
||||
|
var dungeon_roll = document.querySelector('button#dungeon_generate_button'); |
||||
|
dungeon_roll.addEventListener("click", generateWanderingDungeonMonster); |
||||
|
|
||||
|
var wilderness_roll = document.querySelector('button#wilderness_generate_button'); |
||||
|
wilderness_roll.addEventListener("click", generateWanderingWildernessMonster); |
||||
|
}); |
||||
|
|
||||
|
function toggleLocation(e) { |
||||
|
var dungeon_select = document.querySelector('button#dungeon_select'); |
||||
|
var wilderness_select = document.querySelector('button#wilderness_select'); |
||||
|
|
||||
|
var dungeon_content = document.querySelector('div#dungeon_content'); |
||||
|
var wilderness_content = document.querySelector('div#wilderness_content'); |
||||
|
|
||||
|
if(e.target.id === "dungeon_select") { |
||||
|
wilderness_content.classList.add("uk-hidden"); |
||||
|
dungeon_content.classList.remove("uk-hidden"); |
||||
|
|
||||
|
dungeon_select.classList.add("uk-button-secondary"); |
||||
|
dungeon_select.classList.remove("uk-button-default"); |
||||
|
|
||||
|
wilderness_select.classList.add("uk-button-default"); |
||||
|
wilderness_select.classList.remove("uk-button-secondary"); |
||||
|
} |
||||
|
|
||||
|
if(e.target.id === "wilderness_select") { |
||||
|
dungeon_content.classList.add("uk-hidden"); |
||||
|
wilderness_content.classList.remove("uk-hidden"); |
||||
|
|
||||
|
wilderness_select.classList.add("uk-button-secondary"); |
||||
|
wilderness_select.classList.remove("uk-button-default"); |
||||
|
|
||||
|
dungeon_select.classList.add("uk-button-default"); |
||||
|
dungeon_select.classList.remove("uk-button-secondary"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function generateWanderingDungeonMonster() { |
||||
|
resetOutputAreas(); |
||||
|
|
||||
|
var dungeon_level_select = document.querySelector('select#dungeon_level'); |
||||
|
var surprise_mod = document.querySelector('input#surprise_mod'); |
||||
|
var reaction_mod = document.querySelector('input#reaction_mod'); |
||||
|
var check_mod = document.querySelector('input#check_mod'); |
||||
|
|
||||
|
var payload = { |
||||
|
type: "dungeon", |
||||
|
dungeon_level: parseInt(dungeon_level_select.value), |
||||
|
surprise_mod: parseInt(surprise_mod.value) || 0, |
||||
|
reaction_mod: parseInt(reaction_mod.value) || 0, |
||||
|
check_mod: parseInt(check_mod.value) || 0 |
||||
|
} |
||||
|
|
||||
|
fetch("{{ url_for('default_views.generate_wandering_monster') }}", { |
||||
|
method: 'POST', |
||||
|
headers: {'Content-Type': 'application/json'}, |
||||
|
body: JSON.stringify(payload) |
||||
|
}).then(response => response.json()).then(populateDungeonResponse); |
||||
|
} |
||||
|
|
||||
|
function generateWanderingWildernessMonster() { |
||||
|
resetOutputAreas(); |
||||
|
|
||||
|
var terrain_type_select = document.querySelector('select#terrain_type'); |
||||
|
var surprise_mod = document.querySelector('input#surprise_mod'); |
||||
|
var reaction_mod = document.querySelector('input#reaction_mod'); |
||||
|
var check_mod = document.querySelector('input#check_mod'); |
||||
|
|
||||
|
var payload = { |
||||
|
type: "wilderness", |
||||
|
terrain_type: parseInt(terrain_type_select.value), |
||||
|
surprise_mod: parseInt(surprise_mod.value) || 0, |
||||
|
reaction_mod: parseInt(reaction_mod.value) || 0, |
||||
|
check_mod: parseInt(check_mod.value) || 0 |
||||
|
} |
||||
|
|
||||
|
fetch("{{ url_for('default_views.generate_wandering_monster') }}", { |
||||
|
method: 'POST', |
||||
|
headers: {'Content-Type': 'application/json'}, |
||||
|
body: JSON.stringify(payload) |
||||
|
}).then(response => response.json()).then(populateWildernessResponse); |
||||
|
} |
||||
|
|
||||
|
function resetOutputAreas() { |
||||
|
document.querySelector('div#dungeon_output').classList.remove('uk-animation-fade'); |
||||
|
document.querySelector('div#wilderness_output').classList.remove('uk-animation-fade'); |
||||
|
|
||||
|
document.querySelector('div#dungeon_output').classList.add('uk-hidden'); |
||||
|
document.querySelector('div#wilderness_output').classList.add('uk-hidden'); |
||||
|
} |
||||
|
|
||||
|
function populateDungeonResponse(data) { |
||||
|
var result_flag = document.querySelector('div#dungeon_output div#roll_result'); |
||||
|
|
||||
|
// If we didn't get a Monster back |
||||
|
if(data.check) { |
||||
|
result_flag.innerText = `Danger! The party has encountered a foe! (${data.check_roll})`; |
||||
|
|
||||
|
result_flag.classList.remove('uk-text-success'); |
||||
|
result_flag.classList.add('uk-text-warning'); |
||||
|
|
||||
|
// Populate data-points |
||||
|
document.querySelector('div#dungeon_output #wm_entity').innerText = data.entity; |
||||
|
document.querySelector('div#dungeon_output #wm_monster_table').innerText = data.monster_table; |
||||
|
document.querySelector('div#dungeon_output #wm_dungeon_level').innerText = data.dungeon_level; |
||||
|
document.querySelector('div#dungeon_output #wm_quantity').innerText = data.quantity; |
||||
|
document.querySelector('div#dungeon_output #wm_adjust').innerText = `${data.roll} (${data.initial}) / ${data.adjustment}`;; |
||||
|
document.querySelector('div#dungeon_output #wm_distance').innerText = data.distance + '\'';; |
||||
|
document.querySelector('div#dungeon_output #wm_surprise').innerText = `${data.surprise[0]} (${data.surprise[1]})`; |
||||
|
document.querySelector('div#dungeon_output #wm_reaction').innerText = `${data.reaction[0]} (${data.reaction[1]})`; |
||||
|
|
||||
|
document.querySelector('div#dungeon_output > div.uk-card').classList.remove('uk-hidden'); |
||||
|
} else { |
||||
|
result_flag.innerText = `Safety! The party hasn't encountered a monster... this time. (${data.check_roll})`; |
||||
|
|
||||
|
result_flag.classList.remove('uk-text-warning'); |
||||
|
result_flag.classList.add('uk-text-success'); |
||||
|
|
||||
|
document.querySelector('div#dungeon_output > div.uk-card').classList.add('uk-hidden'); |
||||
|
} |
||||
|
|
||||
|
// Unhide the appropriate content box |
||||
|
document.querySelector('div#wilderness_output').classList.add('uk-hidden'); |
||||
|
document.querySelector('div#dungeon_output').classList.remove('uk-hidden'); |
||||
|
|
||||
|
// Hit a fancy animation |
||||
|
document.querySelector('div#dungeon_output').classList.add('uk-animation-fade'); |
||||
|
|
||||
|
// Reset the form to prepare for next values |
||||
|
document.querySelector('form#wandering_form').reset(); |
||||
|
} |
||||
|
|
||||
|
function populateWildernessResponse(data) { |
||||
|
var result_flag = document.querySelector('div#wilderness_output div#roll_result'); |
||||
|
|
||||
|
// Did we get a monster returned? |
||||
|
if(data.check) { |
||||
|
result_flag.innerText = `Danger! The party has encountered a foe! (${data.check_roll})`; |
||||
|
|
||||
|
result_flag.classList.remove('uk-text-success'); |
||||
|
result_flag.classList.add('uk-text-warning'); |
||||
|
|
||||
|
document.querySelector('#wilderness_output h3#wm_entity').innerText = data.entity; |
||||
|
document.querySelector('#wilderness_output span#wm_surprise').innerText = `${data.surprise[0]} (${data.surprise[1]})`; |
||||
|
document.querySelector('#wilderness_output span#wm_reaction').innerText = `${data.reaction[0]} (${data.reaction[1]})`; |
||||
|
document.querySelector('#wilderness_output span#wm_monster_table').innerText = data.monster_table; |
||||
|
document.querySelector('#wilderness_output span#wm_terrain_type').innerText = data.terrain_type; |
||||
|
|
||||
|
document.querySelector('div#wilderness_output > div.uk-card').classList.remove('uk-hidden'); |
||||
|
} else { |
||||
|
result_flag.innerText = `Safety! The party hasn't encountered a monster... this time. (${data.check_roll})`; |
||||
|
|
||||
|
result_flag.classList.remove('uk-text-warning'); |
||||
|
result_flag.classList.add('uk-text-success'); |
||||
|
|
||||
|
document.querySelector('div#wilderness_output > div.uk-card').classList.add('uk-hidden'); |
||||
|
} |
||||
|
|
||||
|
// Unhide the appropriate content box |
||||
|
document.querySelector('div#dungeon_output').classList.add('uk-hidden'); |
||||
|
document.querySelector('div#wilderness_output').classList.remove('uk-hidden'); |
||||
|
|
||||
|
// Hit a fancy animation |
||||
|
document.querySelector('div#wilderness_output').classList.add('uk-animation-fade'); |
||||
|
|
||||
|
// Reset the form to prepare for next values |
||||
|
document.querySelector('form#wandering_form').reset(); |
||||
|
} |
||||
|
</script> |
||||
|
{% endblock %} |
@ -0,0 +1,267 @@ |
|||||
|
import math |
||||
|
import random |
||||
|
|
||||
|
import dice |
||||
|
|
||||
|
|
||||
|
|
||||
|
DUNGEON_WANDERING_MONSTER_LEVEL = { |
||||
|
# 1 2 3 4 5 6 7 8 9 0 1 2 |
||||
|
1: [1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 3], |
||||
|
2: [1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 4], |
||||
|
3: [1, 2, 2, 3, 3, 3, 3, 3, 3, 4, 4, 5], |
||||
|
4: [2, 3, 3, 4, 4, 4, 4, 4, 4, 5, 5, 6], |
||||
|
5: [3, 4, 4, 5, 5, 5, 5, 5, 5, 6, 6, 6], |
||||
|
6: [4, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6], |
||||
|
} |
||||
|
|
||||
|
DUNGEON_RANDOM_MONSTERS_BY_LEVEL = { |
||||
|
1: [ |
||||
|
("Goblin", "2d4"), ("Kobold", "4d4"), ("Morlock", "1d12"), |
||||
|
("Orc", "2d4"), ("Beetle, Fire", "1d8"), ("Centipede, Giant", "2d4"), |
||||
|
("Ferret, Giant", "1d8"), ("Rat, Giant", "3d6"), ("Men, Brigand", "2d4"), |
||||
|
("Skeleton", "3d4"), ("Stirge", "1d10"), ("NPC Party (Lvl 1)", "1d4+2") |
||||
|
], |
||||
|
2: [ |
||||
|
("Gnoll", "1d6"), ("Hobgoblin", "1d6"), ("Lizardman", "2d4"), |
||||
|
("Troglodyte", "1d8"), ("Bat, Giant", "1d10"), |
||||
|
("Fly, Giant Carnivorous", "1d8"), ("Locust, Cavern", "1d10"), |
||||
|
("Snake, Pit Viper", "1d8"), ("Ghoul", "1d6"), ("Men, Berserker", "1d6"), |
||||
|
("Zombie", "2d4"), ("NPC Party (Lvl 2)", "1d4+2") |
||||
|
], |
||||
|
3: [ |
||||
|
("Bugbear", "2d4"), ("Lycanthrope, Werewolf", "1d6"), ("Ogre", "1d6"), |
||||
|
("Throghrin", "1d6"), ("Ant, Giant", "2d4"), ("Lizard, Drago", "1d3"), |
||||
|
("Scorpion, Giant", "1d6"), ("Wolf, Dire", "1d4"), |
||||
|
("Carcass Scavenger", "1d3"), ("Gargoyle", "1d6"), ("Wight", "1d6"), |
||||
|
("NPC Party (Lvl 4)", "1d4+2") |
||||
|
], |
||||
|
4: [ |
||||
|
("Lycanthrope, Werebear", "1d4"), ("Lycanthrope, Weretiger", "1d4"), |
||||
|
("Minotaur", "1d6"), ("Boar, Giant", "1d4"), ("Owl Bear", "1d4"), |
||||
|
("Phase Tiger", "1d4"), ("Rhagodessa, Giant", "1d4"), |
||||
|
("Snake, Giant Python", "1d4"), ("Cockatrice", "1d4"), ("Medusa", "1d3"), |
||||
|
("Wraith", "1d4"), ("NPC Party (Lvl 5)", "1d4+2") |
||||
|
], |
||||
|
5: [ |
||||
|
("Ettin", "1d2"), ("Giant, Hill", "1d4"), ("Giant, Stone", "1d2"), |
||||
|
("Troll", "1d8"), ("Ankheg", "1d6"), ("Caecilian", "1d3"), |
||||
|
("Basilisk", "1d6"), ("Hell Hound, Greater", "2d4"), |
||||
|
("Salamander, Flame", "1d4+1"), ("Spectre", "1d4"), ("Wyvern", "1d2"), |
||||
|
("NPC Party (Lvl 8)", "1d4+3") |
||||
|
], |
||||
|
6: [ |
||||
|
("Cyclops", "1`"), ("Giant, Cloud", "1d2"), ("Purple Worm", "1d2"), |
||||
|
("Demon Boar", "1d4"), ("Dragon (20 HD)", "1"), ("Hydra (12 HD)", "1"), |
||||
|
("Gorgon", "1d2"), ("Lamia", "1"), ("Remorhaz (15 HD)", "1"), |
||||
|
("Skittering Maw", "1"), ("Vampire (9 HD)", "1d4"), |
||||
|
("NPC Party (Lvl 14)", "1d4+3") |
||||
|
] |
||||
|
} |
||||
|
|
||||
|
WILDERNESS_TYPE_BY_ID = { |
||||
|
1: ["Clear", "Grass", "Scrub"], |
||||
|
2: ["Woods"], |
||||
|
3: ["River"], |
||||
|
4: ["Swamp"], |
||||
|
5: ["Mountains", "Hills"], |
||||
|
6: ["Barren", "Desert"], |
||||
|
7: ["Inhabited"], |
||||
|
8: ["City"], |
||||
|
9: ["Ocean"], |
||||
|
10: ["Jungle"], |
||||
|
} |
||||
|
|
||||
|
WILDERNESS_FREQUENCY_BY_TERRAIN = { |
||||
|
1: 6, # Clear, Grass, Scrub |
||||
|
2: 5, # Woods |
||||
|
3: 5, # River |
||||
|
4: 4, # Swamp |
||||
|
5: 4, # Mountains, Hills |
||||
|
6: 4, # Barren, Desert |
||||
|
7: 6, # Inhabited |
||||
|
8: 6, # City |
||||
|
9: 5, # Ocean |
||||
|
10: 4, # Jungle |
||||
|
} |
||||
|
|
||||
|
WILDERNESS_ENCOUNTER_BY_TERRAIN = { |
||||
|
1: ["Men", "Flyer", "Humanoid", "Animal", "Animal", "Unusual", "Dragon", "Insect"], |
||||
|
2: ["Men", "Flyer", "Humanoid", "Insect", "Unusual", "Animal", "Animal", "Dragon"], |
||||
|
3: ["Men", "Flyer", "Humanoid", "Insect", "Swimmer", "Swimmer", "Animal", "Dragon"], |
||||
|
4: ["Men", "Flyer", "Humanoid", "Swimmer", "Undead", "Undead", "Insect", "Dragon"], |
||||
|
5: ["Men", "Flyer", "Humanoid", "Unusual", "Animal", "Humanoid", "Dragon", "Flyer"], |
||||
|
6: ["Men", "Flyer", "Humanoid", "Humanoid", "Animanl", "Dragon", "Undead", "Unusual"], |
||||
|
7: ["Men", "Flyer", "Humanoid", "Men", "Men", "Insect", "Animal", "Dragon"], |
||||
|
8: ["Men", "Undead", "Humanoid", "Men", "Men", "Men", "Men", "Men", "Men", "Men"], |
||||
|
9: ["Men", "Flyer", "Insect", "Insect", "Humanoid", "Animal", "Animal", "Dragon"], |
||||
|
10: ["Men", "Flyer", "Insect", "Insect", "Humanoid", "Animal", "Animal", "Dragon"], |
||||
|
} |
||||
|
|
||||
|
WILDERNESS_ENCOUNTER_SUBTABLES = { |
||||
|
"Men": { |
||||
|
"Clear, Grass, Scrub": ["Brigand", "Noble", "Mage", "Fighter", "Thief", "Cleric", "Nomad", "Thief", "NPC Party", "Merchant", "Berserker", "Venturer"], |
||||
|
"Woods": ["Brigand", "Thief", "NPC Party", "Merchant", "Berserker", "Brigand", "Cleric", "Mage", "Fighter", "Thief", "Brigand", "NPC Party"], |
||||
|
"River": ["Brigand", "Thief", "NPC Party", "Merchant", "Buccaneer", "Buccaneer", "Cleric", "Mage", "Fighter", "Merchant", "Buccaneer", "NPC Party"], |
||||
|
"Swamp": ["Brigand", "Thief", "NPC Party", "NPC Party", "Merchant", "Cleric", "Venturer", "Berserker", "Fighter", "Mage", "NPC Party", "Thief"], |
||||
|
"Barrens, Mountains, Hills": ["Brigand", "Thief", "NPC Party", "Venturer", "Berserker", "NPC Party", "Cleric", "Mage", "Fighter", "Brigand", "NPC Party", "Barbarian"], |
||||
|
"Desert": ["Nomad", "Nomad", "NPC Party", "Merchant", "Nomad", "Nomad", "Cleric", "Mage", "Fighter", "Noble", "Nomad", "Nomad"], |
||||
|
"Inhabited": ["Thief", "Venturer", "NPC Party", "NPC Party", "Merchant", "Fighter", "Merchant", "Fighter", "Mage", "Cleric", "Cleric", "Noble"], |
||||
|
"City": ["Thief", "Venterer", "NPC Party", "NPC Party", "Merchant", "Fighter", "Merchant", "Fighter", "Mage", "Cleric", "Cleric", "Noble"], |
||||
|
"Ocean": ["Buccaneer", "Pirate", "Merchant", "NPC Party", "Pirate", "Merchant", "Merchant", "Merchant", "Buccaneer", "Pirate", "Merchant", "Pirate"], |
||||
|
"Jungle": ["Brigand", "Venturer", "Thief", "NPC Party", "Cleric", "Fighter", "Medium", "Berserker", "Brigand", "Barbarian", "NPC Party", "Brigand"], |
||||
|
}, |
||||
|
"Humanoid": { |
||||
|
"Clear, Grass, Scrub": ["Bugbear", "Elf", "Gnoll", "Goblin", "Halfling", "Hobgoblin", "Kobold", "Ogre", "Orc", "Pixie", "Throghrin", "Troll"], |
||||
|
"Woods": ["Bugbear", "Cyclops", "Dryad", "Elf", "Giant, Hill", "Gnoll", "Goblin", "Hobgoblin", "Ogre", "Orc", "Pixie", "Troll"], |
||||
|
"River": ["Bugbear", "Elf", "Gnoll", "Hobgoblin", "Lizard Man", "Lizard Man", "Naiad", "Naiad", "Ogre", "Orc", "Sprite", "Troll"], |
||||
|
"Swamp": ["Gnoll", "Goblin", "Hobgoblin", "Lizard Man", "Lizard Man", "Lizard Man", "Naiad", "Ogre", "Orc", "Troglodyte", "Troll", "Troll"], |
||||
|
"Hills, Mountain": ["Dwarf", "Giant, Cloud", "Giant, Frost", "Giant, Hill", "Giant, Stone", "Giant, Storm", "Goblin", "Kobold", "Ogre", "Orc", "Troglodyte", "Troll"], |
||||
|
"Barrens": ["Bugbear", "Giant, Hill", "Goblin", "Gnoll", "Hobgoblin", "Hobgoblin", "Ogre", "Orc", "Orc", "Throghrin", "Troll"], |
||||
|
"Desert": ["Bugbear", "Gnoll", "Giant, Fire", "Hobgoblin", "Hobgoblin", "Minotaur", "Ogre", "Ogre", "Orc", "Orc", "Troll", "Troll"], |
||||
|
"City": ["Doppelganger", "Dwarf", "Elf", "Gnome", "Halfling", "Pixie", "Sprite", "Werebear", "Wereboar", "Wererat", "Weretiger", "Werewolf"], |
||||
|
"Inhabited": ["Doppelganger", "Dwarf", "Elf", "Gnome", "Goblin", "Halfling", "Kobold", "Ogre", "Orc", "Pixie", "Sprite", "Wererat"], |
||||
|
"Jungle": ["Bugbear", "Cyclops", "Elf", "Giant, Fire", "Giant, Hill", "Gnoll", "Goblin", "Lizard Man", "Ogre", "Orc", "Troglodyte", "Troll"], |
||||
|
}, |
||||
|
"Animal": { |
||||
|
"Clear, Grass, Scrub": ["Herd Animal (Antelope)", "Boar*", "Cat, Lion", "Cat, Panther", "Elephant", "Hawk*", "Horse, Light", "Lizard, Tuatara", "Mule (Donkey)", "Snake, Pit Viper", "Snake, Giant Rattler", "Weasel, Giant"], |
||||
|
"Woods": ["Herd Animal (Antelope)", "Bat*", "Bear, Grizzly", "Boar*", "Cat, Panther", "Hawk*", "Owl*", "Snake, Pit Viper", "Spider, Black Widow", "Unicorn", "Wolf", "Wolf, Dire"], |
||||
|
"River": ["Herd Animal (Antelope)", "Bear, Black", "Boar*", "Cat, Panther", "Crab, Giant", "Crocodile*", "Leech, Giant", "Piranha, Giant", "Rat*", "Shrew, Giant", "Swan", "Toad, Giant"], |
||||
|
"Hills": ["Herd Animal (Antelope)", "Bear, Grizzly", "Boar*", "Cat, Mountain Lion", "Eagle*", "Hawk*", "Horse, Light", "Herd Animal (Sheep)", "Snake, Pit Viper", "Owl*", "Wolf", "Wolf, Dire"], |
||||
|
"Mountains": ["Herd Animal (Antelope)", "Bear, Cave", "Cat, Mountain Lion", "Eagle*", "Herd Animal (Goat)", "Hawk*", "Mule (Donkey)", "Baboon, Rock", "Snake, Pit Viper", "Snake, Giant Rattler", "Wolf", "Wolf, Dire"], |
||||
|
"Barrens": ["Herd Animal (Antelope)", "Bear, Cave", "Cat, Mountain Lion", "Eagle*", "Goat, Wild", "Hawk*", "Rock Babooon", "Snake, Pit Viper", "Snake, Giant Rattler", "Spider, Crab", "Wolf, Dire", "Vulture"], |
||||
|
"Desert": ["Herd Animal (Antelope)", "Herd Animal (Antelope)", "Camel", "Camel", "Cat, Lion", "Hawk*", "Lizard, Gecko", "Lizard, Tuatara", "Snake, Giant Rattler", "Wolf (Wild Dog)", "Wolf, Dire", "Vulture"], |
||||
|
"Inhabited": ["Herd Animal (Antelope)", "Boar*", "Cat, Panther", "Lizard, Draco", "Lizard, Gecko", "Lizard, Horned", "Monkey", "Shrew, Giant", "Snake, Pit Viper", "Snake, Python", "Snake, Spitting Cobra", "Spider, Crab"], |
||||
|
"Jungle": ["Goat", "Boar*", "Dog", "Ferret, Giant", "Hawk*", "Horse, Light", "Mule (Donkey)", "Rat*", "Snake, Pit Viper", "Herd Animal (Sheep)", "Weasel, Giant", "Wolf"], |
||||
|
"Other": ["Bear, Cave", "Cat, Sabre-Tooth Tiger", "Crocodile, Giant", "Mastodon", "Pteranodon", "Rhino, Woolly", "Snake, Python", "Stegosaurus", "Titanothere", "Triceratops", "Tyrannosaurus Rex", "Wolf, Dire"], |
||||
|
"*": "Roll 1d6: 1-4 Normal or Swarm, 5-6 Giant" |
||||
|
}, |
||||
|
"Flyer": { |
||||
|
"Barrens": ["Cockatrice", "Gargoyle", "Griffon", "Harpy", "Hawk, Giant", "Hippogriff", "Lammasu", "Manticore", "Pegasus", "Roc, Small", "Stirge", "Wyvern"], |
||||
|
"Desert": ["Chimera", "Cockatrice", "Gargoyle", "Griffon", "Hawk, Giant", "Lammasu", "Manticore", "Pterodactyl", "Roc, Small", "Sphinx", "Wyvern", "Vulture"], |
||||
|
"Mountains": ["Swarm, Bat", "Chimera", "Cockatrice", "Gargoyle", "Griffon", "Harpy", "Hawk, Giant", "Hippogriff", "Manticore", "Pegasus", "Roc*", "Wyvern"], |
||||
|
"Woods": ["Bat, Giant", "Swarm, Bat", "Cockatrice", "Griffon", "Hawk, Giant", "Hippogriff", "Pegasus", "Owl, Giant", "Pixie", "Roc, Small", "Sprite", "Stirge"], |
||||
|
"Other": ["Cockatrice", "Fly, Giant Carnivorous", "Gargoyle", "Griffon", "Hawk, Giant", "Hippogriff", "Bee, Killer Giant", "Pegasus", "Pixie", "Roc, Small", "Sprite", "Stirge"], |
||||
|
"*": "Roll 1d6: 1-3 Small, 4-5 Large, 6 Giant" |
||||
|
}, |
||||
|
"Swimmer": { |
||||
|
"River, Lake": ["Crab, Giant", "Crocodile", "Crocodile", "Crocodile, Large", "Fish, Catfish", "Fish, Piranha", "Fish, Sturgeon", "Leech, Giant", "Lizard Man", "Merman", "Naiad", "Skittering Maw"], |
||||
|
"Ocean": ["Dragon Turtle", "Hydra, Sea", "Merman", "Octopus, Giant", "Sea Dragon", "Sea Serpent", "Shark*", "Shark*", "Skittering Maw", "Snake, Sea", "Squid, Giant", "Whale"], |
||||
|
"Swamp": ["Crab, Giant", "Crocodile", "Crocodile, Large", "Crocodile, Giant", "Fish, Catfish", "Insect Swarm", "Insect Swawrm", "Leech, Giant", "Leech, Giant", "Lizard Man", "Lizard Man", "Skittering Maw"], |
||||
|
"*": "Roll 1d6: 1-2 Bull Shark, 3-4 Mako Shark, 5-6 Great White Shark" |
||||
|
}, |
||||
|
"Dragon": { |
||||
|
"Other": ["Basilisk", "Caecilian", "Chimera", "Dragon*", "Dragon*", "Sphinx", "Hydra/Sea Hydra", "Lamia", "Purple Worm", "Snake, Python", "Salamander", "Wyvern"], |
||||
|
"*": "Always Black in Swamp, Blue in Mountains, Brown in Desert, Green in Woods, Red in Barrens, Sea in Oceans/Rivers/Lake. Otherwise roll 1d10: 1 Black, 2 Blue, 3 Brown, 4 Green, 5 Metallic, 6 Red, 7 Sea, 8 White, 9 Wyrm, 10 Judge’s Choice.", |
||||
|
}, |
||||
|
"Insect": { |
||||
|
"Other": ["Beetle, Fire", "Beetle, Giant Bombardier", "Beetle, Tiger", "Carcass Scavenger", "Centipede, Giant", "Ant, Giant", "Fly, Giant Carnivorous", "Killer Bee", "Rhagodessa", "Scorpion, Giant", "Spider, Black Widow", "Spider, Crab"], |
||||
|
}, |
||||
|
"Undead": { |
||||
|
"Other": ["Ghoul", "Ghoul", "Mummy", "Mummy", "Skeleton", "Skeleton", "Spectre", "Wight", "Wraith", "Vampire", "Zombie", "Zombie"], |
||||
|
}, |
||||
|
"Unusual": { |
||||
|
"Other": ["Basilisk", "Blink Dog", "Centaur", "Gorgon", "Hellhound", "Lycanthrope", "Medusa", "Phase Tiger", "Rust Monster", "Skittering Maw", "Treant", "Ape, White"], |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
def surprise_roll(mod): |
||||
|
roll = int(dice.roll("1d6 + {}".format(mod))) |
||||
|
status = "Surprised" if roll < 3 else "Ready" |
||||
|
return (status, roll) |
||||
|
|
||||
|
def reaction_roll(mod, difference): |
||||
|
roll = int(dice.roll("2d6 + {}".format(mod))) |
||||
|
roll += difference |
||||
|
|
||||
|
if roll <= 2: |
||||
|
status = "Hostile, attacks" |
||||
|
elif roll >= 3 and roll <= 5: |
||||
|
status = "Unfriendly, may attack" |
||||
|
elif roll >= 6 and roll <= 8: |
||||
|
status = "Neutral, uncertain" |
||||
|
elif roll >= 9 and roll <= 11: |
||||
|
status = "Indifferent, uninterested" |
||||
|
elif roll >= 12: |
||||
|
status = "Friendly, helpful" |
||||
|
|
||||
|
return (status, roll) |
||||
|
|
||||
|
def check_encounter(mod=0, throw=6): |
||||
|
roll = int(dice.roll("1d6 + {}".format(mod))) |
||||
|
return ((roll >= throw), roll) |
||||
|
|
||||
|
def generate_dungeon_monster(check_mod, dungeon_level, reaction_mod, surprise_mod): |
||||
|
check = check_encounter(check_mod) |
||||
|
if not check[0]: |
||||
|
return {"check": False, "check_roll": check[1]} |
||||
|
|
||||
|
if not dungeon_level in DUNGEON_WANDERING_MONSTER_LEVEL: |
||||
|
return None |
||||
|
|
||||
|
monster_table = random.choice(DUNGEON_WANDERING_MONSTER_LEVEL[dungeon_level]) |
||||
|
encounter = random.choice(DUNGEON_RANDOM_MONSTERS_BY_LEVEL[monster_table]) |
||||
|
|
||||
|
difference = monster_table - dungeon_level |
||||
|
|
||||
|
quantity = int(dice.roll(encounter[1])) |
||||
|
adjusted = quantity |
||||
|
for _ in range(abs(difference)): |
||||
|
adjusted = adjusted + (adjusted * 0.5) if difference > 0 else adjusted - (adjusted * 0.5) |
||||
|
adjusted = math.floor(adjusted) if adjusted > 1 else 1 |
||||
|
|
||||
|
distance = int(dice.roll("2d6")) * 10 |
||||
|
surprise = surprise_roll(surprise_mod) |
||||
|
reaction = reaction_roll(reaction_mod, difference) |
||||
|
|
||||
|
return { |
||||
|
"dungeon_level": dungeon_level, |
||||
|
"monster_table": monster_table, |
||||
|
"entity": encounter[0], |
||||
|
"quantity": adjusted, |
||||
|
"initial": quantity, |
||||
|
"roll": encounter[1], |
||||
|
"adjustment": difference, |
||||
|
"distance": distance, |
||||
|
"surprise": surprise, |
||||
|
"reaction": reaction, |
||||
|
"check": True, |
||||
|
"check_roll": check[1] |
||||
|
} |
||||
|
|
||||
|
def generate_wilderness_monster(check_mod, terrain_type, reaction_mod, surprise_mod): |
||||
|
check_throw = WILDERNESS_FREQUENCY_BY_TERRAIN[terrain_type] |
||||
|
check = check_encounter(check_mod, check_throw) |
||||
|
if not check[0]: |
||||
|
return {"check": False, "check_roll": check[1]} |
||||
|
|
||||
|
encounter_type_roll = int(dice.roll("1d8")) |
||||
|
encounter_type = WILDERNESS_ENCOUNTER_BY_TERRAIN[terrain_type][encounter_type_roll - 1] |
||||
|
|
||||
|
subtable = WILDERNESS_ENCOUNTER_SUBTABLES[encounter_type] |
||||
|
terrain_type_labels = WILDERNESS_TYPE_BY_ID[terrain_type] |
||||
|
|
||||
|
endtable = None |
||||
|
for k in subtable.keys(): |
||||
|
for l in terrain_type_labels: |
||||
|
if l in k: |
||||
|
endtable = subtable[k] |
||||
|
if endtable == None and "Other" in subtable: |
||||
|
endtable = subtable["Other"] |
||||
|
|
||||
|
entity_roll = int(dice.roll("1d12")) |
||||
|
entity = endtable[entity_roll - 1] |
||||
|
|
||||
|
surprise = surprise_roll(surprise_mod) |
||||
|
reaction = reaction_roll(reaction_mod, 0) |
||||
|
|
||||
|
return { |
||||
|
"terrain_type": ', '.join(terrain_type_labels), |
||||
|
"monster_table": encounter_type, |
||||
|
"entity": entity, |
||||
|
"surprise": surprise, |
||||
|
"reaction": reaction, |
||||
|
"check": True, |
||||
|
"check_roll": check[1] |
||||
|
} |
@ -0,0 +1 @@ |
|||||
|
&{template:acks} {{name=Initiative}} {{roll=[[1d6+@{selected|initiative_mod} &{tracker}]]}} |
@ -0,0 +1 @@ |
|||||
|
&{template:acks} {{name=Character Stat Roll}} {{Strength = [[3d6]]}} {{Dexterity = [[3d6]]}} {{Intelligence = [[3d6]]}} {{Constitution = [[3d6]]}} {{Wisdom = [[3d6]]}} {{Charisma = [[3d6]]}} |
@ -0,0 +1,11 @@ |
|||||
|
?{Equipment Type| |
||||
|
Bladed Weapon, |
||||
|
&{template:acks}{{name=Scavenge Gear}}{{subheader=Bladed Weapon}}{{desc=[[1t[Scavenge-Blade]]]}} |
||||
|
| |
||||
|
Blunt Weapon, |
||||
|
&{template:acks}{{name=Scavenge Gear}}{{subheader=Blunt Weapon}}{{desc=[[1t[Scavenge-Blunt]]]}} |
||||
|
| |
||||
|
Armor and Equipment, |
||||
|
&{template:acks}{{name=Scavenge Gear}}{{subheader=Armour and Equipment}}{{desc=[[1t[Scavenge-Other]]]}} |
||||
|
} |
||||
|
|
Write
Preview
Loading…
Cancel
Save
Reference in new issue