Browse Source

Significantly reorganized Rules/Campaigns

master
Graham Northup 3 years ago
parent
commit
6ad04a6c26
Signed by untrusted user: grissess GPG Key ID: 5D000E6F539376FB
  1. 5
      Rules/Special/archive_spells_unknown.txt
  2. 2
      Rules/Special/construction.txt
  3. 2
      Rules/Special/downtime.txt
  4. 5
      Rules/Special/log_inspiration.txt
  5. 10
      Rules/Special/no_draconic_traits.txt
  6. 2
      Rules/Special/tame_wilderness.txt
  7. 6
      Rules/Special/west_marches_travel_rules.txt
  8. 6
      Rules/Variant/chargen_reroll_ones.txt
  9. 4
      Rules/Variant/con_hp_stacking.txt
  10. 14
      Rules/Variant/natural_checks.txt
  11. 6
      Rules/Variant/perfect_plane_shift.txt
  12. 8
      Rules/Variant/semi_supernatural_breath_weapons.txt
  13. 6
      Rules/Variant/teleport_nerf.txt
  14. 4
      Rules/Variant/triple_natural_one_defecation.txt
  15. 7
      Rules/archive_spells.txt
  16. 11
      Rules/konis.txt
  17. 7
      Rules/log_inspiration.txt
  18. 6
      Rules/natones.txt
  19. 9
      Rules/teleport_nerf.txt
  20. 11
      Rules/west_marches.txt
  21. 2
      app/get_last_n_items.sh
  22. 2
      app/new_content.txt
  23. 11
      app/routes.py
  24. 96
      app/rules.py
  25. 22
      app/static/style.css
  26. 8
      app/templates/campaigns.html
  27. 2
      app/templates/index.html
  28. 29
      app/templates/macros.html
  29. 11
      app/templates/rules.html

5
Rules/Special/archive_spells_unknown.txt

@ -0,0 +1,5 @@
Archive Spells Unknown
----------------------
Any spells on this website are not available to the players until discovered in the world, unless stated otherwise.
Spells on this website that are modifications to core 5e spells (such as the Time Stop variant here) are available as normal in their modified form, and the "regular" 5e variant simply does not exist.

2
Rules/construction.txt → Rules/Special/construction.txt

@ -1,8 +1,6 @@
Construction
------------
Campaigns: West Marches
Construction can be ordered by the party and will progress during downtime. There are five types of construction.
Military - Has a sphere of influence and a combat rating. Within the radius travelable in the specific time, the military will prevent encounters that give less xp than its combat rating. If all encounters for an area are prevented, the area is made Safe. Can be constructed anywhere; see the military construction table in the campaign info document.

2
Rules/downtime.txt → Rules/Special/downtime.txt

@ -1,8 +1,6 @@
Downtime
--------
Campaigns: West Marches
When time advances and a character is not on a mission, they can participate in a downtime activity. Downtime activities are as follows:
Work - You gain 2d8 times proficiency gold for each day worked.

5
Rules/Special/log_inspiration.txt

@ -0,0 +1,5 @@
Log Inspiration
---------------
After each session, each player should write up a log (~1 paragraph, can be longer) detailing what their character did/discovered on the mission.
Once the log is written and on this website, said player will have one inspiration to use on their next mission.

10
Rules/Special/no_draconic_traits.txt

@ -0,0 +1,10 @@
No Draconic Traits
==================
All dragons of Aelin are believed to be locally extinct, thus no party starting on Aelin should have any kind of "draconic" trait; e.g.:
- The Draconic language is extremely esoteric and unlikely to be known casually;
- Class features which target dragons (e.g., Ranger's Favored Enemy) or are based on dragons (e.g., Sorcerer's Draconic Bloodline) are generally unavailable except in extenuating circumstances;
- Dragonborn, half-dragons, and other dragon-derived races are heavily advised against.

2
Rules/tame_wilderness.txt → Rules/Special/tame_wilderness.txt

@ -1,8 +1,6 @@
Tame Wilderness
---------------
Campaigns: West Marches
Areas of the map exist in one of three states.
Wild - The default state. Random encounters occur, and the area is unfit for civilized presence.

6
Rules/travel_rules.txt → Rules/Special/west_marches_travel_rules.txt

@ -1,7 +1,5 @@
Travel Rules
------------
Campaigns: West Marches
West Marches Travel Rules
-------------------------
Every location on the map has an (x,y) coordinate. Travel time is computed using the relevant travel time table in the campaign info document. The distance metric used is euclidean distance, for those who will ask.

