adding lizardfolk

This commit is contained in:
evilchili 2023-09-29 17:23:11 -07:00
parent 99f479910c
commit 9888e35072
5 changed files with 159 additions and 9 deletions

View File

@ -19,6 +19,7 @@ class Ancestry(str, Enum):
highttiefling = 'hightiefling'
human = 'human'
tiefling = 'tiefling'
lizardfolk = 'lizardfolk'
class Language(str, Enum):
@ -33,6 +34,7 @@ class Language(str, Enum):
infernal = 'infernal'
orcish = 'orcish'
undercommon = 'undercommon'
lizardfolk = 'lizardfolk'
app = typer.Typer()

View File

@ -7,7 +7,7 @@ import dice
import textwrap
_available_npc_types = {}
AVAILABLE_NPC_TYPES = {}
def a_or_an(s):
@ -96,9 +96,9 @@ class BaseNPC:
@property
def full_name(self):
name = ' '.join([n.capitalize() for n in self.names])
name = ' '.join([n.title() for n in self.names])
if self.title:
name = self.title.capitalize() + ' ' + name
name = self.title.title() + ' ' + name
if self.nickname:
name = f'{name} "{self.nickname}"'
return name
@ -332,12 +332,12 @@ def available_npc_types():
"""
Load all available NPC submodules and return a dictionary keyed by module name.
"""
if not _available_npc_types:
if not AVAILABLE_NPC_TYPES:
for filename in glob.glob(os.path.join(os.path.dirname(os.path.abspath(__file__)), '*.py')):
module_name = os.path.basename(filename)[:-3]
if module_name not in ['base', '__init__', 'traits']:
_available_npc_types[module_name] = import_module(f'npc.generator.{module_name}').NPC
return _available_npc_types
AVAILABLE_NPC_TYPES[module_name] = import_module(f'npc.generator.{module_name}').NPC
return AVAILABLE_NPC_TYPES
def npc_type(ancestry=None):
@ -358,8 +358,7 @@ def generate_npc(ancestry=None, names=[], pronouns=None, title=None, nickname=No
"""
Return a randomized NPC. Any supplied keyword parameters will override the generated values.
By default, NPC stats are all 10 (+0). If randomize is True, the NPC will be given random stats from the standard
distribution, but overrides will still take precedence.
By default, NPC stats are all 10 (+0). If randomize is True, the NPC will be given random stats from the standard distribution, but overrides will still take precedence.
"""
return npc_type(ancestry)(
names=names,

108
npc/generator/lizardfolk.py Normal file
View File

@ -0,0 +1,108 @@
from npc.languages import lizardfolk
from npc.generator.base import BaseNPC, a_or_an
import textwrap
import random
class NPC(BaseNPC):
ancestry = 'Lizardfolk'
language = lizardfolk.Lizardfolk()
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._tail = None
self._horns = None
self._fangs = None
self._frills = None
@property
def age(self):
if not self._age:
self._age = random.choice([
'hatchling',
'juvenile',
'adult',
'ancient',
])
return self._age
@property
def tail(self):
if not self._tail:
if random.random() <= -0.6:
self._tail = super(self)
else:
self._tail = 'no'
return self._tail
@property
def frills(self):
if not self._frills:
if self.age in ('adult', 'ancient'):
self._frills = random.choice([
'orange',
'red',
'yellow',
'green',
'blue',
'silvery',
])
return self._frills
@property
def skin_color(self):
if not self._skin_color:
self._skin_color = random.choice([
'green',
'blue',
'grey',
'brown',
'tan',
'sandy',
'gold',
])
return self._skin_color
@property
def description(self):
trait = random.choice([
f'{self.eyes} eyes',
f'{self.tail} tail',
f'{self.teeth} teeth',
f'{self.frills} frills',
self.facial_structure,
])
return (
f"{self.full_name} ({self.pronouns}) is {a_or_an(self.age)} {self.age}, {self.skin_color}-scaled "
f"{self.ancestry.lower()} with {a_or_an(self.nose)} {self.nose} snout, {self.body} body and {trait}."
)
@property
def character_sheet(self):
desc = '\n'.join(textwrap.wrap(self.description, width=120))
return f"""\
{desc}
Physical Traits:
Face: {self.face}, {self.nose} snout, {self.teeth} teeth
Eyes: {self.eyes}
Skin: {self.skin_tone}
Scales: {self.skin_color}
Body: {self.body}
Tail: {self.tail}
Voice: {self.voice}
Details:
Personality: {self.personality}
Flaw: {self.flaw}
Goal: {self.goal}
Whereabouts: {self.whereabouts}
"""

View File

@ -0,0 +1,41 @@
from npc.languages.base import BaseLanguage
import random
class Lizardfolk(BaseLanguage):
vowels = []
consonants = []
affixes = []
syllable_template = ()
syllable_weights = []
family = [
'sweet', 'floral', 'fruity', 'sour', 'fermented', 'green', 'vegetal', 'old',
'roasted', 'spiced', 'nutty', 'cocoa', 'pepper', 'pungent', 'burnt', 'carmelized',
'raw', 'rotting', 'dead', 'young',
]
scents = [
'honey', 'caramel', 'maple syrup', 'molasses', 'dark chocolate', 'chocolate', 'almond',
'hazelnut', 'peanut', 'clove', 'cinnamon', 'nutmeg', 'anise', 'malt', 'grain', 'roast',
'smoke', 'ash', 'acrid', 'rubber', 'skunk', 'petroleum', 'medicine', 'salt', 'bitter',
'phrenolic', 'meat', 'broth', 'animal', 'musty', 'earth', 'mould', 'damp', 'wood', 'paper',
'cardboard', 'stale', 'herb', 'hay', 'grass', 'peapod', 'whisky', 'wine', 'malic',
'citric', 'isovaleric', 'butyric', 'acetic', 'lime', 'lemon',
'orange', 'grapefruit', 'pear', 'peach', 'apple', 'grape', 'pineapple', 'pomegranate',
'cherry', 'coconut', 'prune', 'raisin', 'strawberry', 'blueberry', 'raspberry',
'blackberry', 'jasmine', 'rose', 'camomile', 'tobacco',
]
nicknames = []
def person(self):
return(
random.choice(self.family),
'-'.join([
random.choice(self.scents),
random.choice(self.scents),
]),
)

View File

@ -1,6 +1,6 @@
[tool.poetry]
name = "dnd_npcs"
version = "0.2.0"
version = "0.3"
description = "NPC tools for the telisar homebrew campaign setting"
authors = ["evilchili <evilchili@gmail.com>"]
license = "The Unlicense"