mirror of
https://github.com/Threnklyn/dungeon-sheets.git
synced 2026-05-18 12:13:27 +02:00
Merge branch 'master' into Companions-and-Weight
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
name: Python package
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
python-version: ['3.6', '3.7', '3.8', '3.9', '3.10']
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up required system dependencies
|
||||
run: sudo apt-get -y install pdftk texlive-latex-base texlive-latex-extra texlive-fonts-recommended texlive-fonts-extra
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v4.0.0
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
cache: pip
|
||||
- name: Install dependencies and do a local pip install
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install -r requirements.txt
|
||||
pip install -r requirements-tests.txt
|
||||
pip install -e .
|
||||
- name: Install DnD LaTeX styles
|
||||
run: |
|
||||
echo "$(kpsewhich -var-value TEXMFHOME)/tex/latex/"
|
||||
mkdir -p "$(kpsewhich -var-value TEXMFHOME)/tex/latex/"
|
||||
git clone https://github.com/rpgtex/DND-5e-LaTeX-Template.git "$(kpsewhich -var-value TEXMFHOME)/tex/latex/dnd"
|
||||
- name: Run flake
|
||||
run: flake8 dungeonsheets/ --exit-zero
|
||||
- name: Run tests
|
||||
run: >
|
||||
cd examples/;
|
||||
makesheets --debug;
|
||||
makesheets --debug --fancy;
|
||||
makesheets --debug --output-format=epub;
|
||||
cd ../;
|
||||
pytest --cov=dungeonsheets tests/
|
||||
@@ -78,6 +78,73 @@ IP violations.
|
||||
How to Contribute
|
||||
=================
|
||||
|
||||
Running Tests
|
||||
-------------
|
||||
|
||||
Dungeonsheets uses tests to verify the package works as
|
||||
intended. Tests are found in the ``tests/`` folder. To run the tests
|
||||
using *pytest*, run the following from a console:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
pip install -r requirements.txt -r requirements-tests.txt
|
||||
pytest
|
||||
|
||||
You can also run a sub-set of the tests, which can be convenient for
|
||||
development. For example, to run just the tests related to dice
|
||||
mechanics, use ``pytest tests/test_dice.py``. Dungeonsheets defines
|
||||
tests using the *unittest* package in the standard library. **For
|
||||
example**, to test a new function in the ``dungeonsheets/dice.py``
|
||||
module, modify ``tests/test_dice.py``:
|
||||
|
||||
.. code-block:: python
|
||||
:caption: dice.py
|
||||
|
||||
def roll(a, b=None):
|
||||
"""roll(20) means roll 1d20, roll(2, 6) means roll 2d6"""
|
||||
if b is None:
|
||||
return random.randint(1, a)
|
||||
else:
|
||||
return sum([random.randint(1, b) for _ in range(a)])
|
||||
|
||||
.. code-block:: python
|
||||
:caption: test_dice.py
|
||||
|
||||
from unittest import TestCase
|
||||
from dungeonsheets.dice import roll
|
||||
|
||||
class TestDice(TestCase):
|
||||
def test_simple_rolling(self):
|
||||
num_tests = 100
|
||||
# Do a bunch of rolls and make sure the numbers are within the requsted range
|
||||
for _ in range(num_tests):
|
||||
result = roll(6)
|
||||
self.assertGreaterEqual(result, 1)
|
||||
self.assertLessEqual(result, 6)
|
||||
|
||||
def test_multi_rolling(self):
|
||||
num_tests = 100
|
||||
for _ in range(num_tests):
|
||||
result = roll(2, 4) # Roll 2d4
|
||||
self.assertGreaterEqual(result, 2)
|
||||
self.assertLessEqual(result, 8)
|
||||
|
||||
|
||||
Building Documentation
|
||||
----------------------
|
||||
|
||||
Dungeonsheets uses sphinx to build documentations. All files are in
|
||||
reStructuredText and are kept in the ``docs/`` folder. To build the
|
||||
HTML files, run:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
pip install -r requirements.txt -r requirements-tests.txt
|
||||
cd docs/
|
||||
make html
|
||||
|
||||
The results can be found in the ``_build/html/`` foler.
|
||||
|
||||
Submitting Bugs
|
||||
---------------
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
.. include:: ../CONTRIBUTING.rst
|
||||
|
||||
@@ -14,6 +14,7 @@ Welcome to Dungeonsheets's documentation!
|
||||
gm_notes
|
||||
advanced_features
|
||||
examples
|
||||
contributing
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
@@ -15,7 +15,7 @@ class Race:
|
||||
owner = None
|
||||
languages = ("Common",)
|
||||
proficiencies_text = tuple()
|
||||
weapon_proficiences = tuple()
|
||||
weapon_proficiencies = tuple()
|
||||
skill_proficiencies = ()
|
||||
skill_choices = ()
|
||||
num_skill_choices = 0
|
||||
@@ -61,7 +61,7 @@ class _Dwarf(Race):
|
||||
languages = ("Common", "Dwarvish")
|
||||
constitution_bonus = 2
|
||||
proficiencies_text = ("battleaxes", "handaxes", "throwing hammers", "warhammers")
|
||||
weapon_proficiences = (
|
||||
weapon_proficiencies = (
|
||||
weapons.Battleaxe,
|
||||
weapons.Handaxe,
|
||||
weapons.ThrowingHammer,
|
||||
@@ -388,7 +388,7 @@ class Tabaxi(Race):
|
||||
speed = "30 (20 climb)"
|
||||
languages = ("Common", "[Choose One]")
|
||||
weapon_proficiencies = (weapons.Claws,)
|
||||
proficiences_text = ("Claws",)
|
||||
proficiencies_text = ("Claws",)
|
||||
skill_proficiencies = ("perception", "stealth")
|
||||
features = (
|
||||
feats.Darkvision,
|
||||
@@ -427,7 +427,7 @@ class Aarakocra(Race):
|
||||
wisdom_bonus = 1
|
||||
languages = ("Common", "Aarakocra", "Auran")
|
||||
weapon_proficiencies = (weapons.Talons,)
|
||||
proficiences_text = ("Talons",)
|
||||
proficiencies_text = ("Talons",)
|
||||
|
||||
def __init__(self, owner=None):
|
||||
super().__init__(owner=owner)
|
||||
|
||||
@@ -9,6 +9,8 @@ from typing import Union
|
||||
from pathlib import Path
|
||||
|
||||
from dungeonsheets import exceptions
|
||||
from dungeonsheets.magic_items import MagicItem
|
||||
from dungeonsheets.content_registry import find_content
|
||||
|
||||
log = logging.getLogger(__file__)
|
||||
|
||||
@@ -431,6 +433,29 @@ class FoundryCharacterReader(JSONCharacterReader):
|
||||
item_name += f"({quantity})"
|
||||
yield item_name.lower()
|
||||
|
||||
def magic_items(self):
|
||||
"""Loads magic items. If not defined, try to figure out its
|
||||
properties.
|
||||
|
||||
"""
|
||||
item_types = ["weapon", "armor", "equipment"]
|
||||
items = [item for item in self.json_data()['items']
|
||||
if item['type'] in item_types]
|
||||
from pprint import pprint
|
||||
magic_items = [item for item in items if item['data']['rarity'] not in ["Common", ""]]
|
||||
# Convert magic items into classes
|
||||
def make_magic_item(data):
|
||||
try:
|
||||
item = find_content(data['name'], valid_classes=[MagicItem])
|
||||
except exceptions.ContentNotFound:
|
||||
# Make a generic version based on the JSON attributes
|
||||
item_name = data['name'].replace(' ', '')
|
||||
item = type(item_name, (MagicItem,), {})
|
||||
return item
|
||||
|
||||
magic_items = [make_magic_item(item) for item in magic_items]
|
||||
return magic_items
|
||||
|
||||
def class_levels(self):
|
||||
for item in self.json_data()["items"]:
|
||||
if item["type"] == "class":
|
||||
@@ -475,14 +500,14 @@ class FoundryCharacterReader(JSONCharacterReader):
|
||||
"cha": "charisma",
|
||||
}
|
||||
abilities = self.json_data()["data"]["abilities"]
|
||||
save_proficiences = []
|
||||
save_proficiencies = []
|
||||
for abbr, attr in attribute_names.items():
|
||||
char_props[attr] = self.as_int(abilities[abbr]["value"])
|
||||
# Check proficiency
|
||||
is_proficient = bool(abilities[abbr]["proficient"])
|
||||
if is_proficient:
|
||||
save_proficiences.append(attr)
|
||||
char_props["saving_throw_proficiencies"] = save_proficiences
|
||||
save_proficiencies.append(attr)
|
||||
char_props["saving_throw_proficiencies"] = save_proficiencies
|
||||
# Skill proficiencies
|
||||
skill_names = [
|
||||
"acrobatics",
|
||||
@@ -544,6 +569,8 @@ class FoundryCharacterReader(JSONCharacterReader):
|
||||
char_props["equipment"] = ", ".join(self.equipment())
|
||||
char_props["armor"] = self.armor()
|
||||
char_props["shield"] = self.shield()
|
||||
# Magic items
|
||||
char_props["magic_items"] = self.magic_items()
|
||||
# Personality, etc
|
||||
char_props["personality_traits"] = details["trait"].strip()
|
||||
char_props["flaws"] = details["flaw"].strip()
|
||||
@@ -555,12 +582,10 @@ class FoundryCharacterReader(JSONCharacterReader):
|
||||
# Some unused values
|
||||
warn_msg = (
|
||||
"Importing the following traits from JSON is not yet supported: "
|
||||
"magic_items, attacks_and_spellcasting, "
|
||||
"infusions, wild_shapes."
|
||||
"attacks_and_spellcasting, infusions, wild_shapes."
|
||||
)
|
||||
warnings.warn(warn_msg)
|
||||
log.warning(warn_msg)
|
||||
char_props["magic_items"] = ()
|
||||
char_props["attacks_and_spellcasting"] = ""
|
||||
char_props["infusions"] = []
|
||||
char_props["wild_shapes"] = []
|
||||
|
||||
@@ -11,7 +11,7 @@ from dungeonsheets.character import (
|
||||
Ranger
|
||||
)
|
||||
from dungeonsheets.monsters import Panther
|
||||
from dungeonsheets.weapons import Weapon, Shortsword
|
||||
from dungeonsheets.weapons import Weapon, Shortsword, Battleaxe
|
||||
from dungeonsheets.magic_items import MagicItem
|
||||
from dungeonsheets.armor import Armor, LeatherArmor, Shield
|
||||
|
||||
@@ -151,6 +151,11 @@ class TestCharacter(TestCase):
|
||||
char.weapon_proficiencies = tuple()
|
||||
char.race = race.HighElf()
|
||||
self.assertTrue(char.is_proficient(sword))
|
||||
|
||||
def test_racial_is_proficient(self):
|
||||
char = Character(classes=["Wizard"], race="Mountain Dwarf")
|
||||
battleaxe = Battleaxe()
|
||||
self.assertTrue(char.is_proficient(battleaxe))
|
||||
|
||||
def test_proficiencies_text(self):
|
||||
char = Character()
|
||||
|
||||
@@ -44,6 +44,7 @@ class TestDice(TestCase):
|
||||
|
||||
def test_simple_rolling(self):
|
||||
num_tests = 100
|
||||
# Do a bunch of rolls and make sure the numbers are within the requsted range
|
||||
for _ in range(num_tests):
|
||||
result = roll(6)
|
||||
self.assertGreaterEqual(result, 1)
|
||||
|
||||
+12
-1
@@ -226,7 +226,7 @@ class FoundryReaderTests(unittest.TestCase):
|
||||
gp=162,
|
||||
pp=2,
|
||||
weapons=["rapier"],
|
||||
magic_items=(),
|
||||
# magic_items=(),
|
||||
armor="padded armor",
|
||||
shield="shield",
|
||||
personality_traits="Loves a good lawyer joke.",
|
||||
@@ -284,3 +284,14 @@ class FoundryReaderTests(unittest.TestCase):
|
||||
this_result = list(this_result)
|
||||
self.assertEqual(this_result, val, key)
|
||||
|
||||
def test_load_homebrew_weapon(self):
|
||||
"""Check that the properties of a homebrew magic weapon get read
|
||||
properly.
|
||||
|
||||
"""
|
||||
charfile = FOUNDRY_JSON_FILE
|
||||
with warnings.catch_warnings(record=True):
|
||||
result = read_sheet_file(charfile)
|
||||
# Check that some magic items were set
|
||||
self.assertGreater(len(result['magic_items']), 0,
|
||||
"No magic items imported")
|
||||
|
||||
Reference in New Issue
Block a user