6
Rules/Variant/chargen_reroll_ones.txt

@ -0,0 +1,6 @@
Character Generation: 4d6 Drop 1 With Reroll
============================================
For character generation, roll each ability score as 4d6, drop the lowest valued roll, and take the sum.
Any time you roll a 1 on a d6 in this way, you may--at your option--reroll that die. You can reroll the same die indefinitely (as long as it continues to produce 1).

4
Rules/Variant/con_hp_stacking.txt

@ -0,0 +1,4 @@
CON HP Stacking
===============
When calculating HP for a high-level character, use the present CON mod for all hit die rolls, for simplicity. If the CON mod changes (due to an Ability Score Improvement, for example), add (or subtract) your current level's worth of HP for each point it raises (or lowers).

14
Rules/Variant/natural_checks.txt

@ -0,0 +1,14 @@
Natural Checks
==============
In general, rolling a nat 20 for any saving throw or ability check is a critical success, regardless of modifiers. Rolling a nat 1 for any saving throw or ability check is a critical failure, regardless of modifiers.
The definition of "critical" with regard to a save or check is up to interpretation, for example:
- A critical success on a DEX SAV for half damage may result in no damage;
- A critical failure on a DEX SAV may cause double damage;
- A critical success on a Perception check will reveal all possible percepts with relevant senses (at the highest DC);
- A critical success on an INT-based check will reveal all useful information (e.g., critical success Arcana may be as powerful as Identify);
- A critical failure on an INT-based check may result in believable utter fabrication;
...and so forth.
The DM may decide, even on a nat 20, that a success is utterly impossible given the circumstances.

6
Rules/Variant/perfect_plane_shift.txt

@ -0,0 +1,6 @@
Perfect Plane Shift
-------------------
Clerics, Warlocks, Druids, Sorcerers, and Wizards at (or sufficiently near) level 20, and familiar with the destination (as would be determined by the familiarity table of Teleport) can use two consecutive actions and two casts of Plane Shift to teleport to a familiar destination on the same plane. This teleportation occurs without error.
The Plane Shifting creature must pass through a different plane, the "transitory plane", to achieve the full teleport. Clerics and Warlocks typically pass through the plane of their deity without incident. Druids, Sorcerers, and Wizards may earn some ire by transiting through the domain of some powerful, unknown source; roll a d20, on 1: the Plane Shift succeeds, but invokes the wrath of some extraplanar entity (e.g., a devil of Baator, a Githyanki invasion, a deity themself) in doing so. The exact consequences depend on the entity, but can range from a smoldering enmity to an immediate attack--potentially using the Plane Shifters own portal.

8
Rules/Variant/semi_supernatural_breath_weapons.txt

@ -0,0 +1,8 @@
Semi-Supernatural Breath Weapons
--------------------------------
Whereas 5e considers them extraordinary, and 3.5e considers them supernatural, consider breath weapons "semi-supernatural" in the following sense:
A dragon's breath weapon requires the same freedom as is required to cast a V-only spell, except there is no actual V component, and thus magical silence does not interrupt it. It does, however, require an unobstructed mouth, so a gag, muzzle, or other such binding will cause it to fail. Effects which prevent a spellcaster from casting at all also cause it to fail.
The area of effect of a breath weapon is non-magical elemental energy, and is not affected by, e.g., Antimagic Field.

6
Rules/Variant/teleport_nerf.txt

@ -0,0 +1,6 @@
Teleport Nerf
-------------
Teleportation can only target permanent teleport circles that you know the sigil sequence for. Short range teleportation (dimension door, misty step, etc) is unaffected by this rule. Plane Shift works, but is notoriously inaccurate (you could end up virtually anywhere).
Gate (and only Gate) can be used to teleport anywhere.

4
Rules/Variant/triple_natural_one_defecation.txt

@ -0,0 +1,4 @@
Triple Natural 1 Defecation
===========================
If a player rolls 3 natural 1s consecutively, their character shits themself.

7
Rules/archive_spells.txt

@ -1,7 +0,0 @@
Archive Spells
--------------
Campaigns: West Marches
Any spells on this website are not available to the players until discovered in the world, unless stated otherwise.
Spells on this website that are modifications to canon spells (such as the Time Stop variant here) are available as normal in their modified form, and do not exist as they do in canon.

11
Rules/konis.txt

@ -0,0 +1,11 @@
Konis/Aelin
===========
Special: No Draconic Traits
Variant: Perfect Plane Shift
Variant: Natural Checks
Variant: CON HP Stacking
Variant: Semi-Supernatural Breath Weapons
Konis is the continent for C1-C3, and Aelin is the continent for upcoming C4.

7
Rules/log_inspiration.txt

@ -1,7 +0,0 @@
Log Inspiration
---------------
Campaigns: West Marches
After each session, each player will write up a log (~1 paragraph, can be longer) detailing what their character did/discovered on the mission.
Once the log is written and on this website, said player will have one inspiration to use on their next mission.

6
Rules/natones.txt

@ -1,6 +0,0 @@
Three Natural 1s
--------
Campaigns: misc
If a player rolls 3 natural 1s consecutively their character shits them self.

9
Rules/teleport_nerf.txt

@ -1,9 +0,0 @@
Teleport Travel Nerf
--------------------
Campaigns: West Marches
Teleportation can only target permanent teleport circles that you know the code to.
Short range teleportation (dimension door, misty step, etc) is unaffected by this rule.
Gate can be used to teleport anywhere.

11
Rules/west_marches.txt

@ -0,0 +1,11 @@
West Marches
============
Special: Archive Spells Unknown
Special: Construction
Special: Downtime
Special: Log Inspiration
Special: Tame Wilderness
Special: West Marches Travel Rules
Variant: Teleport Nerf

2
app/get_last_n_items.sh

@ -1,5 +1,5 @@
#!/bin/bash
PATTERN="^(Items|Spells|Places|Monsters|Rules).*txt"
PATTERN="^(Items|Spells|Places|Monsters).*txt"
LIMIT=10
X=0
rm "new_content.txt"

2
app/new_content.txt

@ -1,3 +1,4 @@
Items/fractal_sponge.txt
Items/manite.txt
Spells/stasis.txt
Items/ring_of_enlargement_reduction.txt
@ -7,4 +8,3 @@ Items/anchored_mithril.txt
Items/firestorm_quartz.txt
Items/lunar_steel.txt
Spells/antidivinity_strike.txt
Items/glass_weapon.txt

11
app/routes.py

@ -6,6 +6,8 @@ from items import get_items, get_worlds, get_monsters, get_new_items
from dungeon import genGridDungeonB64
import rules
from random import choice, sample
import re
@ -151,7 +153,6 @@ bucket_route('places', 'Places')
bucket_route('characters', 'Characters')
bucket_route('spells', 'Spells')
bucket_route('feats', 'Feats')
bucket_route('rules', 'Rules')
@app.route("/monsters")
def render_monsters():
@ -162,6 +163,14 @@ def random_monster():
m = choice(get_monsters())
return redirect("/monsters#"+m.name)
@app.route("/campaigns")
def campaigns():
return render_template('campaigns.html', rules=rules)
@app.route("/rules")
def view_rules():
return render_template('rules.html', rules=rules)
@app.route("/dungeon")
def dungeon():
return render_template('dungeon.html')

96
app/rules.py

@ -0,0 +1,96 @@
import os, glob
def get_kinds():
basepath = '../Rules/'
return sorted([pth for pth in os.listdir(basepath) if os.path.isdir(os.path.join(basepath, pth))])
class Rule(object):
CACHE_FNAME = {}
CACHE_NAME = {}
@classmethod
def get_cached(cls, path):
ret = cls.CACHE_FNAME.get(path)
if not ret:
ret = cls(path)
cls.CACHE_FNAME[path] = ret
return ret
@classmethod
def get_named(cls, name):
return cls.CACHE_NAME.get(name)
def __init__(self, path):
try:
f = open(path, 'r')
except OSError:
self.name = 'Invalid Rule'
self.kind = 'Invalid Kind'
self.content = 'Non-existent rule ' + path
else:
self.kind = os.path.basename(os.path.dirname(path))
lines = f.read().split('\n')
self.name = lines.pop(0)
lines.pop(0) # Divider
while not lines[0]:
lines.pop(0)
self.content = '\n'.join(lines)
self.CACHE_NAME[self.name] = self
class ManualRule(object):
def __init__(self, name, content, kind):
self.name = name
self.content = content
self.kind = kind
self.invalid = True
def rules_of_kind(kind):
basepath = os.path.join('../Rules', kind)
for fn in os.listdir(basepath):
fullfn = os.path.join(basepath, fn)
if os.path.isfile(fullfn):
yield Rule.get_cached(fullfn)
def sorted_rules_of_kind(kind):
return sorted(rules_of_kind(kind), key=lambda ru: ru.name)
def get_rules():
for kind in get_kinds():
for rule in rules_of_kind(kind):
yield rule
class Campaign(object):
def __init__(self, path):
f = open(path, 'r')
lines = f.read().split('\n')
self.name = lines.pop(0)
lines.pop(0) # Divider
while not lines[0]:
lines.pop(0)
kinds = set(get_kinds())
self.content = []
self.rules = []
for line in lines:
kind, sep, name = line.partition(':')
name = name.strip()
if sep and kind in kinds:
rule = Rule.get_named(name)
if rule:
self.rules.append(rule)
else:
self.rules.append(ManualRule(name, 'Rule not found.', 'Invalid Kind'))
continue
self.content.append(line)
self.content = '\n'.join(self.content)
def get_campaigns():
list(get_rules()) # Populate cache
basepath = '../Rules'
for fn in os.listdir(basepath):
fullfn = os.path.join(basepath, fn)
if os.path.isfile(fullfn):
yield Campaign(fullfn)
def get_sorted_campaigns():
return sorted(get_campaigns(), key=lambda ca: ca.name)

22
app/static/style.css

@ -36,6 +36,11 @@ h4 {
margin-bottom: 0.5em;
}
.sec {
border-bottom: 1px solid #9f9;
color: #6c6;
}
nav {
font-size: 200%;
}
@ -170,3 +175,20 @@ table.rolls td, table.rolls th {
font-style: italic;
}
.rule, .campaign {
border: 1px solid #66c;
text-align: left;
padding: 0.5em;
margin-bottom: 2em;
}
.rule h3, .campaign h3 {
text-align: center;
margin-top: 0;
color: #99f;
}
dl.rules dd {
margin-left: 5ex;
font-size: 65%;
}

8
app/templates/campaigns.html

@ -0,0 +1,8 @@
{% extends "base.html" %}
{% block content %}
<h2><a href="/rules">Rules</a> &middot; Campaigns</h2>
{% for ca in rules.get_sorted_campaigns() %}
{{ macros.campaign(ca, rules) }}
{% endfor %}
<a class="topLink" href="#">Scroll to Top</a>
{% endblock %}

2
app/templates/index.html

@ -20,7 +20,7 @@
<li><a href="/spells">Spells</a></li>
<li><a href="/feats">Feats</a></li>
<li><a href="/monsters">Monsters</a></li>
<li><a href="/rules">Rules</a></li>
<li><a href="/rules">Rules</a> &middot; <a href="/campaigns">Campaigns</a></li>
</ul>
</nav>

29
app/templates/macros.html

@ -35,3 +35,32 @@
{% if ma.special %}{{ ma.special }}{% endif %}
</div>
{%- endmacro %}
{% macro campaign(ca, rules) -%}
<div class="campaign">
<h3>{{ca.name}}</h3>
<div class="rulesContainer">
{% for kind in rules.get_kinds() %}
<h4>{{kind}}</h4>
<div class="ruleKindContainer">
<dl class="rules">
{% for rule in ca.rules %}
{% if rule.invalid or rule.kind == kind %}
<dt><a href="/rules#{{rule.name}}">{{rule.name}}</a></dd>
<dd class="freeform monospace">{{rule.content}}</dd>
{% endif %}
{% endfor %}
</dl>
</div>
{% endfor %}
</div>
<span class="freeform monospace">{{ca.content}}</span>
</div>
{%- endmacro %}
{% macro rule(ru) -%}
<div class="rule" id="{{ru.name}}">
<h3>{{ru.name}}</h3>
<span class="freeform monospace">{{ru.content}}</span>
</div>
{%- endmacro %}

11
app/templates/rules.html

@ -0,0 +1,11 @@
{% extends "base.html" %}
{% block content %}
<h2>Rules &middot; <a href="/campaigns">Campaigns</a></h2>
{% for kind in rules.get_kinds() %}
<h3 id="{{kind}}" class="sec">{{kind}}</h2>
{% for ru in rules.sorted_rules_of_kind(kind) %}
{{ macros.rule(ru) }}
{% endfor %}
{% endfor %}
<a class="topLink" href="#">Scroll to Top</a>
{% endblock %}
Loading…
Cancel
Save